| title | C++ snippets |
|---|
How to track all the places where access to .my_value happens?
struct MyItem
{
int my_value = 0;
};
void Process(MyItem& item)
{
item.my_value = 4; // **
}
int main()
{
MyItem item;
item.my_value = 2;
Process(item);
item.my_value = 8;
}Use __declspec(property(...)) MSVC extension, also supported by Clang:
#include <print>
#include <source_location>
#include <stacktrace> // clang: -lstdc++exp
struct MyItem
{
int get_value()
{
return _value_impl;
}
void set_value(int v
, std::source_location loc = std::source_location::current())
{
std::println(" -- set _value: {} -> {}, callsite: {},{}/{}, callstack:\n{}"
, _value_impl
, v
, loc.file_name()
, loc.line()
, loc.function_name()
, std::to_string(std::stacktrace::current()));
_value_impl = v;
}
__declspec(property(get = get_value, put = set_value)) int my_value;
int _value_impl = 0;
}; -- set _value: 0 -> 2, callsite: /app/example.cpp,38/int main(), callstack:
0# MyItem::set_value(int, std::source_location) at /app/example.cpp:21
1# main at /app/example.cpp:38
-- set _value: 2 -> 4, callsite: /app/example.cpp,32/void Process(MyItem &), callstack:
0# MyItem::set_value(int, std::source_location) at /app/example.cpp:21
1# Process(MyItem&) at /app/example.cpp:32
2# main at /app/example.cpp:39
-- set _value: 4 -> 8, callsite: /app/example.cpp,40/int main(), callstack:
0# MyItem::set_value(int, std::source_location) at /app/example.cpp:21
1# main at /app/example.cpp:40
Note, /std:c++latest -Z7 for MSVC (mostly so stacktrace resolves) and -std=c++23 -fdeclspec -lstdc++exp for Clang.
#include <vector>
#include <algorithm>
void Sorted_Add(std::vector<int>& vs, int v)
{
auto it = std::upper_bound(vs.begin(), vs.end(), v);
vs.insert(it, v);
}Note, in case of no duplicates, the use of std::lower_boud() brings no difference,
i.e., this is the same as above:
void Sorted_Add(std::vector<int>& vs, int v)
{
auto it = std::lower_bound(vs.begin(), vs.end(), v);
vs.insert(it, v);
}In case of duplicates, std::upper_bound or std::lower_bound still could be used, however, std::upper_bound inserts new value to the end of duplicate values range, std::lower_bound inserts new value before duplicate values range:
{1, 5, 5, 5, 9} // initial
{1, 5, 5, 5, **5**, 9} // std::upper_bound(..., 5)
{1, **5**, 5, 5, 5, 9} // std::lower_bound(..., 5)
The difference could be important in case of (a) many duplicates -- std::upper_bound, in theory, requires less swaps for a later insert and (b) when custom type/struct is used with custom predicate/sort by field -- so insert position is observable.
See this SO and std::upper_bound.
#include <cstdio>
// GCC, Clang: -Werror=format
// GCC, Clang: error: format specifies type 'char *' but the argument has type 'int' [-Werror,-Wformat]
// MSVC: /we4477
// MSVC: error C4477: 'printf' : format string '%s' requires an argument of type 'char *', but variadic argument 1 has type 'int'
#define KK_CHECK_PRINTF(KK_FMT, ...) (void) \
(false && \
std::printf(KK_FMT, ##__VA_ARGS__))KK_CHECK_PRINTF("%s", 10) will fail to compile (/we4477 for MSVC, -Werror=format
for GCC, Clang). This relies on the fact that std::printf is validated by all
compilers. There are ways to achieve similar effect for custom printf functions
by using SAL Annotations for MSVC (see this SO)
and __attribute__((format(printf, 1, 2))) for GCC/Clang. However, using printf
directly is the easiest way.
#include <string_view>
#include <charconv>
#include <cassert>
#include <cstdint>
std::uint64_t ParseToU64(const std::string_view& str)
{
std::uint64_t v = 0;
const char* const start = str.data();
const char* const end = (start + str.size());
auto&& [ptr, ec] = std::from_chars(start, end, v, 10);
assert(ptr == end);
assert(ec == std::errc{});
return v;
}where it does not allow to have string leftovers, so ParseToU64("10a") fails,
compared to regular std::from_chars("10a", ...) and old-style atoi("10a")
analogs.
Sample:
KK_VERIFY(false);
KK_VERIFY(false, "message");
KK_VERIFY(false, "message %s %s", "format", "args");
- with enabled printf format validation on GCC (
-Werror=format), need to silence one other warning with-Wno-format-zero-lengthcaused byKK_VERIFY(false)
Code:
#include <cstdio> // for std::printf validation
#include <cstdlib> // std::abort()
#define KK_CHECK_PRINTF(...) (void) \
(false && std::printf(__VA_ARGS__))
#define KK_VERIFY(KK_EXPRESSION, ...) (void) \
(!!(KK_EXPRESSION) || \
(::detail::dump_verify(__FILE__, __LINE__ \
, #KK_EXPRESSION, ##__VA_ARGS__) \
, KK_CHECK_PRINTF("" __VA_ARGS__) \
, KK_DEBUGBREAK() \
, KK_EXIT() \
, false) \
)
#define KK_DEBUGBREAK() \
__debugbreak() /* MSVC-specific */
#define KK_EXIT() \
std::abort() /* OR std::quick_exit(-1)*/
#include <cstdarg> // va_list
namespace detail
{
void dump_verify(const char* file, unsigned line, const char* expression, const char* fmt, ...)
{
char str[1024]; // not initialized, vsnprintf zero-terminates
va_list args;
va_start(args, fmt);
(void)std::vsnprintf(str, std::size_t(str), fmt, args);
va_end(args);
// flush stdout if needed
(void)std::fprintf(stderr, "Verify '%s' failed at '%s,%u' -> %s\n"
, expression, file, line, str);
}
void dump_verify(const char* file, unsigned line, const char* expression)
{
// flush stdout if needed
(void)std::fprintf(stderr, "Verify '%s' failed at '%s,%u'\n"
, expression, file, line);
}
} // namespace detail- with std::printf format & args validation
- with overload for optional message and or custom format
- up to 15 custom args, modify KK_SELECT_N() to support more
__VA_OPT__could be used to simplify KK_SELECT_N machinery, see Making a flexible assert in C++
Sample:
KK_VERIFY(false);
KK_VERIFY(false, "message");
KK_VERIFY(false, "message %s %s", "format", "args");
Code:
#include <cstdio> // for std::printf validation
#include <cstdlib> // std::abort()
#define KK_CHECK_PRINTF(KK_FMT, ...) (void) \
(false && \
std::printf(KK_FMT, ##__VA_ARGS__))
// Based on GET_MACRO() from https://stackoverflow.com/questions/11761703/overloading-macro-on-number-of-arguments/69945225#69945225
#define KK_EXPAND(x) x
#define KK_SELECT_N( \
_1, _2, _3, _4 \
, _5, _6, _7, _8 \
, _9, _10, _11, _12 \
, _13, _14, _15, _16 \
, NAME, ...) NAME
#define KK_VERIFY(...) \
KK_EXPAND(KK_SELECT_N(__VA_ARGS__ \
, KK_VERIFY_F, KK_VERIFY_F, KK_VERIFY_F, KK_VERIFY_F \
, KK_VERIFY_F, KK_VERIFY_F, KK_VERIFY_F, KK_VERIFY_F \
, KK_VERIFY_F, KK_VERIFY_F, KK_VERIFY_F, KK_VERIFY_F \
, KK_VERIFY_F, KK_VERIFY_F, KK_VERIFY_F, KK_VERIFY_EXPR) \
(__VA_ARGS__))
#define KK_DEBUGBREAK() \
__debugbreak() /* MSVC-specific */
#define KK_EXIT() \
std::abort() /* OR std::quick_exit(-1)*/
#define KK_VERIFY_EXPR(KK_EXPRESSION) (void) \
(!!(KK_EXPRESSION) || \
(::detail::dump_verify(__FILE__, __LINE__, #KK_EXPRESSION) \
, KK_DEBUGBREAK() \
, KK_EXIT() \
, false) \
)
#define KK_VERIFY_F(KK_EXPRESSION, KK_FMT, ...) (void) \
(!!(KK_EXPRESSION) || \
(::detail::dump_verify(__FILE__, __LINE__ \
, #KK_EXPRESSION, KK_FMT, ##__VA_ARGS__) \
, KK_CHECK_PRINTF(KK_FMT, ##__VA_ARGS__) \
, KK_DEBUGBREAK() \
, KK_EXIT() \
, false) \
)
#include <cstdarg> // va_list
namespace detail
{
void dump_verify(const char* file, unsigned line, const char* expression, const char* fmt, ...)
{
char str[1024]; // not initialized, vsnprintf zero-terminates
va_list args;
va_start(args, fmt);
(void)std::vsnprintf(str, std::size_t(str), fmt, args);
va_end(args);
// flush stdout if needed
(void)std::fprintf(stderr, "Verify '%s' failed at '%s,%u' -> %s\n"
, expression, file, line, str);
}
void dump_verify(const char* file, unsigned line, const char* expression)
{
// flush stdout if needed
(void)std::fprintf(stderr, "Verify '%s' failed at '%s,%u'\n"
, expression, file, line);
}
} // namespace detail- with std::format
- with overload for optional message and or custom format
- up to 15 custom args, modify KK_SELECT_N() to support more
__VA_OPT__could be used to simplify KK_SELECT_N machinery, see Making a flexible assert in C++
Sample:
KK_VERIFY(false);
KK_VERIFY(false, "message");
KK_VERIFY(false, "message {} {}", "format", "args");
Code:
#include <cstdlib> // std::abort()
#include <print>
// Based on GET_MACRO() from https://stackoverflow.com/questions/11761703/overloading-macro-on-number-of-arguments/69945225#69945225
#define KK_EXPAND(x) x
#define KK_SELECT_N( \
_1, _2, _3, _4 \
, _5, _6, _7, _8 \
, _9, _10, _11, _12 \
, _13, _14, _15, _16 \
, NAME, ...) NAME
#define KK_VERIFY(...) \
KK_EXPAND(KK_SELECT_N(__VA_ARGS__ \
, KK_VERIFY_F, KK_VERIFY_F, KK_VERIFY_F, KK_VERIFY_F \
, KK_VERIFY_F, KK_VERIFY_F, KK_VERIFY_F, KK_VERIFY_F \
, KK_VERIFY_F, KK_VERIFY_F, KK_VERIFY_F, KK_VERIFY_F \
, KK_VERIFY_F, KK_VERIFY_F, KK_VERIFY_F, KK_VERIFY_EXPR) \
(__VA_ARGS__))
#define KK_DEBUGBREAK() \
__debugbreak() /* MSVC-specific */
#define KK_EXIT() \
std::abort() /* OR std::quick_exit(-1)*/
#define KK_VERIFY_EXPR(KK_EXPRESSION) (void) \
(!!(KK_EXPRESSION) || \
(::detail::dump_verify(__FILE__, __LINE__, #KK_EXPRESSION) \
, KK_DEBUGBREAK() \
, KK_EXIT() \
, false) \
)
#define KK_VERIFY_F(KK_EXPRESSION, KK_FMT, ...) (void) \
(!!(KK_EXPRESSION) || \
(::detail::dump_verify(__FILE__, __LINE__ \
, #KK_EXPRESSION \
, std::format_to_n(::detail::FormatBuffer{}.it()\
, ::detail::FormatBuffer::available_size() \
, KK_FMT, ##__VA_ARGS__)) \
, KK_DEBUGBREAK() \
, KK_EXIT() \
, false) \
)
#include <cstring> // optional, for prettify_filename()
namespace detail
{
// Workaround to the fact that std::make_format_args(10, 'c') does not compile
// since it requires lvalue references (see http://wg21.link/P2905R2),
// hence std::format_to_n() is used.
struct FormatBuffer
{
FormatBuffer(const FormatBuffer&) noexcept = delete;
FormatBuffer& operator=(const FormatBuffer&) noexcept = delete;
static constexpr unsigned BUFFER_SIZE = 1024;
char _buffer[BUFFER_SIZE]; // not initialized
FormatBuffer() noexcept {}
static constexpr std::ptrdiff_t available_size()
{
return BUFFER_SIZE - 1;
}
struct iterator
{
FormatBuffer* _self;
char* _it;
using difference_type = std::ptrdiff_t;
iterator& operator++() noexcept
{
++_it;
return *this;
}
iterator operator++(int) noexcept
{
iterator copy{ *this };
++_it;
return copy;
}
char& operator*() noexcept
{
return *_it;
}
static std::ptrdiff_t my_min(std::ptrdiff_t v1, std::ptrdiff_t v2)
{
return ((v1 < v2) ? v1 : v2);
}
void finish(std::ptrdiff_t size)
{
const std::ptrdiff_t p = my_min(size, FormatBuffer::available_size());
_self->_buffer[p] = '\0';
}
const char* str() const
{
return _self->_buffer;
}
};
iterator it()
{
return {this, _buffer};
}
using format_result = std::format_to_n_result<iterator>;
};
void dump_verify(const char* file, unsigned line, const char* expression, FormatBuffer::format_result&& fmt)
{
fmt.out.finish(fmt.size);
// flush stdout if needed
std::println(stderr, "Verify '{}' failed at '{},{}' -> {}"
, expression, file, line, fmt.out.str());
// C++23 - use std::stacktrace::current() if needed
}
void dump_verify(const char* file, unsigned line, const char* expression)
{
// flush stdout if needed
std::println(stderr, "Verify '{}' failed at '{},{}'"
, expression, file, line);
// C++20 - do std::cerr << std::format(...) instead of std::println()
// C++23 - use std::stacktrace::current() if needed
}
} // namespace detail