Question

struct Error
{
   MACRO(1, Connect);
   MACRO(2, Timeout);
};    

I need to define MACRO() in such way that the above code will generate the following code.

struct Error
{  
   static const int Connect = 1;
   static const int Timeout = 2;
   const char * const name[] = {"Connect", "Timeout"};
};

Is this possible or what is the alternative to get what I'm trying to do?

Was it helpful?

Solution 3

What you want, is to have a single list, that will automatically generate the definition and the name list, correct?

If so, search for X Macros in google.

Example:

#define EXPAND_AS_DEFINITION(a, b) static const int b = a;
#define EXPAND_AS_ARRAY(a, b) #b,

#define STATE_TABLE(ENTRY)  \
   ENTRY(1, Connect)       \
ENTRY(2, Timeout)   

struct Error
{       
   STATE_TABLE(EXPAND_AS_DEFINITION)
   static const char * const name[];      
};

const char * const Error::name[] = {STATE_TABLE(EXPAND_AS_ARRAY) 0};

OTHER TIPS

You can't do this directly, but you can if you move the macros to a separate location (such as a separate file):

macros.hpp

MACRO(1, Connect)
MACRO(2, Timeout)

#undef MACRO

the other file

struct Error
{
  #define MACRO(a, b) static const int b = a;
  #include "macros.hpp"

  const char * const name [] = {
  #define MACRO(a, b) #b,
  #include "macros.hpp"
  }
};

Alternatively, you could achieve a similar effect with Boost.Preprocessor.

Here's a Boost.Preprocessor solution:

#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/seq/size.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
#include <boost/preprocessor/stringize.hpp>

#define FIRST(a, b) a
#define SECOND(a, b) b

#define DECLARE_VAR(r, data, elem)              \
    static const int FIRST elem = SECOND elem;

#define NAME_ARRAY_ELEM(r, data, elem)          \
    BOOST_PP_STRINGIZE(FIRST elem), 

#define MACRO(seq)                                      \
    BOOST_PP_SEQ_FOR_EACH(DECLARE_VAR, ~, seq)          \
    const char * const name[] = {                       \
        BOOST_PP_SEQ_FOR_EACH(NAME_ARRAY_ELEM, ~, seq)  \
    }

int main()
{
    MACRO(((Connect, 1))((TimeOut, 2)));
    return 0;
}

You have to make sure to double bracket each ((Token, value)) pair, however you don't need a separate file for your macro.

It looks like like you are trying to define an enum Error that also has the strings as members. I will give you my own solution to this problem. (I'm not addressing the question but I believe that my answer is relevant for what I understand that OP is trying to do.)

And I just realized that OP is targeting C, not C++, so not sure if this can be done...

In MyEnum.hpp

#define MYENUM(X,...)                                        \
    struct X {                                               \
        enum Enum {__VA_ARGS__};                             \
        static const std::vector<std::string> names;         \
        static X::Enum which(const std::string& s) {         \ 
            return static_cast<X::Enum>(findEnum(s,names));  \
        }                                                    \
        static std::string str(X::Enum i) {                  \ 
            return names[i];}                                \
    }

Here findEnum() is just a linear search over the vector that returns the position index (additionally, in my implementation if it doesn't find it it throws an exception with all the possible correct inputs, I also do case insensitive comparison). Note that an ordered map instead of a vector would be more efficient (O(log(n)) instead of O(n)), but I didn't cared much because the size of those things is very small in my case.

Below the previous macro, declare your enum as

MYENUM(Error,Connect,Timeout); // I put the semicolon here not in the macro 

And in MyEnum.cpp, add

#include <boost/assign/list_of.hpp>

const std::vector<std::string> Error::names = boost::assign::list_of
("Connect")("Timeout");

(I think that it should be possible to use initialization lists with a modern compiler). The important thing here is to make sure that the order is the same, otherwise it will not work.

Then, you can do stuff like this:

Error::Enum err1 = Error::Connect;
Error::Enum err2 = Error::which("Timeout");
std::cout << "Got " << Error::str(err1) << " error. Not good.\n";
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top