Best way to build a string in multiple pieces? - c++

I'm building a large C++ program with a variety of different compile-time options, selected by #defines (or the -D option).
I want to have a version string that lists a number of them as tags, and ideally, to have that version string defined as a literal, not a constant.
Currently, I'm looking at three options, none of which is ideal.
Piles of preprocessor defines
#ifdef AAA
#define AAAMSG " [A]"
#else
#define AAAMSG ""
#endif
#ifdef BBB
#define BBBMSG " [B]"
#else
#define BBBMSG ""
#endif
// ...
#define REVISION __DATE__ " " __TIME__ AAAMSG BBBMSG CCCMSG DDDMSG
Build a constant
const char *const REVISION=__DATE__ " " __TIME__
#ifdef AAA
" [A]"
#endif
#ifdef BBB
" [B]"
#endif
// ...
;
Redefine the token
#define REVISION __DATE__ " " __TIME__
#ifdef AAA
#define REVISION REVISION " [A]"
#endif
#ifdef BBB
#define REVISION REVISION " [B]"
#endif
// ...
The first one is incredibly verbose (imagine that with half a dozen independent elements) and error-prone. The second one is far better, but it creates a constant instead of a literal, so I can't use it as part of another string - example:
send(sock,"rev " REVISION "\n",sizeof(REVISION)+4,0);
It seems silly to use run-time string manipulation (an sprintf or somesuch) for a compile-time constant. The third example, of course, just straight-up doesn't work, but it is pretty much what I'm trying to do.
Is there some alternative method?

#define AAAMSG ""
#define BBBMSG ""
#ifdef AAA
#define AAAMSG " [A]"
#endif
define all your empties.. then treat it like a switch.
If you keep the types the same, you shouldn't have any issues with redefining..
Note: I am not 100% sure this works, but changing a define can be done.

Closing off this question with the comment that I'm sticking with option 1. There appears to be no way to do what I was hoping to do, so the imperfect remains. Thanks to those who contributed!

Related

How to use preprocessor IF on DEFINE that is an ENUM member?

