Expansion of C++ macro within macro to be concatenated - c++

I have to let CS students modify a group number and then generate some stuff related to that number. I don't want students to modify much but only that one group number defined somewhere as
#define GROUP_NUM 0
However, I cannot find a way to generate the rest from that MACRO.
For example:
#include <iostream>
using namespace std;
// string troubles
#define STR(x) #x
#define XSTR(x) STR(x)
// the trouble
#define GROUP_NUM 0
#define CLASS_NAME Group GROUP_NUM
#define CLASS_STR XSTR(CLASS_NAME)
int main()
{
cout << "CLASS_NAME = " << CLASS_STR << endl;
return 0;
}
outputs sadly
CLASS_NAME = Group 0
Here the trouble is that I don't want that space between Group and 0 because I need to use CLASS_NAME to create some class out of it such as
class CLASS_NAME : public .... { ... }
I tried to use
#define CLASS_NAME Group##GROUP_NUM
but then GROUP_NUM doesn't get expanded and it outputs
CLASS_NAME = GroupGROUP_NUM
The only solution I have found is to define these CLASS_NAME and CLASS_STR as macro functions which are passed the group number as argument (on the call site, not in another macro!):
// the trouble
#define GROUP_NUM 0
#define CLASS_NAME(g) Group ## g
#define CLASS_STR(g) XSTR(CLASS_NAME(g))
int main()
{
cout << "CLASS_NAME = " << CLASS_STR(GROUP_NUM) << endl;
return 0;
}
Is there a better solution? Why doesn't the following get expanded?
#define CLASS_STR XSTR(CLASS_NAME(GROUP_NUM))
The definition of XSTR above seems to show that one can use chained macros, so I don't understand why it doesn't get expanded here.
Update:
The trick of the helper macro functions is the solution. However I'd like to clarify the solution:
how does the macro expansion trick work? does it fully expand directly or does it only do one round of expansion? (i.e. if I have imbricated macros, would it mean I need more than one layer of helper function?)
the expansion with ## is still somewhat mysterious to me
My full problem was involving the nori framework and especially this.
With that macro, i.e.
#define NORI_REGISTER_CLASS(cls, name) \
cls *cls ##_create(const PropertyList &list) { \
return new cls(list); \
} \
static struct cls ##_{ \
cls ##_() { \
NoriObjectFactory::registerClass(name, cls ##_create); \
} \
} cls ##__;
If I use NORI_REGISTER_CLASS(CLASS_NAME, "mystring"), it gets incorrectly partially expanded into
Group0 * CLASS_NAME_create (const PropertyList &list) { return new Group0 (list); } ...
But if I use one wrapper macro to call that one, it works. What is the rule for macro expansion here? Why does the first cls get expanded and the second one keeps the name?

A posible solution would be:
#include <iostream>
using namespace std;
#define CLASS_NUM 0
// String macros
#define XSTR(x) STR(x)
#define STR(x) #x
#define CONCAT(x,y) x##y
// XCONCAT will first process tokens before passing them to CONCAT
#define XCONCAT(x,y) CONCAT(x,y)
// This will generate Group0
#define CLASS_NAME XCONCAT(Group,CLASS_NUM)
// Then this will generate "Group0"
#define CLASS_STR XSTR(CLASS_NAME)
int main()
{
cout << "CLASS_NAME = " << CLASS_STR << endl;
return 0;
}
With this code the output is:
CLASS_NAME = Group0

The ## operator only works on tokens. So you need a helper macro.
Try this:
#include <ostream>
#include <iostream>
// the trouble
using namespace std;
// the trouble
// string troubles
#define STR(x) #x
#define XSTR(x) STR(x)
#define GROUP_NUM 0
#define CLASS_STR(g) XSTR(Group ## g)
#define CLASS_STR2(g) CLASS_STR(g)
int main()
{
cout << "CLASS_NAME = " << CLASS_STR2(GROUP_NUM) << endl;
return 0;
}

Use BOOST_PP_CAT for this:
#define CLASS_NAME BOOST_PP_CAT(Group,GROUP_NUM)
http://www.boost.org/doc/libs/1_54_0/libs/preprocessor/doc/ref/cat.html

Related

