Using boost preprocessor to stringize a tuple - c++

I am beginner in using boost preprocessor. I want to use this library to generate a name from a tuple, for example I have a tuple like this (float, string, int, bool) and I want to construct something like this "Foo<float, string, int, bool>". I thought that it must be easy by using BOOST_PP_STRINGIZE, BOOST_PP_CAT and BOOST_PP_REPEAT but I unfortunately could not find a way to generate the string I wanted.
Please give me a suggestion about creating this string. Foo is a class name and tuple is generated automatically during preprocess.

You can use BOOST_PP_TUPLE_ENUM to get a comma-separated expansion of the tuple elements. You can then use #__VA_ARGS__ to stringize the resulting list. See it live:
#define STRINGIZE_ALL_I(...) #__VA_ARGS__
#define STRINGIZE_ALL(...) STRINGIZE_ALL_I(__VA_ARGS__)
#define MAKE_STRING(tuple) STRINGIZE_ALL(Foo<BOOST_PP_TUPLE_ENUM(tuple)>)
// "Foo<float, string, int, bool>"
MAKE_STRING((float, string, int, bool))
STRINGIZE_ALL_I exists for the same reason you have an extra layer in STRINGIZE and CONCAT - to evaluate macros before stringizing. In this case, you would get a string containing BOOST_PP_TUPLE_ENUM((…)) if you neglected to have two layers.
Note that STRINGIZE_ALL is called with the argument list Foo<float, string, int, bool>. That's four arguments, not one.
If variadic macros are not available (e.g., C++98), you can take advantage of the fact that "abc" "def" will be turned into "abcdef" by the compiler. See it live:
#define STRINGIZE_ALL_MACRO(s, state, x) state "," BOOST_PP_STRINGIZE(x)
#define STRINGIZE_ALL(seq) \
BOOST_PP_SEQ_FOLD_LEFT( \
STRINGIZE_ALL_MACRO, \
BOOST_PP_STRINGIZE(BOOST_PP_SEQ_HEAD(seq)), \
BOOST_PP_SEQ_TAIL(seq) \
)
#define MAKE_STRING(size, tuple) \
STRINGIZE_ALL( \
BOOST_PP_TUPLE_TO_SEQ( \
size, \
(Foo<BOOST_PP_TUPLE_ENUM(size, tuple)>) \
) \
)
// "Foo<float" "," "string" "," "int" "," "bool>"
MAKE_STRING(4, (float, string, int, bool))
For this version, your autogenerated tuple must generate a size as well. It's entirely possible to generate a tuple (size, (elems)) that you can use with this macro.

Related

Checking that macro parameter is one of the allowed ones?

I'd like to accomplish something like the following:
#define FOO(bar, ...) \
static_assert(bar == "foo" || bar == "bazz", "Invalid value for bar") \
...
In other words, I'd like to check at compile time that the value given to the macro is one of the allowed ones. What is the cleanest way for doing a compile time string comparison when comparing against strings of variable length?
You could use string views.
#include <string_view>
using namespace std::string_view_literals;
// Note the sv after the string
#define FOO(bar, ...) \
static_assert(bar == "foo"sv || bar == "bazz"sv, "Invalid value for bar") \
...
The expression "foo"sv invokes a literal operator. It constructs a std::string_view from "foo". std::string_view has overloaded == operators for comparing with strings. These overloaded operators are constexpr which means that they can be evaluated at compile time.

Is it possible to create a variadic macro which generates a forwarding function?