I am struggling with this for a while now, and cant get it to work!
I have a preprocessor define for LOG_LEVEL which defines what logs my program should emit.
I Have a lot of LOG points, so performance is needed,
therefore, no use of runtime check for log_level.
I trimmed my code to the minimal problematic construct which can be played with (here)[https://onlinegdb.com/u39ueqNAI]:
#include <iostream>
typedef enum {
LOG_SILENT=0,
LOG_ERROR,
LOG_WARNING,
LOG_INFO,
LOG_DEBUG
} E_LOG_LEVELS;
// this define is set using -DLOG_LEVEL=LOG_WARNING to the compiler.
#define LOG_LEVEL LOG_WARNING
int main() {
std::cout << "Logging Level Value is " << LOG_LEVEL << std::endl; // output 2 (correctly)
#if LOG_LEVEL==LOG_WARNING
std::cout << "Log Level Warning!" << std::endl; // outputs (correctly)
#endif
#if LOG_LEVEL==LOG_ERROR
std::cout << "Log Level Error!" << std::endl; // outputs (Why ??? )
#endif
return 0;
}
The main issue is that the #if LOG_LEVEL==LOG_* always true.
I also tried #if LOG_LEVEL==2 but this returned FALSE (uff).
what's going on ?
how can I test that a define is an enum value ?
You don't need the preprocessor for this. A normal
if (LOG_LEVEL <= LOG_WARNING)
will not create a runtime test when the condition involves only constants and the build has any optimization at all.
Modern C++ allows you to force the compiler to implement the conditional at compile-time, using if constexpr (...). This will prune away dead branches even with optimization disabled.
Finally, if you insist on using the preprocessor, and you can guarantee that the macro will use the symbolic name (you'll never build with g++ -DLOG_LEVEL=2), then you can
#define STRINGIFY(x) #x
#define STRINGY2(x) STRINGIFY(x)
#define PP_LOG_LEVEL STRINGY2(LOG_LEVEL)
#define LOG_LEVEL_IS(x) STRINGY2(LOG_LEVEL)==STRINGIFY(x)
then either
#if PP_LOG_LEVEL=="LOG_WARNING"
or
#if PP_LOG_LEVEL_IS(LOG_WARNING)
But I would recommend avoiding preprocessor string comparison for this.
If you do use the preprocessor, I recommend checking the actual value against a whitelist and using #error to stop the build in case LOG_LEVEL isn't set to any of the approved names.

How do I add whitespace to a concat preprocessor macro?

Given the below, how can I get something that yields ONE TWO THREE? I can't seem to figure out what to put in ?? to yield the intended concat behavior. You can't seem to #define a single space.
#include <iostream>
#define STRINGIFY(x) #x
#define STRINGIFYMACRO(y) STRINGIFY(y)
#define CONCAT2(X,Y) X ## Y
#define CONCAT(X,Y) CONCAT2(X,Y)
#define CAT \
ONE \
TWO
#define DOG \
THREE
#define BAT CONCAT(CONCAT(CAT,?? ),DOG)
int main()
{
std::cout << STRINGIFYMACRO(BAT) << std::endl;
return 0;
}
The ## operator is used to combine two consecutive tokens. It cannot be used to combined things that are not tokens (such as whitespace), nor can it be used if the result is not a valid (preprocessing) token.
In other words, don't use ## to combine strings. That's not what it is for, and it won't work.
Remember that in C++, consecutive strings will be concatenated by the compiler. So there is normally no need to concatenate the contents of the string. Even if you had to concatenate the contents, you could do that without token concatenation because the contents of a string are not a token. (This is only necessary if you are trying to create a computed #include filename from multiple components.)
One other useful thing to know is that whitespace inside a macro replacement (not at either end) is reduced to a single space character, which is preserved by the stringify operator.
So if you're OK with the single space character, you can just do this:
#include <iostream.h>
#define STRINGIFY(x) #x
#define STRINGIFY_EXPANDED(x) STRINGIFY(x)
#define CAT ONE TWO
#define DOG THREE
#define BAT CAT DOG
int main() {
std::cout << STRINGIFY_EXPANDED(BAT) << '\n';
return 0;
}
Try it online!
Otherwise, you could stringify in pieces and concatenate the pieces as desired:
#define DOG STRINGIFY(ONE) " " STRINGIFY(TWO)
#define CAT STRINGIFY(THREE)
#define BAT DOG "\t" CAT
Try it online!

C++ object-like macro containing URL interpreted as comment

Recently I run into strange behavior of my code:
#define STR(X) #X
#define XSTR(X) STR(X)
#define TEST_SERVER_HTTP http://host.cz/import.php
qDebug() << "Test server: " XSTR(TEST_SERVER_HTTP);
outputting just "http:" text.
I have figured out, that the "//" part of the URL is here interpreted as C++ comment, cutting the rest of URL text away.
Obvious workaround would be
#define TEST_SERVER_HTTP http:\/\/host.cz\/import.php
however I am getting (G++) warning
invalid string literal, ignoring final '\' [enabled by default]
wherever macro is used, even though the compiled code runs as expected.
Questions are:
Is there some "correct" way in C++ how to quote slash to suppress double slash meaning of start of comment ?
Is there some "canonical" workaround for putting URL constants as replace value of object-like macro ?
And no
#define TEST_SERVER_HTTP "http://host.cz/import.php"
is not acceptable for reasons given in the rest of my code.
It can be done easily, but you need to hack into it (as always):
#define STRINGIFY_(x) #x
#define STRINGIFY(x) STRINGIFY_(x)
#define SLASH() /
#define DOUBLE_SLASH() SLASH()SLASH()
#define STH_BEFORE() sth/before
#define STH_AFTER() sth/after
#define STH_DOUBLE_SLASH_STH STH_BEFORE()DOUBLE_SLASH()STH_AFTER()
int main()
{
//test
std::cout << STRINGIFY(STH_DOUBLE_SLASH_STH) << std::endl;
}
Output:
sth/before//sth/after
Usable new-macro-non-definiable version would be:
#define PLACEBO(x) x
#define STH_DOUBLE_SLASH_STH(x,y) PLACEBO(x)DOUBLE_SLASH()PLACEBO(y)
int main()
{
std::cout << STRINGIFY(STH_DOUBLE_SLASH_STH(before, after)) << std::endl;
}
Tested with MSVC 2015 and GCC 5.3.0.
Also note, that MSVC allows such comment (this is not sane!)
DOUBLE_SLASH() fun comment
You can't escape tokens in C/C++; the only place that backslash has an effect is inside string literals. Outside of a string literal (and when not part of a universal character name), a backslash is just a one-character token which is only valid during the preprocessing phases.
It is, however, easy enough to prevent multi-character tokens from being formed during the initial division of code into tokens, and you can do so without introducing whitespace. All you need is the identity macro:
#define I(X) X
Now you can use that to avoid the double slash becoming comments:
#define STR(X) #X
#define XSTR(X) STR(X)
#define TEST_SERVER_HTTP http:I(/)I(/)host.cz/import.php
qDebug() << "Test server: " XSTR(TEST_SERVER_HTTP);
There is no point using that on the single slash, or indeed any other character.
That solution will not, however, work to escape quotes and apostrophes (or whitespace, for that matter). As far as I know, there is no way to introduce an unmatched quote or apostrophe as a token.
Disclaimer: I didn't try that with MSVC, which is known to have a non-compliant preprocessor implementation. But it should work without issues on any standards-compliant implementation.

How can I use #pragma message() so that the message points to the file(lineno)?

In order to add 'todo' items into my code, I want to put a message in the compiler output.
I would like it to look like this:
c:/temp/main.cpp(104): TODO - add code to implement this
in order to make use of the Visual Studio build output functionality to navigate to the respective line by double-clicking it.
But the __LINE__ macro seems to expand to an int, which disallows writing
#pragma message( __FILE__ "("__LINE__"): ..." )
Would there be another way?
Here is one that allows you to click on the output pane:
(There are also some other nice tips there)
http://www.highprogrammer.com/alan/windev/visualstudio.html
// Statements like:
// #pragma message(Reminder "Fix this problem!")
// Which will cause messages like:
// C:\Source\Project\main.cpp(47): Reminder: Fix this problem!
// to show up during compiles. Note that you can NOT use the
// words "error" or "warning" in your reminders, since it will
// make the IDE think it should abort execution. You can double
// click on these messages and jump to the line in question.
#define Stringize( L ) #L
#define MakeString( M, L ) M(L)
#define $Line MakeString( Stringize, __LINE__ )
#define Reminder __FILE__ "(" $Line ") : Reminder: "
Once defined, use like so:
#pragma message(Reminder "Fix this problem!")
This will create output like:
C:\Source\Project\main.cpp(47): Reminder: Fix this problem!
just whipped this up now, and it sure beats my old solution of using #error :D
#define _STR(x) #x
#define STR(x) _STR(x)
#define TODO(x) __pragma(message("TODO: "_STR(x) " :: " __FILE__ "#" STR(__LINE__)))
you can modify this how ever you like/to whatever suits your needs.
An example of its usage:
//in code somewhere
TODO(Fix this);
output in the console pane:
1>TODO: Fix this :: c:\users\administrator\documents\visual studio 2008\projects\metatest\metatest\metatest.cpp#33
only downside is you can't jump to the line of this (by double clicking the message in the console pane) using __pragma (but testing with #pragma it doesn't seem to be the case anyways...)
On Visual C++ you can just do
#pragma message(__FILE__ "(" _CRT_STRINGIZE(__LINE__) ")" ": warning: [blah]")
_CRT_STRINGIZE is often already defined in some header, but if it's not, you can define it:
#define _CRT_STRINGIZE_(x) #x
#define _CRT_STRINGIZE(x) _CRT_STRINGIZE_(x)
This one allows it to be used without #pragma (Microsoft specific I think) and when you click it takes you to the line since it shows the file and line number just like a regular err/warning message does since none of the other ones seem to do this. This used to work without the __pragma but newer versions of msvc require it. Ive been using it since sometime in the 90's. I use Visual Studio 2013
#define MacroStr(x) #x
#define MacroStr2(x) MacroStr(x)
#define Message(desc) __pragma(message(__FILE__ "(" MacroStr2(__LINE__) ") :" #desc))
example :
Message("Need to add unit testing here")
output:
1> c:\source\include\mithrilsoftware.h(180) :"Need to add unit testing here"
This is an addendum to the answer for those who find it tedious to punch in #pragma directives every-time they need to put a bookmark in the code: You can save a few keystrokes by whipping up a macro to do this for you! While in general, you cannot have a #pragma directive within macros, MS C/C++ compilers 2008 and above do support a special vendor-specific extension called the __pragma which can be used with macros. See Pragma Directives and the __Pragma Keyword.
I use something akin to the following on a daily basis:
#define STR2(x) #x
#define STR1(x) STR2(x)
#define LOC __FILE__ "("STR1(__LINE__)") : Warning Msg: "
#define WARNING_BUILDER(x) __FILE__ "("STR1(__LINE__)") : Warning Msg: " __FUNCTION__ " requires " #x
#define WREVIEW WARNING_BUILDER(review)
#define WUT WARNING_BUILDER(unit-testing)
#ifdef SPECIAL_WARNINGS
#ifdef SPECIAL_WARNINGS_REVIEW
#define MARK_FOR_REVIEW() do { \
__pragma(message( WREVIEW )) \
} while (0)
#else
#define MARK_FOR_REVIEW
#endif
#ifdef SPECIAL_WARNINGS_UNIT_TEST
#define MARK_FOR_UNIT_TEST() do { \
__pragma(message( WUT )) \
} while (0)
#else
#define MARK_FOR_UNIT_TEST
#endif
#endif
// uncomment/set in build-environment to enable special warnings
//#define SPECIAL_WARNINGS
#ifdef SPECIAL_WARNINGS
// uncomment/set in build-environment if you want only code review warnings
//#define SPECIAL_WARNINGS_REVIEW
// uncomment/set in build-environment if you want only unit-test warnings
//#define SPECIAL_WARNINGS_UNIT_TEST
#endif
int main()
{
MARK_FOR_REVIEW();
MARK_FOR_UNIT_TEST();
}
You can easily extend it to suit your needs and add more warnings. The good part of having such a system is that you can selectively turn-on say, only code-review items and not have to worry about anything else by setting the appropriate macro in the build settings.
Use the # token. I've posted an example from MSDN below:
// collisions.h
#define __STR2__(x) #x
#define __STR1__(x) __STR2__(x)
#define __LOC__ __FILE__ "("__STR1__(__LINE__)") : Warning Msg: "
// collisions.cpp
#pragma message(__LOC__"Need to do 3D collision testing")

using macros to place #ifdef in the code

I am trying to do something like this
#define VB_S #ifdef VERBOSE
#define VB_E #endif
so that in the code instead of writing
#ifdef VERBOSE
cout << "XYZ" << endl;
#endif
I can write
VB_S
cout << "XYZ" << endl;
VB_E
This gives me a compile time error: Stray '#' in the program.
Can anyone put light on what is the right way to do this
You can't put directives inside macros. (# inside a macro as another signification -- it is the stringizing operator and must be followed by a parameter id -- but the restriction is older than that meaning)
You could do something like this:
#ifdef VERBOSE
#define VB(x) x
#else
#define VB(x) do { } while (false)
#endif
VB(cout << "foo");
Similar to Erik's response:
#ifdef VERBOSE
#define VB(...) __VA_ARGS__
#else
#define VB(...) /* nothing */
#endif
Using a variadic macro has the benefit of allowing commas inside the VB() call. Also, AFAIK, you can remove the do...while.
I prefer the following:
#define VERBOSE 1
// or 0, obviously
if (VERBOSE)
{
// Debug implementation
}
This is a little more readable since VB_S doesn't mean anything to the average user, but if (VERBOSE) does.