开发者

c++ metaprogramming madness

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 开发者_如何学PythonI 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


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.


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... ;-)


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


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.)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