0

I created a macro taking 3 conditions (as example, 8 in my real case):

#define FOO(A,B,C) \
  BOOST_PP_IF(A, a1, a2) \
  BOOST_PP_IF(B, b1, b2) \
  BOOST_PP_IF(C, c1, c2)

This works as I expect. Now I want to expand all the possibilities:

FOO(0,0,0)
FOO(0,0,1)
FOO(0,1,0)
FOO(0,1,1)
FOO(1,0,0)
FOO(1,0,1)
FOO(1,1,0)
FOO(1,1,1)

By this way, I have to write 8 lines. In my real case, I have to write 256 lines.

How can I generate it directly with (boost) preprocessor tools ?

MCV example with constructors declaration of a class Foo close to my real problem:

#define WRITE_FOO(A,B,C) \
  Foo(int a1 BOOST_PP_COMMA_IF(A) BOOST_PP_IF(A, int a2, BOOST_PP_EMPTY()),
           double b1 BOOST_PP_COMMA_IF(B) BOOST_PP_IF(B, double b2, BOOST_PP_EMPTY()),
           bool c1 BOOST_PP_COMMA_IF(C) BOOST_PP_IF(C, bool c2, BOOST_PP_EMPTY()));

Then

class Foo {
  public:
    WRITE_FOO(0,0,0)
    WRITE_FOO(0,0,1)
    WRITE_FOO(0,1,0)
    WRITE_FOO(0,1,1)
    WRITE_FOO(1,0,0)
    WRITE_FOO(1,0,1)
    WRITE_FOO(1,1,0)
    WRITE_FOO(1,1,1)
  private:
    int a_1;
    int a_2;
    double b_1;
    double b_2;
    bool c_1;
    bool c_2;
};

is extended to

class Foo {
  public:
    Foo(int a1, double b1, bool c1);
    Foo(int a1, double b1, bool c1, bool c2);
    Foo(int a1, double b1, double b2, bool c1);
    Foo(int a1, double b1, double b2, bool c1, bool c2);
    Foo(int a1, int a2, double b1, bool c1);
    Foo(int a1, int a2, double b1, bool c1, bool c2);
    Foo(int a1, int a2, double b1, double b2, bool c1);
    Foo(int a1, int a2, double b1, double b2, bool c1, bool c2);
  private:
    int a_1;
    int a_2;
    double b_1;
    double b_2;
    bool c_1;
    bool c_2;
};

The implementation is similar to:

#define IMPLEMENT_FOO(A,B,C) \
  Foo::Foo(int a1 BOOST_PP_COMMA_IF(A) BOOST_PP_IF(A, int a2, BOOST_PP_EMPTY()),
           double b1 BOOST_PP_COMMA_IF(B) BOOST_PP_IF(B, double b2, BOOST_PP_EMPTY()),
           bool c1 BOOST_PP_COMMA_IF(C) BOOST_PP_IF(C, bool c2, BOOST_PP_EMPTY())): \
    a_1(a1), \
    a_2(BOOST_PP_IF(A, a2, a1)), \
    b_1(b1),
    b_2(BOOST_PP_IF(B, b2, b1)), \
    c_1(c1),
    c_2(BOOST_PP_IF(C, c2, c1)) \
{}
Caduchon
  • 4,574
  • 4
  • 26
  • 67
  • 3
    part of me wants to reply "by not using macros" – UKMonkey May 14 '18 at 14:13
  • @UKMonkey: if I don't use macro, I have to implement 256 versions of a class... But any constructive comment is welcome. – Caduchon May 14 '18 at 14:15
  • 1
    Can you use http://en.cppreference.com/w/cpp/types/enable_if ? Or can you make your class into a template? – UKMonkey May 14 '18 at 14:16
  • 2
    Pls post your minimum C++ version. This can be solved with TMP. The higher your C++ version the conciser the solution will be. – rustyx May 14 '18 at 14:17
  • I don't use C++11, but I can use enable_if from boost. – Caduchon May 14 '18 at 14:17
  • I can't use templates because the FOO macro generate constructors with variable number of arguments (I can't use C++11 for compatibility reason). – Caduchon May 14 '18 at 14:20
  • 2
    This is pretty much the perfect example of an XY problem ... https://meta.stackexchange.com/questions/66377/what-is-the-xy- problem We're digging to understand your problem; to give you a more useful solution – UKMonkey May 14 '18 at 14:22
  • Edit done with a example close to my real case. – Caduchon May 14 '18 at 14:31
  • I suppose (1) the actual types (instead of int, double, bool) are not (implicitly) convertible and – user202729 May 14 '18 at 14:32
  • I am curious what use case would need 256 different constructors for the same class. – Max Langhof May 14 '18 at 14:35
  • @user202729, no implicit convertion (actual types are own classes). No default value. – Caduchon May 14 '18 at 14:44
  • From what I can see, the cleanest route so far is to make all your optional arguments a `boost::optional<>` and then default them to `boost::none`. The example you give though is a function; not a class - which is also a little confusing – UKMonkey May 14 '18 at 14:46
  • @MaxLanghof: The main class is a generic class for mathematical testcases encoding. The constructor takes 8 pairs of data describing the test. In a lot a spacial cases, a pair is a duplication of the same data. Having all the constructors available makes the encoding really clearer (thousand of tests defined in a massive unit-testing procedure). – Caduchon May 14 '18 at 14:49
  • You call the macro "WRITE_FOO" - is this a big number of arguments so that you can stream data in different ways depending on what you're streaming? – UKMonkey May 14 '18 at 14:49
  • 2
    I edited with a constructor example and implementation of the constructor. – Caduchon May 14 '18 at 15:02

2 Answers2

3

This is a linear-code solution that solves the question (although I would not suggest doing that in real code, use templates instead if possible)

#define W2(...) W1(__VA_ARGS__,0) W1(__VA_ARGS__,1)
#define W1(...) W0(__VA_ARGS__,0) W0(__VA_ARGS__,1)
W2(0) W2(1)

Try it online!

user202729
  • 3,358
  • 3
  • 25
  • 36
0

Not directly answer your question (this does not use macros), but you can solve your problem instead using template metaprogramming.

Unfortunately variadic template is a C++11 feature. This question contain more details about how to implement them for C++03 with pure C++, or this question with Boost.

Also requires linear code.

struct F{
    int i1,i2,d1,d2;

    template<class... Args>
    F(Args... args){init(args...);}

    void init(){} // base case
    template<class... Args>
    void init(int i1_,Args... args){i1=i1_;i2=i1_;init(args...);}
    template<class... Args>
    void init(int i1_,int i2_,Args... args){i1=i1_;i2=i2_;init(args...);}
    template<class... Args>
    void init(double d1_,Args... args){d1=d1_;d2=d1_;init(args...);}
    template<class... Args>
    void init(double d1_,double d2_,Args... args){d1=d1_;d2=d2_;init(args...);}

};

Try it online! (gcc) or Try it online! (clang)

Requires the types to be assignable. You may want to add some std::move if you're worried about efficiency.

user202729
  • 3,358
  • 3
  • 25
  • 36