I work on a project, and I need to simulate a recursive macro (here TEST_COLUMN). But it fail on visual 2013.
On gcc, it work correctly, but with visual, I have an error.
So, to debug this macro, I use static_assert (maybe there is a better way).
Each time, in the macros TEST_COLUMN_X(name, ...) the value of name contain ALL the parameters, and not only the first one.
If anyone have an idea to fix this.
Here is the minimal code needed to reproduce it:
#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x) //transform x to char
#define EXPAND(x) x
#define NUM_ARGS_HELPER(X,X64,X63,X62,X61,X60,X59,X58,X57,X56,X55,X54,X53,X52,X51,X50,X49,X48,X47,X46,X45,X44,X43,X42,X41,X40,X39,X38,X37,X36,X35,X34,X33,X32,X31,X30,X29,X28,X27,X26,X25,X24,X23,X22,X21,X20,X19,X18,X17,X16,X15,X14,X13,X12,X11,X10,X9,X8,X7,X6,X5,X4,X3,X2,X1,N,...) N
//count to number of argument containing
#define NUM_ARGS(...) EXPAND(NUM_ARGS_HELPER(0, __VA_ARGS__ ,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)) in __VA_ARGS__
//define each level of the macro
#define TEST_COLUMN_0 ;
#define TEST_COLUMN_1(name) static_assert(false,"TEST_COLUMN_1 " STR(name));
#define TEST_COLUMN_2(name,...) static_assert(false,"TEST_COLUMN_2 " STR(name)); //TEST_COLUMN_1(__VA_ARGS__)
#define TEST_COLUMN_3(name,...) static_assert(false,"TEST_COLUMN_3 " STR(name)); //TEST_COLUMN_2(__VA_ARGS__)
#define TEST_COLUMN_4(name,...) static_assert(false,"TEST_COLUMN_4 " STR(name)); TEST_COLUMN_3(__VA_ARGS__)
//tricks to call the correct macro (TEST_COLUMN_X)
#define TEST_COLUMN_N1(N,...) TEST_COLUMN_##N(__VA_ARGS__)
#define TEST_COLUMN(N,...) TEST_COLUMN_N1(N,__VA_ARGS__)
//recursive macro
#define TEST_STATIC_COLUMN(...) TEST_COLUMN(NUM_ARGS(__VA_ARGS__), __VA_ARGS__)
int main(int argc, char* argv[])
{
TEST_STATIC_COLUMN(a, b, c);
return 0;
}
Output :
main.cpp(21): error C2338: TEST_COLUMN_3 a, b, c
but it should be
main.cpp(21): error C2338: TEST_COLUMN_3 a
You can test this using the online compiler http://webcompiler.cloudapp.net/
I don't know why, but adding some call to EXPAND solve this issue:
#define TEST_COLUMN_2(name,...) static_assert(false,"TEST_COLUMN_2 " STR(name)); EXPAND(TEST_COLUMN_1(__VA_ARGS__))
#define TEST_COLUMN_3(name,...) static_assert(false,"TEST_COLUMN_3 " STR(name)); EXPAND(TEST_COLUMN_2(__VA_ARGS__))
#define TEST_COLUMN_4(name,...) static_assert(false,"TEST_COLUMN_4 " STR(name)); EXPAND(TEST_COLUMN_3(__VA_ARGS__))
#define TEST_COLUMN_N1(N,...) EXPAND(TEST_COLUMN_##N(__VA_ARGS__))
One may generally use __LINE__ and __FILE__ in C++ programs, with many toolchains, including GCC.
__LINE__ under GCC evaluates to an expression of type int;
__FILE__ evaluates to a char const[N] where N is the appropriate value.
Does any major toolchain provide an equivalent to __FILE__ with type wchar const[N]?
If so, what is it?
You can make your own WFILE:
#define WIDE2(x) L##x
#define WIDE1(x) WIDE2(x)
#define WFILE WIDE1(__FILE__)
Tested with non-ASCII characters and filename 马克.cpp:
#include <stdio.h>
#include <io.h>
#include <fcntl.h>
#define WIDE2(x) L##x
#define WIDE1(x) WIDE2(x)
#define WFILE WIDE1(__FILE__)
int main() {
_setmode(_fileno(stdout), _O_U16TEXT); // required for Unicode output to console
wprintf(L"%s\n", WFILE);
}
Demo (running from cmd.exe and Chinese language support installed):
C:\>cl /W4 /nologo 马克.cpp
马克.cpp
C:\>马克.exe
马克.cpp
Use:
WIDE(MEXPAND(__FILE__))
and
WIDE(STRINGIFY(__LINE__))
or replace __LINE__ with anything that needs to be stringified, and replace __FILE__ with any macro string literal you want to widen.
Using the following definitions:
#define STRINGIFY2(m) #m
#define MEXPAND(m) m
#define STRINGIFY(m) STRINGIFY2(m)
#define WIDE(m) L ## m
Example usage:
#define AssertBreakMethod DebugBreak
#define AssertBreakForce(expr) \
do \
{ \
if (!(expr)) \
{ \
OutputDebugStringW(WIDE(MEXPAND(__FILE__)) \
WIDE("(") WIDE(STRINGIFY(__LINE__)) \
WIDE("): Assertion failed: ") \
WIDE(#expr) WIDE("\n")); \
AssertBreakMethod(); \
} \
} \
while (0)
Note that the whole parameter to OutputDebugString is assembled statically at compile time into a single string literal.
The trick with stringification of a macro is passing it through another macro. When __FILE__ is passed to MEXPAND it is expanded at that time. MEXPAND returns its argument which is now a string. It is then legal to put the leading L there to make it wide.
STRINGIFY does the same trick, it passes its argument through STRINGIFY2 which expands the argument to the line number (which looks like an integer at that point) then STRINGIFY2 puts the # symbol before it, stringifying the integer.
In Visual Studio just surround it with _T(), for example:
TRACE( _T("function = %s"), _T(__FUNCTION__);
I would have put this answer as a comment to an earlier reply but was not allowed due to not having the minimum 50 reputation to comment...
In Visual Studio, _T(__FILE__) will NOT expand to L__FILE__ unless you modify its standard definition of _T in the tchar.h header file. _T(__FILE__) and _T(__FUNCTION__) worked 5 years ago and still work today if you are looking for wide versions of the current file and function.
_T(x) is defined as __T(x), which is defined as L##x when _UNICODE is defined and x otherwise. So _T(__FILE__) expands to something like __T("my_file.c"), which then expands to L"my_file.c" or "my_file.c" depending on _UNICODE. It is useful to test things before claiming that they do not work.
For example use const auto name = L"" __FUNCTION__;
I need to have a string that uses a macro value which is an integer. But it's outside of any functions, so I do not want to create a variable. I'm using it in a #pragma comment.
so something like this:
#define num 7
#pragma comment(lib, "string" + num)
which would combine the string and num making it (#pragma comment(lib, "string7")
What you want to do is called stringification:
#define stringify_1(x...) #x
#define stringify(x...) stringify_1(x)
#define NUM 7
char *p = stringify(NUM);
This is inspired by __stringify macro in include/linux/stringify.h in Linux kernel helpers.
I am not completely clear on the intent, it sounds like some preprocessor capability:
http://gcc.gnu.org/onlinedocs/cpp/Stringification.html#Stringification
From that example you find this terse explanation that seems to be what you want.
#define xstr(s) str(s)
#define str(s) #s
#define foo 4
str (foo)
==> "foo"
xstr (foo)
==> xstr (4)
==> str (4)
==> "4"
So you would be able to do something like this:
#define xstr(s) str(s)
#define str(s) #s
#define num 7
#pragma comment(lib, "string" xstr(num))
Normal string merging rules should make that all fine if it were in actual code, but I am not sure if the string will automatically merge in the pragma. That is probably implementation dependent.
What does this mean?
#define __T(x) x
Just return x?
I am seeing code using syntax I've not seen before:
#define CREATE_ENCODER(CODEC) \
strcpy(codecName, __T(#CODEC); \
pCodec = new CODEC##VideoEncoder();
if(parFileName) \
{ pEncoderParams = new CODEC##EncoderParams; \
}
What is the # for?
Actually __T is used to turn string to wchar_t* or char*, in tchar.h you have:
#define __T(x) L ## x
used when UNICODE is enabled and
#define __T(x) x
when it is disabled
If your code is to be compiled on both UNICODE and non-UNICODE compilations you use:
TCHAR* sz = __T("My text");
most WINAPI functions use TCHAR* or some of its form
Actually I prefer _T() version, never knew __T version exists, at the bottom of tchar.h you have it defined:
#define _T(x) __T(x)
#define _TEXT(x) __T(x)
So back to your example:
strcpy(codecName, __T(#CODEC)); \
is equivalent to:
strcpy(codecName, "CODEC"); \
on NON-unicode build, and
strcpy(codecName, L"CODEC"); \
on UNICODE build
VERY IMPORTANT!!: using _T or __T is not really enough to make sure you code is UNICODE compilant. It will make it easier to call WINAPI functions. You must prepare your code to work with UNICODE.
Yes, that define is simply replaced with the passed value. This kind of define is often used if you e.g. want to determine at compile time if you want to pass a value through a translation function (#define __T(x) translate(x)) or not (#define __T(x) x).
# stringifies the passed value: http://gcc.gnu.org/onlinedocs/cpp/Stringizing.html and
## is the concatenation operator: http://gcc.gnu.org/onlinedocs/cpp/Concatenation.html
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!