views:

166

answers:

4

consider the following templated datastructures

enum eContent{
    EINT = 1,
    EFLOAT = 2,
    EBOOL = 4
};

template<int>
struct Container{
    Container(){assert(false);} //woops, don't do that!
};

template<>
struct Container<EINT>{
    Container():i(123){}
    int i;
};

template<>
struct Container<EFLOAT>{
    Container():f(123.456f){}
    float f;
};

template<>
struct Container<EBOOL>{
    Container():b(true){}
    bool b;
};



<fancy macro goes here that creates me all kind of combinations including for example>
    template<>
    struct Container<EFLOAT | EBOOL>: public Container<EFLOAT>, public Container<EBOOL>{
        Container():Container<EFLOAT>(),Container<EBOOL>(){}
    };
</fancy macro>

such that I then can for example define a variable like this:

Container<EINT|EFLOAT|EBOOL> myVar;

how would I define this fancy macro?

Why I want this? Let it be for the sake of fun and learning metaprogramming

A: 

If you're doing what I think you are, take a look at boost::variant, which does exactly what you are trying to do.

Michael Goldshteyn
thanks for this, but i guess boost::variant is not the proper solution for me - I want the resulting types to have a determinable memory layout and minimal size
Mat
+3  A: 

Well, first off, || is the boolean or operator; when used as you have used it, it'll always result in 1 (or true, rather, but true is always promoted to 1 when cast to int as it is in this case) which in the case of your code is equal to EINT, so your template would always instantiate as Container<EINT>.

Presumably, you're looking for the bitwise or operator, |. Even then, the compiler is going to actually bitwise-or the values, so you'll get a value of 7 which will result in the unspecialized template being used, which will fail.

Exactly what are you trying to accomplish? There are ways to make a type that's flexible enough to hold multiple data of multiple types, but the or operator doesn't do anything remotely like what you want in the context of template arguments.

Jonathan Grynspan
i corrected the || typo in the question. yes it shall result in 7 and that's exactly what i want, a macro that creates the proper definition of `Container<7>` as well as all other bitwise combinations of the enum values
Mat
@Mat: I think Jonathan is right that you should look at trying what you want to do with the value type instead of by the container type. It would help if your question would contain what exactly you want to accomplish.
n1ck
let's say I want to define a lot of different types of VertexData for graphics processing. some can have uv coordinates, some can have multiple uv coordinates, some can have bitangents, some can have a vertexcolor and so on. depending on the application, a different, lightweight type of vertexData is required that combines some of those atributes and i don't want to define all possible combinations by hand
Mat
+1  A: 
enum eContent{
    eInt    = 1,
    eFloat  = 2,
    eBool   = 4
};

template<unsigned, unsigned>
struct Member {};

template<>
struct Member<eInt, eInt>{
    Member():i(123){}
    unsigned i;
};

template<>
struct Member<eFloat, eFloat>{
    Member():f(123.456f){}
    float f;
};

template<>
struct Member<eBool, eBool>{
    Member():b(true){}
    bool b;
};

template< unsigned members >
struct Container
    : Member< members & eInt, eInt >
    , Member< members & eFloat, eFloat >
    , Member< members & eBool, eBool >
{};

int main()
{
    Container< eFloat | eBool > c;
    c.f;    // OK
    c.b;    // OK
    c.i;    // !Nah
}

But I don't think it's good for anything, really, it's just a solution to the literal problem you stated.

If you have some real problem in mind (for which you think this could be a solution), try to ask about that.

Unless it's just play, or homework, of course. :-)

Cheers & hth.,

PS: As a matter of good C++ programming practice, reserve ALL UPPERCASE names for macros, and only for macros. That way you avoid many potential name collisions. Using ALL UPPERCASE for constants is a Java/Python/etc. convention, to some degree suitable for those languages, but decidedly not for C++. It stems from early C, where constants had to be expressed as macros. ALL UPPERCASE was (and is) used for macros, not for constants -- well, except Brian Kernighan, but let's not delve into history... ;-)