why can't compare string in #if expression in c++

i want to write code like this but i can't:
#define COLOR "red"
#define RED "red"
#define BLUE "blue"
int main()
{
// following code can't be compiled
#if (COLOR==RED)
cout<<"red"<<endl;
#endif
// following code can work
if(COLOR==RED)
cout<<"red"<<endl;
else
cout<<"notred"<<endl;
}
so how can i realize string compare in #if expression? or may be this is not possible?
BTW i know i can realize in other ways like following:
#define COLOR 1
#define RED 1
#define BLUE 2
// can be compiled and word correctly
#if(COLOR==RED)
cout<<"red"<<endl;
#endif
Because #if only works with integers
expression is a C expression of integer type, subject to stringent restrictions. It may contain...
see https://gcc.gnu.org/onlinedocs/gcc-3.0.1/cpp_4.html#SEC38
#include <iostream>
// internal functions, can be put into included header
#define TRUE 1
#define XEQUAL(a, b) a##_##b
#define EQUAL(a, b) XEQUAL(a, b)
#define XCUSTOMSTR(a) color_##a
#define CUSTOMSTR(a) XCUSTOMSTR(a)
#define XSTR(a) #a
#define STR(a) XSTR(a)
// define available colors
#define red_red TRUE
#define blue_blue TRUE
#define green_green TRUE
// optional custom names
#define color_green "grĂ¼n"
#define COLOR red
#define COLOR2 green
int main() {
#if EQUAL(COLOR, red)
std::cout << "my_"STR(COLOR)"_file.txt";
#endif
#if EQUAL(COLOR, COLOR2)
char filename[] = "my_"STR(COLOR2)"_file.txt";
std::cout << filename;
#endif
std::cout << CUSTOMSTR(green);
return 0;
}
The trick is to compare before making a string out of it.
The preprocessor can not process strings (except generate them), but it can process textual tokens!

How to call macro that uses token pasting?

I am trying to print ffmpeg version in a C++ program. I see that in the /libavutil/version.h there is AV_VERSION which should tell the version number in the format x.x.x.
As a test I used some random numbers as function parameters like this: std::string version = AV_VERSION(3,4,2);. The same error I get if I use LIBAVUTIL_VERSION_MAJOR, LIBAVUTIL_VERSION_MINOR and LIBAVUTIL_VERSION_MICRO from the file. That was actually my first try to print the version number.
The error I get is invalid suffix '.2' on floating constant or invalid suffix '.101' on floating constant if I try to print std::cout << AV_VERSION(LIBAVUTIL_VERSION_MAJOR,LIBAVUTIL_VERSION_MINOR,LIBAVUTIL_VERSION_MICRO) << std::endl;
I do understand that the preprocessor is thinking that the token is a float, hence the error. How do you actually use this type of macro funtion?
That macro is in the file I mentioned above, so it must be a way to call that macro function without giving an error, thinking that is a mature library, and I guess other libraries use something similar for printing version number.
Here is how AV_VERSION is defined in the header file and how I call it:
#define AV_VERSION_INT(a, b, c) ((a)<<16 | (b)<<8 | (c))
#define AV_VERSION_DOT(a, b, c) a ##.## b ##.## c
#define AV_VERSION(a, b, c) AV_VERSION_DOT(a, b, c)
#define AV_VERSION_MAJOR(a) ((a) >> 16)
#define AV_VERSION_MINOR(a) (((a) & 0x00FF00) >> 8)
#define AV_VERSION_MICRO(a) ((a) & 0xFF)
#define LIBAVUTIL_VERSION_MAJOR 57
#define LIBAVUTIL_VERSION_MINOR 9
#define LIBAVUTIL_VERSION_MICRO 101
#define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
LIBAVUTIL_VERSION_MINOR, \
LIBAVUTIL_VERSION_MICRO)
#define LIBAVUTIL_VERSION AV_VERSION(LIBAVUTIL_VERSION_MAJOR, \
LIBAVUTIL_VERSION_MINOR, \
LIBAVUTIL_VERSION_MICRO)
int main()
{
std::string version = AV_VERSION(3,4,2);
std::cout << AV_VERSION(LIBAVUTIL_VERSION_MAJOR,LIBAVUTIL_VERSION_MINOR,LIBAVUTIL_VERSION_MICRO) << std::endl;
return 0;
}
I coud've skip this error but as I'm trying to learn C++ I am pretty sure that I will find more of this type of macros so no point to avoid learning them now as I'm facing them.
Thanks in advance!
You need to use a stringize expansion. Because of how the preprocessor works, this involves two macros:
#define STR(x) #x
#define XSTR(x) STR(x)
The macro STR will take whatever parameter you give it and make that a string literal.
The macro XSTR will first expand its parameter x and the result will be the parameter to STR.
To illustrate:
STR(LIBAVUTIL_VERSION) will give "LIBAVUTIL_VERSION"
XSTR(LIBAVUTIL_VERSION) will give "57.9.101"
Demo according to your code:
int main()
{
std::string version1 = XSTR(LIBAVUTIL_VERSION);
std::string version2 = XSTR(AV_VERSION(3,4,2));
std::cout << version1 << "\n";
std::cout << version2 << "\n";
return 0;
}
Output:
57.9.101
3.4.2

