In C++ one sometimes has annoying bugs where someone forgot to close out a namespace which was opened in a header file, and it can sometimes be difficult to track down exactly which one.
Somewhat less banally, sometimes there are macros which for technical reasons cannot appear enclosed within any namespace, or cryptic error messages may occur. In the boost fusion library, BOOST_FUSION_DEFINE_STRUCT
requires this, for example. Instead you are supposed to use the macro at filescope, and pass it the namespace that you want the declaration to be within.
For that matter, you get undefined behavior if you include any standard library header within a namespace.
Is it possible to do something like, write a macro e.g. ASSERT_FILESCOPE
so that I can put a line ASSERT_FILESCOPE;
at the end of a C++ header file, or at the start of a macro like BOOST_FUSION_DEFINE_STRUCT
, which will cause a static_assert
failure with a nice error message if that expression is not at filescope?
One possibility is to declare a name and then test for ::name
. To avoid the same name being declared in the global scope before a later use of the macro in the same translation unit, you can use __COUNTER__
, or __LINE__
if that works better for you. It's not perfect by any means, but at least the first line of the error contains the message. live example
#include <type_traits>
#define CONCAT(x, y) CONCAT_I(x, y)
#define CONCAT_I(x, y) x##y
#define ASSERT_FILESCOPE \
ASSERT_FILESCOPE_I(__COUNTER__)
#define ASSERT_FILESCOPE_I(counter) \
static constexpr bool CONCAT(ASSERTION_FAILED_NOT_AT_FILE_SCOPE, counter)() { return true; } \
static_assert(::CONCAT(ASSERTION_FAILED_NOT_AT_FILE_SCOPE, counter)(), "Detected a location other than file scope.")
I get this error from Clang:
main.cpp:17:5: error: no member named 'ASSERTION_FAILED_NOT_AT_FILE_SCOPE0' in the global namespace; did you mean simply 'ASSERTION_FAILED_NOT_AT_FILE_SCOPE0'?
ASSERT_FILESCOPE;
^~~~~~~~~~~~~~~~
main.cpp:7:5: note: expanded from macro 'ASSERT_FILESCOPE'
ASSERT_FILESCOPE_I(__COUNTER__)
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:11:19: note: expanded from macro 'ASSERT_FILESCOPE_I'
static_assert(::CONCAT(ASSERTION_FAILED_NOT_AT_FILE_SCOPE, counter)(), "Detected a location other than file scope.")
^~
main.cpp:17:5: note: 'ASSERTION_FAILED_NOT_AT_FILE_SCOPE0' declared here
main.cpp:7:5: note: expanded from macro 'ASSERT_FILESCOPE'
ASSERT_FILESCOPE_I(__COUNTER__)
^
main.cpp:10:20: note: expanded from macro 'ASSERT_FILESCOPE_I'
static constexpr bool CONCAT(ASSERTION_FAILED_NOT_AT_FILE_SCOPE, counter)() { return true; } \
^
main.cpp:3:22: note: expanded from macro 'CONCAT'
#define CONCAT(x, y) CONCAT_I(x, y)
^
main.cpp:4:24: note: expanded from macro 'CONCAT_I'
#define CONCAT_I(x, y) x##y
^
<scratch space>:101:1: note: expanded from here
ASSERTION_FAILED_NOT_AT_FILE_SCOPE0
^
and this one from GCC:
main.cpp:11:19: error: '::ASSERTION_FAILED_NOT_AT_FILE_SCOPE0' has not been declared
static_assert(::CONCAT(ASSERTION_FAILED_NOT_AT_FILE_SCOPE, counter)(), "Detected a location other than file scope.")
^
main.cpp:7:5: note: in expansion of macro 'ASSERT_FILESCOPE_I'
ASSERT_FILESCOPE_I(__COUNTER__)
^
main.cpp:17:5: note: in expansion of macro 'ASSERT_FILESCOPE'
ASSERT_FILESCOPE;
^
main.cpp:11:19: note: suggested alternative:
static_assert(::CONCAT(ASSERTION_FAILED_NOT_AT_FILE_SCOPE, counter)(), "Detected a location other than file scope.")
^
main.cpp:7:5: note: in expansion of macro 'ASSERT_FILESCOPE_I'
ASSERT_FILESCOPE_I(__COUNTER__)
^
main.cpp:17:5: note: in expansion of macro 'ASSERT_FILESCOPE'
ASSERT_FILESCOPE;
^
main.cpp:10:34: note: 'foo::ASSERTION_FAILED_NOT_AT_FILE_SCOPE0'
static constexpr bool CONCAT(ASSERTION_FAILED_NOT_AT_FILE_SCOPE, counter)() { return true; } \
^
main.cpp:4:24: note: in definition of macro 'CONCAT_I'
#define CONCAT_I(x, y) x##y
^
main.cpp:10:27: note: in expansion of macro 'CONCAT'
static constexpr bool CONCAT(ASSERTION_FAILED_NOT_AT_FILE_SCOPE, counter)() { return true; } \
^
main.cpp:7:5: note: in expansion of macro 'ASSERT_FILESCOPE_I'
ASSERT_FILESCOPE_I(__COUNTER__)
^
main.cpp:17:5: note: in expansion of macro 'ASSERT_FILESCOPE'
ASSERT_FILESCOPE;
^