Say I have a macro like this:
#define SET_TYPE_NAME(TYPE, NAME) \
template<typename T> \
std::string name(); \
\
template<> \
std::string name<TYPE>() { \
return NAME; \
}
This won't work if I pass it a template that has more than one parameter, because the comma in the <int, int> is interpreted as separating the macro arguments, not the template arguments.
SET_TYPE_NAME(std::map<int, int>, "TheMap")
// Error: macro expects two arguments, three given
This problem seems to be solved by doing this:
SET_TYPE_NAME((std::map<int, int>), "TheMap")
But now another problem arises, one that I really did not expect:
template<>
std::string name<(std::map<int, int>)>()
// template argument 1 is invalid
It seems that the extra parentheses make the template argument invalid. Is there any way around this?
Besides typedef, you could switch the order of the arguments and use variadic macros (requires C99 or C++11-compatible compiler):
#define SET_TYPE_NAME(NAME, ...) \
template<typename T> \
std::string name(); \
\
template<> \
std::string name<__VA_ARGS__>() { \
return NAME; \
}
...
SET_TYPE_NAME("TheMap", std::map<int, int>)
You could use typedef:
typedef std::map<int, int> int_map;
SET_TYPE_NAME(int_map, "TheMap");
boost's BOOST_FOREACH suffers from the same issue.
Some time ago (while searching the web for the utility of Identity<T>) I got to this page.
In brief, to answer the question, if you don't have C++11 support and/or can't (or don't want to) use a typedef, you call your macro the "right" way:
// If an argument contains commas, enclose it in parentheses:
SET_TYPE_NAME((std::map<int, int>), "TheMap")
// For an argument that doesn't contain commas, both should work:
SET_TYPE_NAME((SomeType1), "TheType1")
SET_TYPE_NAME(SomeType2, "TheType2")
and then to get rid of the (possible) unwanted parentheses around the type, you can use a "helper" like this:
template<typename> struct RemoveBrackets;
template<typename T> struct RemoveBrackets<void (T)> {
typedef T Type;
};
and in your macro change the line:
std::string name<TYPE>() { \
to:
std::string name< RemoveBrackets<void (TYPE)>::Type >() { \
(or define a helper macro, say
#define REMOVE_BRACKETS(x) RemoveBrackets<void (x)>::Type
then replace the line with
std::string name< REMOVE_BRACKETS(TYPE) >() { \
).
(For the full story read the paragraph "An even better solution" at the end of the article linked above.)
Edit: just found that. But it uses <void X> when it really should use <void (X)> (in get_first_param<void X>::type); indeed the parentheses are necessary if you pass a "simple", non-bracketed argument (like SomeType2 in my code above) -- and they don't hurt if X is already bracketed (e.g., void ((SomeType1)) is equivalent to void (SomeType1); again, see the article). (By the way, I noticed that many answers on the other SO page are in essence "Macros are dumb". I won't comment, though.)
I'd like to add an answer to my own question. Now that Boost 1.50.0 was released, Boost.Utility has a new mini-library that helps with this kind of thing. If you look at the source you'll see it's implemented the same as gx_'s solution. Here's how to use it:
#include <boost/utility/identity_type.hpp>
SET_TYPE_NAME(BOOST_IDENTITY_TYPE((std::map<int, int>)), "TheMap");
http://www.boost.org/doc/libs/1_50_0/libs/utility/identity_type/doc/html/index.html
I like the typedef way proposed by hmjd better, but for the record, the usual way I've seen around this is to kick the angle brackets out of the macro and write:
#define SET_TYPE_NAME(TYPE, NAME) \
template<typename T> \
std::string name(); \
\
template<> \
std::string name TYPE() { \
return NAME; \
}
Usage:
SET_TYPE_NAME(<std::map<int, int> >, "TheMap")
This is a variation of an old technique used for error message reporting and fprintf:
#define Error(args) do { \
printf("ERROR: "); \
printf args; \
printf("\n"); \
return 1; \
} while(0)
Called with:
Error(("Index out of range: %d not in %d ... %d.", var, min, max));
It's ugly but it worked. Useful if the coding style rules ban typedef.
typedef std::map<int,int> IntMap_t;
SET_TYPE_NAME(IntMap_t, "TheMap")
You can declare a typedef and use it in the macro
A more generic way is to always use () around your arguments that may contain a comma and use CONCAT to remove the parenthesis. If you do so, you can define multiple parameters packs wits comma inside or put arguments in your favorite order
#ifndef CONCAT
#define CONCAT __VA_ARGS__
#endif
#define MYMACRO(tparam,classname)\
template < CONCAT tparam >\
class CONCAT classname {};
//create a template class X<T,U>
MYMACRO( (typename T,typename U) , (X<T,U>) )
I know this is an old question. Though this comes in top results on a Google search and nobody has yet mentioned an even more simple solution by defining a macro for the comma:
#define COMMA ,
SET_TYPE_NAME(std::map<int COMMA int>, "TheMap")
Related
I want to apply a function to data buffers and their types are known at runtime.
I use for that a templated function template <typename T1, typename T2, typename T3> void myFunction().
myFunction is a member of a class, which also contains the data structures storing the buffers. The buffers are stored in a char* pointer, and I have an enumerate to know the actual data type of my buffer, allowing me to cast the pointer into the correct type.
I also have a data structure to register all of the data type combination in that way :
// functionPtr is declared earlier
functionPtr = static_cast<void(*)(void)>( &myFunction< DataType1, DataType2, DataType3 >);
registerFunction(functionPtr);
Finally, I wrote a macro to parse each type combination.
My problem is that it seems to be too much data to expand for the compiler. I reduced to the minimal example below :
Preproc, 915 bytes
#include <boost/preprocessor.hpp>
// List the possible types
#define STRIP_ALL_TYPES \
(eIBT_Int8)(eIBT_UInt8) \
(eIBT_Int16)(eIBT_UInt16) \
(eIBT_Int32)(eIBT_UInt32) \
(eIBT_Real32) \
(eIBT_Binary) \
(eIBT_Label16)(eIBT_Label32)
# Generate all the combinations
#define GENERATE_TYPE_COMBINATION(r, product) (product)
// Generate the instruction for a given type combination
#define TEMPLATE_SPECIFIC_TYPE_COMBINATION(r, data, elem)\
functionPtr = static_cast<void(*)(void)>(\
&CurProcessorType::myFunction< BOOST_PP_SEQ_ENUM(elem) >);
// Generate all the possible instructions
#define GENERATE_TEMPLATE(ST)\
BOOST_PP_SEQ_FOR_EACH(TEMPLATE_SPECIFIC_TYPE_COMBINATION, _, \
BOOST_PP_SEQ_FOR_EACH_PRODUCT(GENERATE_TYPE_COMBINATION, ST))
GENERATE_TEMPLATE((STRIP_ALL_TYPES)(STRIP_ALL_TYPES)(STRIP_ALL_TYPES))
Try it online!
By expanding the macro, the generated lines compiled by the compiler should look like :
functionPtr = static_cast<void(*)(void)>( &CurProcessorType::myFunction< eIBT_Label16, eIBT_Label16, eIBT_Label16 >);
but when I test my code on TIO, I get the error .code.tio:23:1: error: macro "BOOST_PP_IIF_1" requires 2 arguments, but only 1 given GENERATE_TEMPLATE((STRIP_ALL_TYPES)(STRIP_ALL_TYPES)(STRIP_ALL_TYPES))
It works only with a few items in STRIP_ALL_TYPES.
Is there a workaround I can use to be able to compile?
Thanks
I'm not sure what you were trying to achieve with BOOST_PP_SEQ_FOR_EACH, as BOOST_PP_SEQ_FOR_EACH_PRODUCT already gives you all permutations of the template arguments:
#include <boost/preprocessor.hpp>
// List the possible types
#define STRIP_ALL_TYPES \
(eIBT_Int8)(eIBT_UInt8) \
(eIBT_Int16)(eIBT_UInt16) \
(eIBT_Int32)(eIBT_UInt32) \
(eIBT_Real32) \
(eIBT_Binary) \
(eIBT_Label16)(eIBT_Label32)
// Generate all the combinations
#define GENERATE_TYPE_COMBINATION(r, product)\
functionPtr = static_cast<void(*)(void)>(\
&CurProcessorType::myFunction< BOOST_PP_SEQ_ENUM(product) >);
#define GENERATE_TEMPLATE(ST)\
BOOST_PP_SEQ_FOR_EACH_PRODUCT(GENERATE_TYPE_COMBINATION, ST)
GENERATE_TEMPLATE((STRIP_ALL_TYPES)(STRIP_ALL_TYPES)(STRIP_ALL_TYPES))
Try it online!
Also, I should note that the cast to void(*)(void) is not valid if CurProcessorType::myFunction is a non-static member function. You should use a member function pointer type.
I am writing a macro that takes a declaration as its single argument. Is it possible to deduce the type of the declaration inside the macro without splitting up the single argument into separate type and identifier arguments?
#define M(declaration) \
declaration; \
static_assert(sizeof(/* deduce type of 'declaration' */) == 4, "!")
M(int i);
M(double d{3.14});
M(std::string s{"Hello, world!"});
The following implementation would work but it feels less user-friendly (imo):
#define M(type, identifier) \
type identifier; \
static_assert(sizeof(type) == 4, "!")
M(int, i);
M(double, d{3.14});
M(std::string, s{"Hello, world!"});
If possible, I would prefer to take the declaration as a single argument.
Related question: Macro to get the type of an expression; but I failed to get that code to work in my example (compiler error: expected nested-name-specifier).
If your static assertion message is really that simple "!"1, I suggest you ditch the preprocessor. Make the type system work for you instead:
namespace detail {
template<typename T>
struct check_declared_type {
using type = T;
static_assert(sizeof(type) == 4, "!");
};
}
template<typename T>
using M = typename detail::check_declared_type<T>::type;
// .. Later
int main() {
M<int> i;
M<double> d{3.14};
M<std::string> s{"Hello, world!"};
}
1 - Specifically, if you don't need the preprocessor to stringify anything for you.
This macro ought to work for all your examples, but it does have a nasty issue:
#define M(declaration) \
declaration; \
do { \
struct dummy__ { declaration; }; \
static_assert(sizeof(dummy__) == 4, "!"); \
} while (false)
The problem is that an initializer within a class definition must use either the = token or a braced-init-list at top level, and not parentheses at top level. So for example M(SomeClass obj(true, 3)); won't compile, even if sizeof(SomeClass)==4. Since braced initializers are not entirely equivalent to parenthesis initializers, this means some declarations would be impossible to use with the macro.
Say I have a macro like this:
#define SET_TYPE_NAME(TYPE, NAME) \
template<typename T> \
std::string name(); \
\
template<> \
std::string name<TYPE>() { \
return NAME; \
}
This won't work if I pass it a template that has more than one parameter, because the comma in the <int, int> is interpreted as separating the macro arguments, not the template arguments.
SET_TYPE_NAME(std::map<int, int>, "TheMap")
// Error: macro expects two arguments, three given
This problem seems to be solved by doing this:
SET_TYPE_NAME((std::map<int, int>), "TheMap")
But now another problem arises, one that I really did not expect:
template<>
std::string name<(std::map<int, int>)>()
// template argument 1 is invalid
It seems that the extra parentheses make the template argument invalid. Is there any way around this?
Besides typedef, you could switch the order of the arguments and use variadic macros (requires C99 or C++11-compatible compiler):
#define SET_TYPE_NAME(NAME, ...) \
template<typename T> \
std::string name(); \
\
template<> \
std::string name<__VA_ARGS__>() { \
return NAME; \
}
...
SET_TYPE_NAME("TheMap", std::map<int, int>)
You could use typedef:
typedef std::map<int, int> int_map;
SET_TYPE_NAME(int_map, "TheMap");
boost's BOOST_FOREACH suffers from the same issue.
Some time ago (while searching the web for the utility of Identity<T>) I got to this page.
In brief, to answer the question, if you don't have C++11 support and/or can't (or don't want to) use a typedef, you call your macro the "right" way:
// If an argument contains commas, enclose it in parentheses:
SET_TYPE_NAME((std::map<int, int>), "TheMap")
// For an argument that doesn't contain commas, both should work:
SET_TYPE_NAME((SomeType1), "TheType1")
SET_TYPE_NAME(SomeType2, "TheType2")
and then to get rid of the (possible) unwanted parentheses around the type, you can use a "helper" like this:
template<typename> struct RemoveBrackets;
template<typename T> struct RemoveBrackets<void (T)> {
typedef T Type;
};
and in your macro change the line:
std::string name<TYPE>() { \
to:
std::string name< RemoveBrackets<void (TYPE)>::Type >() { \
(or define a helper macro, say
#define REMOVE_BRACKETS(x) RemoveBrackets<void (x)>::Type
then replace the line with
std::string name< REMOVE_BRACKETS(TYPE) >() { \
).
(For the full story read the paragraph "An even better solution" at the end of the article linked above.)
Edit: just found that. But it uses <void X> when it really should use <void (X)> (in get_first_param<void X>::type); indeed the parentheses are necessary if you pass a "simple", non-bracketed argument (like SomeType2 in my code above) -- and they don't hurt if X is already bracketed (e.g., void ((SomeType1)) is equivalent to void (SomeType1); again, see the article). (By the way, I noticed that many answers on the other SO page are in essence "Macros are dumb". I won't comment, though.)
I'd like to add an answer to my own question. Now that Boost 1.50.0 was released, Boost.Utility has a new mini-library that helps with this kind of thing. If you look at the source you'll see it's implemented the same as gx_'s solution. Here's how to use it:
#include <boost/utility/identity_type.hpp>
SET_TYPE_NAME(BOOST_IDENTITY_TYPE((std::map<int, int>)), "TheMap");
http://www.boost.org/doc/libs/1_50_0/libs/utility/identity_type/doc/html/index.html
I like the typedef way proposed by hmjd better, but for the record, the usual way I've seen around this is to kick the angle brackets out of the macro and write:
#define SET_TYPE_NAME(TYPE, NAME) \
template<typename T> \
std::string name(); \
\
template<> \
std::string name TYPE() { \
return NAME; \
}
Usage:
SET_TYPE_NAME(<std::map<int, int> >, "TheMap")
This is a variation of an old technique used for error message reporting and fprintf:
#define Error(args) do { \
printf("ERROR: "); \
printf args; \
printf("\n"); \
return 1; \
} while(0)
Called with:
Error(("Index out of range: %d not in %d ... %d.", var, min, max));
It's ugly but it worked. Useful if the coding style rules ban typedef.
typedef std::map<int,int> IntMap_t;
SET_TYPE_NAME(IntMap_t, "TheMap")
You can declare a typedef and use it in the macro
A more generic way is to always use () around your arguments that may contain a comma and use CONCAT to remove the parenthesis. If you do so, you can define multiple parameters packs wits comma inside or put arguments in your favorite order
#ifndef CONCAT
#define CONCAT __VA_ARGS__
#endif
#define MYMACRO(tparam,classname)\
template < CONCAT tparam >\
class CONCAT classname {};
//create a template class X<T,U>
MYMACRO( (typename T,typename U) , (X<T,U>) )
I know this is an old question. Though this comes in top results on a Google search and nobody has yet mentioned an even more simple solution by defining a macro for the comma:
#define COMMA ,
SET_TYPE_NAME(std::map<int COMMA int>, "TheMap")
How can I create a typedef using a macro (#define)?
I am, for various reasons, trying to use a macro to do typedef'ing in C. Something along the lines of templates for C++.
As an example, I would expect the preprocessor to expand the #define and typedef a struct whose "content" member is of type char:
#define DEFINE_FOO_TYPE(content_type__, content_type_name__) \
typedef struct { \
content_type__ content; \
} content_type_name__
DEFINE_FOO_TYPE(char, foo_t);
foo_t foo_var;
foo_var.content = 'g';
Apparently that is not the case. Is it possible to do these sort of things in C at all, or is the only way a C++ template?
To preempt questions along the lines of "why don't you just use a C++ template?". The answer is that I'm trying to do it in C because it's fun.
Try this:
#define DEFINE_FOO_TYPE(content_type__, content_type_name__) \
typedef struct { \
content_type__ content; \
} content_type_name__
DEFINE_FOO_TYPE(char, foo_t);
int main() {
foo_t foo_var;
foo_var.content = 'g';
return 0;
}
I.e. use content_type_name__ instead of element_type_name__
Proof of compilation
I posted in a comment because I thought it was too trivial, but here it is:
Surely you are not just pasting that quoted code into gcc, are you? You can't just execute code outside any function. Put the last line inside a function.
simply put the last two lines of code into main()
or any function it will work for sure.
How do you write a macro with variable number of arguments to define a function? Suppose that we define the class class1 with 2 parameters and class class2 with three parameters.
class class1 {
public:
int arg1;
int arg2;
class1(int x1, int x2): arg1(x1), arg2(x2) {}
};
class class2 {
public:
int arg1;
int arg2;
int arg3;
class1(int x1, int x2, int x3): arg1(x1), arg2(x2), arg3(x3) {}
};
For each class that I define or even classes that have been defined before I want to write the following:
template<> inline void writeInfo<class1>(const class1& obj, FILE* fp) {
writeAmount(2, fp);
writeName("arg1", fp);
writeInfo(obj.arg1, fp);
writeName("arg2", fp);
writeInfo(obj.arg2, fp);
}
template<> inline void writeInfo<class2>(const class2& obj, FILE* fp) {
writeAmount(3, fp);
writeName("arg1", fp);
writeInfo(obj.arg1, fp);
writeName("arg2", fp);
writeInfo(obj.arg2, fp);
writeName("arg3", fp);
writeInfo(obj.arg3, fp);
}
We do not need to care about the definitions of writeAmount, writeName or writeInfo. What I would like to do is write something like:
MACROWRITEINFO(class1, 2, arg1, arg2);
MACROWRITEINFO(class2, 3, arg1, arg2, arg3);
Is it possible to create such macro so that it can expand to the above template definitions? I've read in a lot of places that macros are evil, but in this case I believe that they are very helpful since they'll reduce the amount of code I type and thus the amount of typos I'll make during the creation of the template functions.
First of all you should improve your formatting/code. Your code lacks "class" keywords and semicolons after classes definitions - when you post a snippet make sure it's proper code, because some people (i.e. me) will try to compile it.
Second of all, dont use function template specialization. If macros are evil, then they must be satan incarnation. Just stick to the good old overloads. See here for details.
And at least - an answer. You could mess around with variadic macros if all args were of the same type - for example, you could create an array inside writeInfo function and iterate over elements. Since it's cleary not the case here you can define many variants of MACROWRITEINFO macro for different number of parameteres, using some common blocks to reduce code repetition. For example:
#define MACROWRITEINFO_BEGIN(type, amount) \
void writeInfo(const type& obj, FILE* fp) \
{ \
writeAmount(amount, fp);
#define MACROWRITEINFO_NAMEINFO(name) \
writeName(#name, fp); \
writeInfo(obj.##name, fp);
#define MACROWRITEINFO_END() \
}
Using those you can now define variants based on number of arguments.
#define MACROWRITEINFO1(type, arg1) \
MACROWRITEINFO_BEGIN(type, 1) \
MACROWRITEINFO_NAMEINFO(arg1) \
MACROWRITEINFO_END()
#define MACROWRITEINFO2(type, arg1, arg2) \
MACROWRITEINFO_BEGIN(type, 2) \
MACROWRITEINFO_NAMEINFO(arg1) \
MACROWRITEINFO_NAMEINFO(arg2) \
MACROWRITEINFO_END()
And so on...
EDIT:
Well I guess it is possible to use variadic macros here. Take at look at this SO question. It's pure madness, but you should be able to achieve what you want.
EDIT:
My idea was to expand variadic arguments into array then iterate over them; if they were of the same type, let's say int, you could write:
#define VAARGSSAMPLE(...) \
int args[] = { __VA_ARGS__ }; \
for (int i = 0; i < sizeof(args)/sizeof(int); ++i) \
{ \
printf("%d\n", args[i]); \
}
VAARGSSAMPLE(1, 5, 666);
So if all your variables were of the same type you could put them in an array. But they are not, so it won't do. If you really, really want to stick to variadic arguments go to my first edit.
I don't think it's possible to do that with a macro. You can use variable arguments (variadic) but you can't generate code which depends on the arguments.
I'd suggest you create a DSL (e.g. simple xml..) and generate code out of it. this is much cleaner and good practice.
You could do this:
<writeInfos>
<writeInfo class="class1" amount="3">
<arguments>
<argument>arg1</argument>
<argument>arg2</argument>
</arguments>
</writeInfo>
</writeInfos>
Then create source code out of this. You should add this step in your build process..
But you can also define something much simpler.. you could put your MACROWRITEINFO "functions" in a text file and parse it yourself..