Alf P. Steinbach
aaah - took me a moment to understand, but that's cool! but how would I do the baseConstructor calling if there was a constructor taking an argument of same type for each member. for example `Member(Container<0x00000FFF> full):f(full.f){}` in case of the float member. how would I do the baseconstructor calling in the compound type?
Mat
or for the sake of avoiding the problem that the compound type is not defined yet, let's take a fixed `struct Full{float f, int i, bool b};`as argument
Mat
ok - this works like a charm! I'm now able to create arbitrary POD like objects with a single line - thanks so much - I admire your templating skills! =)
Mat
A: 

ok, I use your Container structs, combine them with XCont, and define the XContainer you want:

// a (bit-)LIST is an int that contains the value (TAIL<<1|HEAD),
// where TAIL is a LIST, and HEAD is either 1 or 0.
// while iterating through the LIST from right,
// SHL counts how far each consumed HEAD has to be shifted left,
// back to its original position.

template<int TAIL,int HEAD,int SHL>
struct XCont;

//the empty LIST
template<int SHL>
struct XCont<0,0,SHL>{};

//HEAD equals 0, so we just recurse through the TAIL.
template<int TAIL,int SHL>
struct XCont<TAIL,0,SHL>:public XCont< (TAIL>>1) , (TAIL&1) , (SHL+1) >{};

//HEAD equals 1, so we do like above, but we have to append Container< (1<<SHL) >.
template<int TAIL,int SHL>
struct XCont<TAIL,1,SHL>:public XCont< (TAIL>>1) , (TAIL&1) , (SHL+1) >,public Container< (1<<SHL) >{};


template<int E>
struct XContainer:public XCont< (E>>1) , (E&1) , (0) >{};

It works like this:

  • The bitmask will be interpreted as LIST of bits from right to left (least significant bit first.
  • We iterate through the bits by bit-shifting (>>) the LIST.
  • The LIST is represented in functional programming style as tuple of HEAD and TAIL, where HEAD is the first element and TAIL is the LIST of remaining elements without that HEAD.
  • Whenever we find a 1-bit as HEAD, we want to recalculate its bit-position, so we count that via SHL. Of course, there are other approaches, too, like bitshifting a mask over the list and testing its value on 0 and non-0.

These do equal:

  • XContainer< EBOOL | EFLOAT | EINT >
  • XContainer< 0x04 | 0x02 | 0x01 >
  • XContainer< 0x07 >
  • XCont< (0x07>>1) , (0x07&1) , (0) >
  • XCont< ((EBOOL | EFLOAT | EINT) >>1) , (EINT) , (0) >
  • XCont< ((EBOOL | EFLOAT) >>1) , (EINT) , (0) >
  • XCont< ((EBOOL | EFLOAT) >>1) , (EINT>>0) , (0) >
  • XCont< TAIL,1,SHL >, where...
    • TAIL = ((EBOOL | EFLOAT) >>1)
    • 1 = (EINT>>0)
    • SHL = (0)
    • EINT = (1 << SHL)
  • XCont< TAIL>>1,TAIL&1,SHL+1 > ++ Container< (1 << SHL) >
  • ...
  • XCont< 0,0,(3) > ++ Container< (1<<(2)) > ++ Container< (1<<(1)) > ++ Container< (1<<(0)) >
  • XCont< 0,0,(3) > ++ Container< EBOOL > ++ Container< EFLOAT > ++ Container< EINT >

C++ templates behave like pattern-matching in Haskell. So, to me, it is easier to think about it in a simple style of Haskell functions without any fancy Haskell things. If anyone is curious:

xcontainer :: Int -> String
xcontainer(e) = "struct XContainer:" ++ (
                   xcont( (e .>>. 1) , (e .&. 1) , (0) )
                ) ++ "{}"

xcont :: (Int,Int,Int) -> String
xcont(   0,0,shl) = "public XCont<0,0," ++ show(shl) ++ ">"
xcont(tail,0,shl) = (  xcont( (tail .>>. 1) , (tail .&. 1) , (shl+1) )
                    )
xcont(tail,1,shl) = (  xcont( (tail .>>. 1) , (tail .&. 1) , (shl+1) )
                    ) ++ "," ++ container(1 .<<. shl)

container :: Int -> String
container(e) = "public Container<" ++ show(e) ++ ">"

(This is valid Haskell but in a non-haskell writing style.)

comonad
wow - can you please explain what's going on here?
Mat
+explanation appended.
comonad