简体   繁体   English

C ++元编程疯狂

[英]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 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 为了乐趣和学习元编程而去

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> . 当您使用它时,它将始终为1 (或true ,而不是,但是当EINTint时, true总是提升为1 ,在这种情况下),在您的代码中等于EINT因此,您的模板将始终实例化为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. 即使这样,编译器实际上仍将按位或值,所以您将获得7的值,这将导致使用未专业化的模板,这将失败。

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. 有多种方法可以使类型足够灵活,以容纳多种类型的多个数据,但是or运算符不会像在模板参数的上下文中那样远程执行任何操作。

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. PS:作为良好的C ++编程惯例,请将所有大写名称保留给宏,并且仅保留给宏。 That way you avoid many potential name collisions. 这样,您可以避免许多潜在的名称冲突。 Using ALL UPPERCASE for constants is a Java/Python/etc. 将ALL UPPERCASE用作常量是Java / Python / etc。 convention, to some degree suitable for those languages, but decidedly not for C++. 约定,某种程度上适合那些语言,但绝对不适合C ++。 It stems from early C, where constants had to be expressed as macros. 它起源于早期的C,其中常量必须表示为宏。 ALL UPPERCASE was (and is) used for macros, not for constants -- well, except Brian Kernighan, but let's not delve into history... ;-) 所有大写都是(并且是)用于宏的,而不是常量的-恩,除了布莱恩·科尼根(Brian Kernighan),但让我们不探究历史... ;-)

如果您在做我想做的事,请看一下boost::variant ,它确实在做您想做的事情。

ok, I use your Container structs, combine them with XCont, and define the XContainer you want: 好的,我使用您的Container结构,将它们与XCont结合在一起,然后定义所需的XContainer:

// 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. 我们通过对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. LIST以函数式编程风格表示为HEAD和TAIL的元组,其中HEAD是第一个元素,而TAIL是没有该HEAD的其余元素的LIST。
  • Whenever we find a 1-bit as HEAD, we want to recalculate its bit-position, so we count that via SHL. 每当我们找到一个1位作为HEAD时,我们都想重新计算其位的位置,因此我们通过SHL对其进行计数。 Of course, there are other approaches, too, like bitshifting a mask over the list and testing its value on 0 and non-0. 当然,还有其他方法,例如将掩码在列表上移位并在0和非0上测试其值。

These do equal: 这些确实相等:

  • XContainer< EBOOL | XContainer <电子书| EFLOAT | EFLOAT | EINT > EINT>
  • XContainer< 0x04 | XContainer <0x04 | 0x02 | 0x02 | 0x01 > 0x01>
  • XContainer< 0x07 > XContainer <0x07>
  • XCont< (0x07>>1) , (0x07&1) , (0) > XCont <(0x07 >> 1),(0x07&1),(0)>
  • XCont< ((EBOOL | EFLOAT | EINT) >>1) , (EINT) , (0) > XCont <(((EBOOL | EFLOAT | EINT)>> 1),(EINT),(0)>
  • XCont< ((EBOOL | EFLOAT) >>1) , (EINT) , (0) > XCont <(((EBOOL | EFLOAT)>> 1),(EINT),(0)>
  • XCont< ((EBOOL | EFLOAT) >>1) , (EINT>>0) , (0) > XCont <(((EBOOL | EFLOAT)>> 1),(EINT >> 0),(0)>
  • XCont< TAIL,1,SHL >, where... XCont <TAIL,1,SHL>,其中...
    • TAIL = ((EBOOL | EFLOAT) >>1) 尾巴=((EBOOL | EFLOAT)>> 1)
    • 1 = (EINT>>0) 1 =(EINT >> 0)
    • SHL = (0) SHL =(0)
    • EINT = (1 << SHL) EINT =(1 << SHL)
  • XCont< TAIL>>1,TAIL&1,SHL+1 > ++ Container< (1 << SHL) > XCont <TAIL >> 1,TAIL&1,SHL + 1> ++容器<(1 << SHL)>
  • ... ...
  • XCont< 0,0,(3) > ++ Container< (1<<(2)) > ++ Container< (1<<(1)) > ++ Container< (1<<(0)) > XCont <0,0,(3)> ++容器<(1 <<(2))> ++容器<(1 <<(1))> ++容器<(1 <<(0))>
  • XCont< 0,0,(3) > ++ Container< EBOOL > ++ Container< EFLOAT > ++ Container< EINT > XCont <0,0,(3)> ++容器<EBOOL> ++容器<EFLOAT> ++容器<EINT>

C++ templates behave like pattern-matching in Haskell. C ++模板的行为类似于Haskell中的模式匹配。 So, to me, it is easier to think about it in a simple style of Haskell functions without any fancy Haskell things. 因此,对我来说,用一种简单的Haskell函数样式进行思考就比较容易,而无需花哨的Haskell事物。 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.) (这是有效的Haskell,但采用非haskell写作风格。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM