簡體   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