I am trying to define a macro that behaves differently when used in a function context vs a class body or namespace. The purpose of this is to selectively include BOOST_LOG_NAMED_SCOPE
(or achieve equivalent behavior) in the LOG_CONTEXT
macro referenced here.
I have tried two approaches:
Using
BOOST_PP_IF
along with a preprocessor function to test whether e.g.__func__
is non-empty.The output of
g++ -E
on a basic source file still contains the literal text__func__
in both global and function scope so that probably rules out any approach targeting the preprocessing phase.Using something like
sizeof(__func__)
to select a specialized template that implements behavior similar toBOOST_LOG_NAMED_SCOPE
. I can re-use theboost::log::attributes::named_scope::sentry
object from boost log, but I'm stuck trying to figure out how to instantiate it conditionally in a function context, or at least in a way that works everywhere. The following construction seems to work fine in class definitions and functions, but fails with a "multiple definition" error when linking together multiple translation units that include a header with aLOG_CONTEXT
at global or namespace scope:#include <boost/log/attributes/named_scope.hpp> #include <type_traits> namespace logging { namespace attrs = boost::log::attributes; namespace detail { // Default no-op on construction when not in a function. template<typename ScopeTraits, bool InFunction> class named_scope_helper {}; // Specialization when in a function. template<typename ScopeTraits> class named_scope_helper<ScopeTraits, true> : public attrs::named_scope::sentry { public: named_scope_helper() BOOST_NOEXCEPT : sentry( ScopeTraits::scope_name(), ScopeTraits::filename(), ScopeTraits::lineno() ) {} }; template<size_t N> class not_1 : public std::true_type {}; template<> class not_1<1> : public std::false_type {}; #define LOGGING_LOG_IN_FUNCTION \ ::logging::detail::not_1<sizeof(__func__)>::value } // namespace detail } // namespace logging // scope_name_t/filename_t are required since attrs::named_scope::sentry // requires string literals. #define LOG_CONTEXT( name_ ) \ struct __logging_log_scope_traits__ \ { \ using scope_name_t = const char (&)[sizeof(name_)]; \ static scope_name_t scope_name() \ { return name_; } \ using filename_t = const char (&)[sizeof(__FILE__)]; \ static filename_t filename() \ { return __FILE__; } \ static size_t lineno() \ { return __LINE__; } \ }; \ ::logging::detail::named_scope_helper< \ __logging_log_scope_traits__, LOGGING_LOG_IN_FUNCTION> \ BOOST_LOG_UNIQUE_IDENTIFIER_NAME(_log_named_scope_sentry_);
My normal workaround for this would be to use
static
in the declaration of the_log_named_scope_sentry_
, but that defeats the purpose.
I could add another macro that is strictly for non-execution contexts, but wanted to investigate this approach first since it would be an interesting trick to have. How can I proceed on one of the two approaches I've started above, or is there another option I haven't considered?
A general solution would be preferred but I'm only really concerned with GCC and Clang.