I'm pretty sure the answer to this question is a "no" but I would be very glad to be proven wrong
Say I have a variadic template function
template <typename... Args>
void log(Args&&... args) { ... }
...and I also have macros to generate concrete logging functions. Like:
#define LOG_2(fname, arg0_type, arg0_name, arg1_type, arg1_name) \
void fname(arg0_type arg0_name, arg1_type arg1_name) { \
log(std::forward<arg0_type>(arg0_name), std::forward<arg1_type>(arg1_name)); }
Macros named LOG_0 to LOG_10 with similar code also exist.
I use them to generate special logging functions which serve two purposes. First they give strong types to the arguments, and second they give them names, which with code-completion cam be used as hints. Example:
LOG_2(progress_log, const char*, msg, float, part)
...
progress_log("Loading: ", complete/total);
Then besides the strong typing of the args, I get the following nice code completion hint:
So, is it possible to drop the explicit arity in the macros and write something which has the exact same effect but uses variadic macros? ...and also isn't macro which uses a counter to call one of the underlying arity-macros by concatenating the appropriate number with LOG_.
I am aware I can count the arguments but most code-completion tools utterly fail to expand counting macros, especially if the possible argument count is big (like 10). Thus the code-completion effect gets lost. And if I would be willing to drop the code-completion hint effect, then I'd rather go with a something like LOG_FUN(progress_log, void(const char*, float)) which can also be used to generate compilation errors, but can have no argument names as hints
The Boost.Preprocessor library is made for just this sort of case when you really want to do something fancy with preprocessor macros. It can get a bit ugly, though.
#include <boost/preprocessor/variadic/to_seq.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/control/if.hpp>
#include <boost/preprocessor/arithmetic.hpp>
#include <boost/preprocessor/punctuation.hpp>
#include <boost/preprocessor/logical.hpp>
#include <utility>
template <typename... Args>
void log(Args&&...) {}
#define DEF_LOG_COMMA_BEFORE_PAIR(i) \
BOOST_PP_COMMA_IF(BOOST_PP_AND(i, BOOST_PP_NOT(BOOST_PP_MOD(i,2))))
#define DEF_LOG_EXPAND_PARAM(r, data, i, elem) \
DEF_LOG_COMMA_BEFORE_PAIR(i) elem
#define DEF_LOG_EXPAND_CALL(r, data, i, elem) \
DEF_LOG_COMMA_BEFORE_PAIR(i) \
BOOST_PP_IF( BOOST_PP_MOD(i,2), \
(elem), \
std::forward<elem> )
#define DEF_LOG_FUNC(name, ...) \
inline void name( \
BOOST_PP_SEQ_FOR_EACH_I(DEF_LOG_EXPAND_PARAM, ~, \
BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \
) { log( \
BOOST_PP_SEQ_FOR_EACH_I(DEF_LOG_EXPAND_CALL, ~, \
BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \
); }
DEF_LOG_FUNC(progress_log, const char*, msg, float, part)
int main() {
progress_log("Progress", 0.25);
}
To pull this apart, first look at the DEF_LOG_COMMA_BEFORE_PAIR macro. It's just a helper that expands to a comma if the argument i is an even number other than zero, and expands to nothing otherwise. By putting it before the result of transforming one vararg parameter, we get a comma every other item. (After might seem more natural, but it would be a little more complicated to exclude a comma after the last term than this way of excluding the comma before the first item.) Note BOOST_PP_COMMA_IF is used to avoid expanding to a comma too early, which would confuse the other Boost macros.
Jumping down to the definition of the main macro DEF_LOG_FUNC, you'll see it uses BOOST_PP_SEQ_FOR_EACH_I twice. This is a tool which applies a supplied macro to each term from a passed sequence of arguments. The macro given must accept four arguments:
r: used when you need multiple levels of preprocessor loops
data: a sequence of tokens passed directly through BOOST_PP_SEQ_FOR_EACH_I to each invocation of the macro
i: the zero-based index of the sequence element
elem: the actual sequence element
Neither of our macros actually needs data, so we just pass ~ as a dummy token to both BOOST_PP_SEQ_FOR_EACH_I uses. The two helper macros we pass to BOOST_PP_SEQ_FOR_EACH_I are DEF_LOG_EXPAND_PARAM for building the function parameter list and DEF_LOG_EXPAND_CALL for building the argument list to the actual log function. DEF_LOG_EXPAND_PARAM doesn't need to do anything but add commas, using that DEF_LOG_COMMA_BEFORE_PAIR helper. DEF_LOG_EXPAND_CALL needs to do different things to even and odd arguments, so in addition to the DEF_LOG_COMMA_BEFORE_PAIR tool again, it uses BOOST_PP_IF to transform even (type) arguments to std::forward<elem> and odd (parameter) arguments to (elem).
So as desired, the preprocessor replaces
DEF_LOG_FUNC(progress_log, const char*, msg, float, part)
as
inline void progress_log(
const char* msg , float part
) { log(
std::forward<const char*> (msg) , std::forward<float> (part)
); }
You can use some tags instead. If you need to provide hint for special cases then you can write some explicit specializations:
enum Category{normal, progress};
template<Category category = normal, typename... Args>
void log(Args... args) { ... }
template<>
void log<progress, char const *, float>(char const *, float) { ... }
// typing this will show generic and specialized variant hints in VS2017
log<progress>("Loading: ", complete/total);

c++ macro recognizing tokens as arguments

So, it's been a while since I have written anything in C++ and now I'm working on a project using C++11 and macros.
I know that by using the stringify operator I can do this:
#define TEXT(a) #a //expands to "a"
How am I supposed to use the preprocessor for recognizing the tokens like + and * to do this:
#define TEXT(a)+ ??? //want to expand to "a+"
#define TEXT(a)* ??? //want to expand to "a*"
when the input has to be in that syntax?
I have tried doing that:
#define + "+"
but of course it doesn't work. How can I make the preprocessor recognize those tokens?
NOTE:
This is actually part of a project for a small language that defines and uses regular expressions, where the resulting string of the macros is to be used in a regex. The syntax is given and we have to use it as it is without making any changes to it.
eg
TEXT(a)+ is to be used to make the regular expression: std::regex("a+")
without changing the fact that TEXT(a) expands to "a"
First,
#define TEXT(a) #a
doesn't “convert to "a"”. a is just a name for a parameter. The macro expands to a string that contains whatever TEXT was called with. So TEXT(42 + rand()) will expand to "42 + rand()". Note that, if you pass a macro as parameter, the macro will not be expanded. TEXT(EXIT_SUCCESS) will expand to "EXIT_SUCCESS", not "0". If you want full expansion, add an additional layer of indirection and pass the argument to TEXT to another macro TEXT_R that does the stringification.
#define TEXT_R(STUFF) # STUFF
#define TEXT(STUFF) TEXT_R(STUFF)
Second, I'm not quite sure what you mean with TEXT(a)+ and TEXT(a)*. Do you want, say, TEXT(foo) to expand to "foo+"? I think the simplest solution in this case would be to use the implicit string literal concatenation.
#define TEXT_PLUS(STUFF) # STUFF "+"
#define TEXT_STAR(STUFF) # STUFF "*"
Or, if you want full expansion.
#define TEXT_R(STUFF) # STUFF
#define TEXT_PLUS(STUFF) TEXT_R(STUFF+)
#define TEXT_STAR(STUFF) TEXT_R(STUFF*)
Your assignment is impossible to solve in C++. You either misunderstood something or there’s an error in the project specification. At any rate, we’ve got a problem here:
TEXT(a)+ is to be used to make the regular expression: std::regex("a+") without changing the fact that TEXT(a) expands to "a" [my emphasis]
TEXT(a) expands to "a" — meaning, we can just replace TEXT(a) everywhere in your example; after all, that’s exactly what the preprocessor does. In other words, you want the compiler to transform this C++ code
"a"+
into
std::regex("a+")
And that’s simply impossible, because the C++ preprocess does not allow expanding the + token.
The best we can do in C++ is use operator overloading to generate the desired code. However, there are two obstacles:
You can only overload operators on custom types, and "a" isn’t a custom type; its type is char const[2] (why 2? Null termination!).
Postfix-+ is not a valid C++ operator and cannot be overloaded.
If your assignment had just been a little different, it would work. In fact, if your assignment had said that TEXT(a)++ should produce the desired result, and that you are allowed to change the definition of TEXT to output something other than "a", then we’d be in business:
#include <string>
#include <regex>
#define TEXT(a) my_regex_token(#a)
struct my_regex_token {
std::string value;
my_regex_token(std::string value) : value{value} {}
// Implicit conversion to `std::regex` — to be handled with care.
operator std::regex() const {
return std::regex{value};
}
// Operators
my_regex_token operator ++(int) const {
return my_regex_token{value + "+"};
}
// more operators …
};
int main() {
std::regex x = TEXT(a)++;
}
You don't want to jab characters onto the end of macros.
Maybe you simply want something like this:
#define TEXT(a, b) #a #b
that way TEXT(a, +) gets expanded to "a" "+" and TEXT(a, *) to "a" "*"
If you need that exact syntax, then use a helper macro, like:
#define TEXT(a) #a
#define ADDTEXT(x, y) TEXT(x ## y)
that way, ADDTEXT(a, +) gets expanded to "a+" and ADDTEXT(a, *) gets expanded to "a*"
You can do it this way too:
#define TEXT(a) "+" // "a" "+" -> "a+"
#define TEXT(a) "*" // "a" "*" -> "a*"
Two string literals in C/C++ will be joined into single literal by specification.

Variadic macro without arguments

I am using some logging macros, which are supposed to print out the information provided by the __PRETTY_FUNCTION__ macro and if needed name and value of up to two arguments.
A simplified version of my code looks like
template<typename Value1, typename Value2>
void Log(std::string const& function,
std::string const& variable_1 = "", Value1 value_1 = Value1(0),
std::string const& variable_2 = "", Value2 value_2 = Value2(0)) {
std::cout << function << " "
<< variable_1 << " " << value_1 << " "
<< variable_2 << " " << value_2 << std::endl;
}
#define LOG0() Log(__PRETTY_FUNCTION__)
#define VARIABLE(value) #value, value
#define LOG1(value) Log(__PRETTY_FUNCTION__, VARIABLE(value))
#define LOG2(value, value1) Log(__PRETTY_FUNCTION__, VARIABLE(value), VARIABLE(value1))
#define LOG(arg0, arg1, arg2, arg, ...) arg
#define CHOOSE(...) LOG(,##__VA_ARGS__, LOG2, LOG1, LOG0)
#define Debug(...) CHOOSE(__VA_ARGS__)(__VA_ARGS__)
I can use these macros like
Debug();
int x = 0;
Debug(x);
int y = 1;
Debug(x, y);
When I compile this code with clang I get a nice output containing class and function information as well as name and value of the variables.
But I also get the warning that standard compliant code is not allowed to have zero variadic arguments.
warning: token pasting of ',' and __VA_ARGS__ is a GNU extension [-Wgnu-zero-variadic-macro-arguments]
#define CHOOSE(...) LOG(,##__VA_ARGS__, LOG2, LOG1, LOG0)
^
warning: must specify at least one argument for '...' parameter of variadic macro [-Wgnu-zero-variadic-macro-arguments]
Debug();
Gcc on the other hand fails to compile with
error: expected primary-expression before ‘)’ token
#define LOG1(value) Log(__PRETTY_FUNCTION__, VARIABLE(value))
^
Debug();
Obviously it is dangerous to work with zero variadic arguments.
Is there any way that I can turn this code into standard compliant code without removing the convenience of having just one macro that takes zero to two arguments?
If this is not possible, is there a way to make also gcc compile this code?
The hard part of this is distinguishing between Debug() and Debug(x). In both cases, you are technically passing a single argument to the macro Debug. In the first case, the token sequence of that argument is empty, and in the second case it contains a single token. These cases can be distinguished with a trick due to to Jens Gustedt.
Here's the trick:
#define COMMA_IF_PARENS(...) ,
Observe that COMMA_IF_PARENS X produces a comma if X starts with (...), and otherwise expands to a token sequence containing no additional (top-level) commas. Likewise, COMMA_IF_PARENS X () produces a comma if X is empty or starts with (...) and otherwise expands to a token sequence containing no additional (top-level) commas. (In each case, the token sequence also contains all the top-level commas from X itself.)
We can use that trick like this:
#define CHOOSE(...) \
LOG(__VA_ARGS__ \
COMMA_IF_PARENS __VA_ARGS__ \
COMMA_IF_PARENS __VA_ARGS__ (), \
CHOICES)
Note that:
COMMA_IF_PARENS __VA_ARGS__ produces the number of commas in __VA_ARGS__ plus 1 if __VA_ARGS__ starts with (...).
COMMA_IF_PARENS __VA_ARGS__ () produces the number of commas in __VA_ARGS__ plus 1 if __VA_ARGS__ is empty or starts with (...). (Note that this can fail if __VA_ARGS__ ends in the name of a function-like macro, and we don't address that potential problem here.)
Let c be the number of commas in __VA_ARGS__, p be 1 if __VA_ARGS__ starts with (...) and 0 otherwise, and e be 1 if __VA_ARGS__ is empty and 0 otherwise.
The number of macro arguments produced prior to CHOICES is 3 c + 2 p + e. Taken modulo 3, the number of commas is 0 or 2 for a normal argument, and 1 if we have an empty list of arguments.
This gives us 6 cases we care about:
#define CHOICES LOG2, impossible, LOG2, LOG1, LOG0, LOG1
#define LOG(a0, a1, a2, a3, a4, a5, arg, ...) arg
However, this doesn't quite work, because we need to delay expanding the LOG(...) macro invocation until after we expand the COMMA_IF_PARENS machinery. One way to do that is:
#define LPAREN (
#define EXPAND(...) __VA_ARGS__
#define CHOOSE(...) EXPAND(LOG LPAREN COMMA_IF_PARENS [...]))
We also should add another comma to the end of CHOICES so that we always have a (possibly empty) argument corresponding to the ... parameter of LOG.
Putting it all together, we get this:
#define COMMA_IF_PARENS(...) ,
#define LPAREN (
#define EXPAND(...) __VA_ARGS__
#define CHOOSE(...) \
EXPAND(LOG LPAREN \
__VA_ARGS__ COMMA_IF_PARENS __VA_ARGS__ COMMA_IF_PARENS __VA_ARGS__ (), \
LOG2, impossible, LOG2, LOG1, LOG0, LOG1, ))
#define LOG(a0, a1, a2, a3, a4, a5, arg, ...) arg
with everything else unchanged from your code. (This can be generalized much further, but the above is sufficient to demonstrate the technique.)

How to remove the enclosing parentheses with macro?

No comma is allowed in a macro argument because it will be treated as more than one arguments and the preprocessing will be wrong. However, we can parenthesize the argument to let preprocessor treat it as one argument. Is there a macro or other techniques which can remove the enclosing parentheses?
For example, if I define a macro like
#define MY_MACRO(a, b) ...
and use it like
MY_MACRO( A<int, double>, text );
will be wrong. use it like
MY_MACRO( (A<int, double>), text)
with a macro or technique to remove the parentheses will be fine. Boost provides BOOST_IDENTITY_TYPE macro for only types but not general cases
#define ESC(...) __VA_ARGS__
then
MY_MACRO( ESC(A<int, double>), text );
might do what you want.
This macro trick is similar to Yakk's solution but removes the need to explicitly pass in another macro as a parameter.
#include <stdio.h>
#define _Args(...) __VA_ARGS__
#define STRIP_PARENS(X) X
#define PASS_PARAMETERS(X) STRIP_PARENS( _Args X )
int main()
{
printf("without macro %d %d %d %d %d %d\n", (5,6,7,8,9,10) ); // This actually compiles, but it's WRONG
printf("with macro %d %d %d %d %d %d\n", PASS_PARAMETERS((5,6,7,8,9,10)) ); //Parameter "pack" enclosed in parenthesis
return 0;
}
Of course you could get creative by making the PASS_PARAMETERS macro into a variadic macro and pass in multiple parameter packs.
Only maybe removing parentheses:
If you need to strip one layer of parenthesis, but only if there are parenthesis to strip, this longer set of macros does the trick:
#define DEPAREN(X) ESC(ISH X)
#define ISH(...) ISH __VA_ARGS__
#define ESC(...) ESC_(__VA_ARGS__)
#define ESC_(...) VAN ## __VA_ARGS__
#define VANISH
This may be needed if you want to use MY_MACRO for different sets of datatypes:
#define MY_MACRO(a, b) DEPAREN(a), b
MY_MACRO( ({x, y, z}), text )
//> {x,y,z}, text
MY_MACRO( singlearg, text )
//> singlearg, text
How it works:
We start with DEPAREN(X) ESC(ISH X). If X has parenthesis, we get ESC(ISH(X)). If X does not have parenthesis, we get ESC(ISH X).
We then expand ESC(...) into ESC_(__VA_ARGS__), which expands the interior.
ISH(...) turns into ISH __VA_ARGS__, which strips one layer of parentheses from X. Now, regardless of whether or not X originally had parenthesis, we have ESC_(ISH X).
We now need to get rid of ISH. However, because we already defined ISH(...), we can't also define it as #define ISH . That's why we concatenate it with another token (VAN) to get VANISH X.
VANISH is defined as , so we are finally left with X, sans parentheses.
A simple hack could be to use variadic macros:
#define MY_MACRO(a, b...) ...
Then you can use it like:
MY_MACRO(text, A<int, double>)
The comma in the second argument is still interpreted as the argument separator (meaning the macro is actually called with three arguments), but it's expanded inside the macro, making the behavior the same. The variadic argument has to be last in the macro, however.