C++20 added std::source_location
as a replacement for the debugging macros __LINE__
, __FILE__
, etc.
This is great. I have a macro that builds up a variable declaration in order to log and profile a block of code using said macros:
#define TOKEN_PASTE_SIMPLE(x, y) x##y
#define TOKEN_PASTE(x, y) TOKEN_PASTE_SIMPLE(x, y)
#define TOKEN_STRINGIZE_SIMPLE(x) #x
#define TOKEN_STRINGIZE(x) TOKEN_STRINGIZE_SIMPLE(x)
//...
#if defined PROFILE_LOG_SCOPE || defined PROFILE_LOG_SCOPE_FUNCTION
#undef PROFILE_LOG_SCOPE
#undef PROFILE_LOG_SCOPE_FUNCTION
#endif
#ifdef PROFILE_BUILD
#define PROFILE_LOG_SCOPE(tag_str) ProfileLogScope TOKEN_PASTE(plscope_, __LINE__)(tag_str)
#define PROFILE_LOG_SCOPE_FUNCTION() PROFILE_LOG_SCOPE(__FUNCSIG__)
#else
#define PROFILE_LOG_SCOPE(tag_str)
#define PROFILE_LOG_SCOPE_FUNCTION()
#endif
However, replacing the macros with the source_location version breaks because the function calls are not evaluated before the macro expansion.
#define TOKEN_PASTE_SIMPLE(x, y) x##y
#define TOKEN_PASTE(x, y) TOKEN_PASTE_SIMPLE(x, y)
#define TOKEN_STRINGIZE_SIMPLE(x) #x
#define TOKEN_STRINGIZE(x) TOKEN_STRINGIZE_SIMPLE(x)
//...
//TODO: Replace __LINE__ with std::source_location::line
//TODO: Replace __FUNCSIG__ with std::source_location::function_name
#if defined PROFILE_LOG_SCOPE || defined PROFILE_LOG_SCOPE_FUNCTION
#undef PROFILE_LOG_SCOPE
#undef PROFILE_LOG_SCOPE_FUNCTION
#endif
#ifdef PROFILE_BUILD
#define PROFILE_LOG_SCOPE(tag_str) ProfileLogScope TOKEN_PASTE(plscope_, std::source_location::current().line())(tag_str)
#define PROFILE_LOG_SCOPE_FUNCTION() PROFILE_LOG_SCOPE(std::source_location::current().function_name())
#else
#define PROFILE_LOG_SCOPE(tag_str)
#define PROFILE_LOG_SCOPE_FUNCTION()
#endif
QUESTION
How would I get the above to work?
I ultimately went with a hybrid approach. That is, Use __LINE__
to generate the variable name and pass in std::source_location::current()
as a default parameter:
//...
class ProfileLogScope {
public:
explicit ProfileLogScope(const char* scopeName = nullptr, std::source_location location = std::source_location::current()) noexcept;
//...
};
ProfileLogScope::ProfileLogScope(const char* scopeName, std::source_location location) noexcept
: m_scope_name(scopeName)
, m_time_at_creation(TimeUtils::Now())
, m_location(location)
{
/* DO NOTHING */
}
ProfileLogScope::~ProfileLogScope() noexcept {
const auto now = TimeUtils::Now();
TimeUtils::FPMilliseconds elapsedTime = (now - m_time_at_creation);
DebuggerPrintf(std::format("ProfileLogScope {} in file {} on line {} took {:.2f} milliseconds.\n", m_scope_name != nullptr ? m_scope_name : m_location.function_name(), m_location.file_name(), m_location.line(), elapsedTime.count()));
}
//...
#define TOKEN_PASTE_SIMPLE(x, y) x##y
#define TOKEN_PASTE(x, y) TOKEN_PASTE_SIMPLE(x, y)
#define TOKEN_STRINGIZE_SIMPLE(x) #x
#define TOKEN_STRINGIZE(x) TOKEN_STRINGIZE_SIMPLE(x)
//...
#if defined PROFILE_LOG_SCOPE || defined PROFILE_LOG_SCOPE_FUNCTION
#undef PROFILE_LOG_SCOPE
#undef PROFILE_LOG_SCOPE_FUNCTION
#endif
#ifdef PROFILE_BUILD
#define PROFILE_LOG_SCOPE(tag_str) auto TOKEN_PASTE(plscope_, __LINE__) = ProfileLogScope{tag_str}
#define PROFILE_LOG_SCOPE_FUNCTION() auto TOKEN_PASTE(plscope_, __LINE__) = ProfileLogScope{nullptr}
#else
#define PROFILE_LOG_SCOPE(tag_str)
#define PROFILE_LOG_SCOPE_FUNCTION()
#endif