Reputation: 173
Using an X Macro to keep a couple of different things in sync using an out of module provided list.
Part of this involves creating an else/if chain to validate a string.
I am currently doing this:
if (0) {
// this will never run, and be compiled out
}
#define X(name) else if (some_thing == #name) { // do thing based on name... }
MY_LIST
#undef X
else {
// Report unrecognized string...
}
but it feels a little bit ugly to me (I am not a big fan of the un-executable if(0) {}
).
I have thought about doing this using a switch
statement...
constexpr hash(const char* const str, size_t len, uint64_t init_value)
// implementation left to imagination of the reader)...
//...
switch(hash(some_thing, len(some_thing))) {
#define X(name) case hash(#name, const_len(#name)): { break; }
MY_LIST
#undef X
default:
{
good_thing = false;
break;
}
}
// Now that it has been validated as a good string... save it etc.
But I worry about collisions (as unlikely as a collision is).
Is there a way to do the if/else chain without starting with an if(0)
?
I am stuck with std::14
if it helps (I know it doesn't).
Upvotes: 2
Views: 111
Reputation: 214770
It's actually pretty easy given that there is no such thing as else if
in C or C++. There is just else
which might refer to an immediately following if
statement.
Solution:
#define IF_ELSE(n) if(n==3) { puts("bingo!"); } else
LIST(IF_ELSE); // note the semicolon here when calling the X-macro list
Full example:
#include <stdio.h>
#define LIST(X) \
X(1) \
X(2) \
X(3) \
int main()
{
#define IF_ELSE(n) if(n==3) { puts("bingo!"); } else
LIST(IF_ELSE); // note the semicolon here
return 0;
}
Preprocessor output:
if(1==3) { puts("bingo!"); } else if(2==3) { puts("bingo!"); } else if(3==3) { puts("bingo!"); } else;
Indented as the language grammar always treats "else if
":
if(1==3)
{
puts("bingo!");
}
else
if(2==3)
{
puts("bingo!");
}
else
if(3==3)
{
puts("bingo!");
}
else
;
Upvotes: 0
Reputation: 31020
What is an if-else chain but a series of if
statements that terminate as soon as one works? In this case we can use break
together with do-while
:
do {
# define X(name) if (some_thing == #name) { // do thing based on name...; break; }
MY_LIST
# undef X
// Unrecognized case goes here.
} while (0);
Upvotes: 1
Reputation: 7688
You can set a flag in each string match - then you don't need else
:
bool found = false;
#define X(name) if (some_thing == #name) { found = true; /* do thing based on name here ...*/ }
MY_LIST
#undef X
if (!found) {
// Report unrecognized string...
}
Or, if it is important to avoid comparisons after a string is matched, you can do it like this (but it is harder to understand in my opinion):
#define X(name) if (some_thing == #name) { /* do thing based on name... */ } else
MY_LIST
#undef X
{
// Report unrecognized string...
}
Upvotes: 2
Reputation: 1350
Is there a way to do the if/else chain without starting with an if(0)?
I don't know of a better way to generate an if-else chain with macros. I agree it does look a little hacky.
However, here's another way way to validate that this string is one in the set built from X-Macros that compiles for C++14. Up to your taste if you think its better or not for your codebase.
#include <set>
#include <string>
void validate_string(std::string some_thing) {
/* Build the set of strings to test against with X-Macros */
static const std::set<std::string> validate_set = {
#define X(name) #name,
MY_LIST
#undef X
};
if (validate_set.find(some_thing) != validate_set.end()){
/* Then some_thing is one of the compile time constants */
}
}
Upvotes: 2