Looping with macros

I need to do a loop where I turn this simple example of reflection:
std::string mystring[3] = {{"mystring[0]"},{"mystring[1]"},{"mystring[2]"}};
into a more managable form for longer arrays. The solution sounds like I should either use macros with a loop, or recursion. However, macros don't support loops or recursion!
How do I create a macro to handle this for me arbitrarily?
#define NAME_OBJ(type, name, size)
Try using Boost. If it fails, try using more Boost.
#include <boost/preprocessor/repeat.hpp>
#include <boost/preprocessor/comma_if.hpp>
#include <boost/preprocessor/stringize.hpp>
#define DETAIL_NAME_OBJ_ELEM(z, n, name) \
BOOST_PP_COMMA_IF(n) { BOOST_PP_STRINGIZE(name) "[" BOOST_PP_STRINGIZE(n) "]" }
#define NAME_OBJ(type, name, size) \
type name[size] = { BOOST_PP_REPEAT(size, DETAIL_NAME_OBJ_ELEM, name) }
Then this:
NAME_OBJ(std::string, mystring, 3);
... expands to this:
std::string mystring[3] = {
{ "mystring" "[" "0" "]" },
{ "mystring" "[" "1" "]" },
{ "mystring" "[" "2" "]" }
};
... in which the adjacent string literals are then automatically merged before compilation.
See it live on Coliru
As you stated, macros in C++ do not support loops or recursion, such that you would have to duplicate lines of code.
A common way of solving such topics is to write a program - let's say myGenerator, which contains a "normal" C++-loop and which's output is a C++ source file (.cpp or .h). Integrate then a call to myGenerartor in a build step before building the rest of your program.
Based on a great answer to handling macros here
https://stackoverflow.com/a/12540675/1723954
I modified/simplified the solution for this specific case:
#define NAME_OBJ(type, name, size) \
type name[size] = { INIT_ELEMENT_ ## size(name) }
#define INIT_ELEMENT_1(name) { #name "[0]" }
#define INIT_ELEMENT_2(name) INIT_ELEMENT_1(name), { #name "[1]" }
#define INIT_ELEMENT_3(name) INIT_ELEMENT_2(name), { #name "[2]" }
...
The solution does not allow for very-very-very large arrays, only as large as you are willing to code. I did 128 and that's fine for all of the cases in my project.

Why is this macro generating a syntax error?

So this is my code:
// Defines a tuple
#define __WINDOW__RESOLUTION__ 500, 200
// Seperate the tuple
#define __WINDOW__X__1(Width, Height) (Width)
#define __WINDOW__Y__1(Width, Height) (Height)
// Add another sort of indirection because my tuple is a macro
#define __WINDOW__X__(Macro) __WINDOW__X__1(Macro)
#define __WINDOW__Y__(Macro) __WINDOW__Y__1(Macro)
// These should be the final values 500 and 200
#define __WINDOW__RESOLUTION__X__ (__WINDOW__X__(__WINDOW__RESOLUTION__))
#define __WINDOW__RESOLUTION__Y__ (__WINDOW__Y__(__WINDOW__RESOLUTION__))
When i use the first macro where the final number should be something seems to go wrong:
std::cout << __WINDOW__RESOLUTION__X__ << std::endl; // Outputs 200 instead of 500
above line outputs the number 200, so the Y value instead of the X value
std::cout << __WINDOW__RESOLUTION__Y__ << std::endl; // ERR with macro underlined
this line won't even compile [ C2059, syntax error: ")" ]
Thank you for helping me Alex
For the record - I understand that your solution is "do not do this", but I still wanted to provide an answer for the original question.
In fact, I actually don't see anything wrong with your code. It actually works fine if you compile it with a basic test example like this one:
// Defines a tuple
#define __WINDOW__RESOLUTION__ 500, 200
// Seperate the tuple
#define __WINDOW__X__1(Width, Height) (Width)
#define __WINDOW__Y__1(Width, Height) (Height)
// Add another sort of indirection because my tuple is a macro
#define __WINDOW__X__(Macro) __WINDOW__X__1(Macro)
#define __WINDOW__Y__(Macro) __WINDOW__Y__1(Macro)
// These should be the final values 500 and 200
#define __WINDOW__RESOLUTION__X__ (__WINDOW__X__(__WINDOW__RESOLUTION__))
#define __WINDOW__RESOLUTION__Y__ (__WINDOW__Y__(__WINDOW__RESOLUTION__))
#include <iostream>
using namespace std;
int main() {
// your code goes here
std::cout << __WINDOW__RESOLUTION__X__ << std::endl;
std::cout << __WINDOW__RESOLUTION__Y__ << std::endl;
return 0;
}
It looks like your error may be related to the brackets in your last macro, though, so just removing them could possibly solve that, but again - this wasn't required for me (using gcc 5.4 under ubuntu):
// These should be the final values 500 and 200
#define __WINDOW__RESOLUTION__X__ __WINDOW__X__(__WINDOW__RESOLUTION__)
#define __WINDOW__RESOLUTION__Y__ __WINDOW__Y__(__WINDOW__RESOLUTION__)
It was previously also pointed out that double underscore __ followed by an upper-case letter is reserved, but it certainly didn't stop gcc from compiling this - maybe it would generate an error with stricter compile flags, though!
It seems to work fine when compiled with gcc4.9, but the problem might lie in that the compiler threads __WINDOW__RESOLUTION__ as a single parameter. For example, if you replace
#define __WINDOW__RESOLUTION__X__ (__WINDOW__X__(__WINDOW__RESOLUTION__))
with
#define __WINDOW__RESOLUTION__X__ (__WINDOW__X__(500, 200))
It will throw an error because __WINDOW__X__ expects only 1 parameter.
You can solve this by using ... and __VA_ARGS__ in your macro, which will forward any parameter that it receives:
#define __WINDOW__X__(...) __WINDOW__X__1(__VA_ARGS__)
#define __WINDOW__Y__(...) __WINDOW__Y__1(__VA_ARGS__)

C++ concatenation in macro

I have the following set of macros (which I got from the first answer for How can I use the compile time constant __LINE__ in a string?) defined in an attempt to have a macro 'LOCATION_PREFIX' that I can use to "capture" the location of a line of code (will be passed to other functions that will perform logging).
#define STRINGIZE(f,l) STRINGIZE2(f) "(" STRINGIZE2(l)"):"
#define STRINGIZE2(x) #x
#define LOCATION_PREFIX STRINGIZE(__FILE__, __LINE__)
int main(){
printf("%s here\n", LOCATION_PREFIX);
printf("%s over here\n", LOCATION_PREFIX);
return 1;
}
Output:
"t.cpp"(8): here
"t.cpp"(9): over here
I want the output to be:
t.cpp(8): here
t.cpp(9): over here
Any help is greatly appreciated !
You don't need the first STRINGIZE2 invocation, as the file name is already a quoted string:
#define STRINGIZE(f,l) f "(" STRINGIZE2(l) "):"
#define STRINGIZE2(x) #x
#define LOCATION_PREFIX STRINGIZE(__FILE__, __LINE__)
int main(){
printf("%s here\n", LOCATION_PREFIX);
printf("%s over here\n", LOCATION_PREFIX);
return 1;
}