Replacing __LINE__ and __FUNCSIG__ with the new std::source_location in a macro

166 views Asked by At

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?

1

There are 1 answers

0
Casey On BEST ANSWER

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