I have a logging library for C++ projects that I use. It works in a namespace logger:: and inside that I have all necessary class definitions and pre-defined loggers;
namespace logger {
class log_level {
...
}
log_level info;
log_level debug;
}
Now, why is it logger instead of just log, well because C++ already has a log function, and this would cause a warning message:
warning: built-in function 'log' declared as non-function [-Wbuiltin-declaration-mismatch]
Now this is probably something that could be just ignored, if I don't use log function at all. But even then.. I don't like warnings and errors. So I came to think about a macro that would do the job..
But ever since trying, I have miserably failed as using double colon(:) in macro identifier seems to be a bit more difficult. Possibly impossible as well, but let's ask from the ones who might have better knowledge.
#define log::info logger::info
or
#define PREFIX_LOGGER_NS logger::
#define PREFIX_LOG_NS log::
#define log\:\:info PREFIX_LOGGER_NS info
#define log::debug logger::debug;
all fail.. So, is there a way out of this or is it too much to ask for a descriptive 3 word fake namespace? :)
I eventually decided to use namespace logger instead, even though I don't usually use log - it's better to keep that available and avoid unnecessary warnings.
Thank you for all answers given. They were all good :)
It is impossible because macro substitution happens in translation phase 4, which works on the preprocessing tokens level. The tokens are formed in phase 3 under the rules detailed below. Double colon by itself is a preprocessing token. Quote from cppreference:
- The source file is decomposed into comments, sequences of whitespace characters (space, horizontal tab, new-line, vertical tab, and form-feed), and preprocessing tokens, which are the following:
a) header names such as or "myfile.h"
b) placeholder tokens produced by preprocessing import and module directives (i.e. import XXX; and module XXX;) (since C++20)
c) identifiers
d) preprocessing numbers
e) character literals, including user-defined character literals(since C++11)
f) string literals, including user-defined string literals(since C++11)
g) operators and punctuators (including alternative tokens), such as +, <<=, <%, ##, or and
h) individual non-whitespace characters that do not fit in any other category
Double colon falls into category g and cannot be combined with other characters to form a preprocessor token. That means from the perspective of a preprocessor, log::info
cannot be treated as a whole at all. This is fundamental to the way how preprocessor works, and there is no possible way around it. If you attempt to somehow escape the colon, then a single colon itself becomes a single token and again cannot be combined with anything else (except maybe forming digraphs listed below). This is just as fundamental as there is no way to treat a=
as a single token.
The complete list of operators and punctuators is given in lex.operators:
# ## %: %:%: { } [ ] ( ) <: :> <% %> ; : ... ? :: . .* -> ->* ~ ! + - * / % ^ & | = += -= *= /= %= ^= &= |= == != < > <= >= <=> && || << >> <<= >>= ++ -- , and or xor not bitand bitor compl and_eq or_eq xor_eq not_eq
The #define
preprocessor directive in addition requires the first token after #define
to be a valid identifier.
For completeness, the requirement for an identifier is the follows,
The first character of a valid identifier must be one of the following:
- uppercase Latin letters A-Z
- lowercase Latin letters a-z
- underscore
- any Unicode character with the Unicode property XID_Start
Any other character of a valid identifier must be one of the following:
- digits 0-9
- uppercase Latin letters A-Z
- lowercase Latin letters a-z
- underscore
- any Unicode character with the Unicode property XID_Continue