繁体   English   中英

C ++元编程疯狂

[英]c++ metaprogramming madness

考虑以下模板化数据结构

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>

这样我就可以例如定义一个这样的变量:

Container<EINT|EFLOAT|EBOOL> myVar;

我将如何定义这个奇特的宏?

为什么我要这个? 为了乐趣和学习元编程而去

好吧,首先, || 是布尔值或运算符; 当您使用它时,它将始终为1 (或true ,而不是,但是当EINTint时, true总是提升为1 ,在这种情况下),在您的代码中等于EINT因此,您的模板将始终实例化为Container<EINT>

想必您正在寻找按位或运算符, | 即使这样,编译器实际上仍将按位或值,所以您将获得7的值,这将导致使用未专业化的模板,这将失败。

到底想完成什么? 有多种方法可以使类型足够灵活,以容纳多种类型的多个数据,但是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
}

但是,我认为这对任何事情都没有好处,实际上,这只是您所说的字面问题的解决方案。

如果您有一些实际的问题(您认为这可能是一个解决方案),请尝试提出有关问题。

当然,除非只是玩耍或做作业。 :-)

干杯,……

PS:作为良好的C ++编程惯例,请将所有大写名称保留给宏,并且仅保留给宏。 这样,您可以避免许多潜在的名称冲突。 将ALL UPPERCASE用作常量是Java / Python / etc。 约定,某种程度上适合那些语言,但绝对不适合C ++。 它起源于早期的C,其中常量必须表示为宏。 所有大写都是(并且是)用于宏的,而不是常量的-恩,除了布莱恩·科尼根(Brian Kernighan),但让我们不探究历史... ;-)

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

好的,我使用您的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) >{};

它是这样的:

  • 该位掩码将被解释为从右到左的位列表(最低有效位在前)。
  • 我们通过对LIST进行位移(>>)来对位进行迭代。
  • LIST以函数式编程风格表示为HEAD和TAIL的元组,其中HEAD是第一个元素,而TAIL是没有该HEAD的其余元素的LIST。
  • 每当我们找到一个1位作为HEAD时,我们都想重新计算其位的位置,因此我们通过SHL对其进行计数。 当然,还有其他方法,例如将掩码在列表上移位并在0和非0上测试其值。

这些确实相等:

  • XContainer <电子书| 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>,其中...
    • 尾巴=((EBOOL | EFLOAT)>> 1)
    • 1 =(EINT >> 0)
    • SHL =(0)
    • EINT =(1 << SHL)
  • XCont <TAIL >> 1,TAIL&1,SHL + 1> ++容器<(1 << SHL)>
  • ...
  • XCont <0,0,(3)> ++容器<(1 <<(2))> ++容器<(1 <<(1))> ++容器<(1 <<(0))>
  • XCont <0,0,(3)> ++容器<EBOOL> ++容器<EFLOAT> ++容器<EINT>

C ++模板的行为类似于Haskell中的模式匹配。 因此,对我来说,用一种简单的Haskell函数样式进行思考就比较容易,而无需花哨的Haskell事物。 如果有人好奇:

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) ++ ">"

(这是有效的Haskell,但采用非haskell写作风格。)

暂无
暂无

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

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