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_IFalong with a preprocessor function to test whether e.g.- __func__is non-empty.- The output of - g++ -Eon 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 to- BOOST_LOG_NAMED_SCOPE. I can re-use the- boost::log::attributes::named_scope::sentryobject 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 a- LOG_CONTEXTat 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 - staticin 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.
