簡體   English   中英

具有可變數量成員的結構或類

[英]Structure or Class with variable number of members

我想創建一個具有可變數量的類成員的結構/類,可以在編譯階段決定(就像在模板元編程中完成)

示例:其假設,其中類型和變量名稱將被指定為類型T1變量名稱應該是varName1等等.....

template <class T1 (varName1) >
MyClass
{
     T1 varName1;

}

template <class T1 (varName1), class T2 (varName2) >
MyClass
{
     T1 varName1;
     T1 varName2;
}

並且在主代碼中可以聲明如下或者可以指定類型和名稱的其他方式

MyClass Obj;

和MyClass :: somefunc()可以訪問變量名稱,如下所示

MyClass::somefunc()
{
     std::cout <<" abc value : " << abc << std::endl;
     std::cout <<" xyz value : " << xyz << std::endl;
}

這是否可以通過C ++中的模板元編程實現類型和變量名稱規范?

使用模板元編程和少量預處理,可以實現接近期望的語法:

//one has to "declare" once an attribute name to be able to use
//it later in any number of class declarations
DECLARE_ATTRIBUTE_NAME(foo);
DECLARE_ATTRIBUTE_NAME(quux);
DECLARE_ATTRIBUTE_NAME(bar);
DECLARE_ATTRIBUTE_NAME(baz);

//pass types and declared attribute names, separated by comma
typedef TupleWithNamedMembers<int, foo,
                              float, quux,
                              double, bar,
                              char, baz
                        > MyTuple;
//extra call to macro "MAKE_TUPLE" can be avoided, see below
class MyConstruct: public MAKE_TUPLE(MyTuple)
{ };

//usage
int main(int argc, char* argv[])
{
    MyConstruct construct;
    construct.foo = 3;
    construct.bar = 5.6;
    construct.quux = 8.9;
    construct.baz = 'h';
    return 0;
}

實施:

#ifndef TupleWithNamedMembersH
#define TupleWithNamedMembersH
//---------------------------------------------------------------------------

#include <Loki/typelist.h>
#include <Loki/HierarchyGenerators.h>

template<class T, int a>
struct attribute
{
};

//the generated id is not really unique in all cases
//one should provide better implementation
#define GENERATE_UNIQ_ID(name) ((sizeof(#name)<<16)|__LINE__)

//specializations of the struct "attribute" act like compile-time map between
//a name ID and an attribute name 
#define DECLARE_ATTRIBUTE_NAME_IMPL(name, id) \
    enum { id = GENERATE_UNIQ_ID(name) }; \
    template<class T> \
    struct attribute<T,id> \
    {\
        T name;\
    };
#define DECLARE_ATTRIBUTE_NAME(name)\
    DECLARE_ATTRIBUTE_NAME_IMPL(name, name)

//helps to pass pair of type and name ID as a single type
template<class T, int i>
struct pair
{
    static const int val = i;
    typedef T type;
};

//unpacks compile-time data from PairT and inherits attribute
//with name selected by ID
template<class PairT>
class holder: public attribute<typename PairT::type,PairT::val>
{    };

//turns template arguments into Loki::TypeList
template
<
    typename T1  = Loki::NullType, int i1 = 0, typename T2  = Loki::NullType, int i2 = 0,
    typename T3  = Loki::NullType, int i3 = 0, typename T4  = Loki::NullType, int i4 = 0,
    typename T5  = Loki::NullType, int i5 = 0, typename T6  = Loki::NullType, int i6 = 0,
    typename T7  = Loki::NullType, int i7 = 0, typename T8  = Loki::NullType, int i8 = 0,
    typename T9  = Loki::NullType, int i9 = 0, typename T10 = Loki::NullType, int i10 = 0
>
struct TupleWithNamedMembers
{
public:
    typedef Loki::TL::MakeTypelist<pair<T1,i1>, pair<T2,i2>,
                                   pair<T3,i3>, pair<T4,i4>,
                                   pair<T5,i5>, pair<T6,i6>,
                                   pair<T7,i7>, pair<T8,i8>,
                                   pair<T9,i9>, pair<T10,i10>
                         >::Result Result;
};

//this macro is required because of internal compiler error that I encounter
//Loki::GenScatterHierarchy makes a class inherit every attribute from the type list
#define MAKE_TUPLE(types) Loki::GenScatterHierarchy<types::Result, holder>

#endif //end of "TupleWithNamedMembers.h"

注意:MAKE_TUPLE宏應該是一個元函數,如果您的編譯器可以使用以下代碼:

template
<
    typename T1  = Loki::NullType, int i1 = 0, typename T2  = Loki::NullType, int i2 = 0,
    typename T3  = Loki::NullType, int i3 = 0, typename T4  = Loki::NullType, int i4 = 0,
    typename T5  = Loki::NullType, int i5 = 0, typename T6  = Loki::NullType, int i6 = 0,
    typename T7  = Loki::NullType, int i7 = 0, typename T8  = Loki::NullType, int i8 = 0,
    typename T9  = Loki::NullType, int i9 = 0, typename T10 = Loki::NullType, int i10 = 0
>
struct MakeTupleWithNamedMembers
{
private:
    typedef Loki::TL::MakeTypelist<pair<T1,i1>, pair<T2,i2>,
                                   pair<T3,i3>, pair<T4,i4>,
                                   pair<T5,i5>, pair<T6,i6>,
                                   pair<T7,i7>, pair<T8,i8>,
                                   pair<T9,i9>, pair<T10,i10>
                         >::Result type_list;
public:
    typedef Loki::GenScatterHierarchy<type_list, holder> Result;
};

//usage
class MyConstruct: public MakeTupleWithNamedMembers<int, foo, float, quux>::Result
{ };

如上所述不可能。 您可以使用boost的預處理器庫獲得相同的功能。

最終你所要求的不同於簡單地傳遞說...

struct Members
{
    int a_;
    double b_;
};

... INTO ...

template <class Members>
class Add_Stuff : public Members
{
  public:
    doSomething() { ... };
};

...在那個doSomething有能力迭代和打印成員,對吧?

您還可以編寫一個簡單的程序/腳本來讀取類型和標識符列表,並輸出所需的C ++代碼。 如果你有很多領域需要處理,這可能是一個很好的方法。 作為一個最小的頭頂輪廓,並假設輸入如此新行強制執行簡單類型與標識符划分(使您為數組創建typedef等):

std::string
idn1
const int*
idn2
my_typedef
ind3

...你可以生成一些C ++代碼...

std::ostringstream streaming;
streaming << "    void somefunc() const\n{\n    std::cout ";

cout << "class " << class_name << "\n{\n";
while (cin.getline(type) && cin.getline(identifier))
{
    cout << "    " << type << ' ' << identifier << '\n';
    streaming << "<< \"" << identifier << " \" << identifier << "\n        ";
}
cout << "  public:\n" << streaming.str() << "\n"
        "};\n";

顯然,您可以清理輸入以允許更自然的類型和標識符的C ++表達式並使解析邏輯復雜化 - 正則表達式可能足以滿足您的需求,或者您可以嘗試精神或自己動手。

預處理器hackery可以直接在C ++中實現類似的東西,但是恕我直言,編寫和維護它會更加丑陋和耗時。

如果你實際上不需要按標識符訪問成員,那么你可以做TokenMacGuy建議的每個字段可以具有相同的類型(不是那么糟糕 - 考慮boost :: variant或〜:: any),或者如果有另一個選項,你可以確保每個字段都有一個獨特的類型(同樣,這可以通過簡單的包裝模板類強制):我稱之為“類型映射” - 你可以在這里使用類型作為鍵,進入有效的類型的關聯容器 - 不同的值,在編譯時解析所有查找並支持somefunc()實現所需的自動迭代。 如果需要,可以將其與字符串組合用於運行時類型命名,但無法實現在編譯時解析或驗證的標識符字符串。

我在6年前(使用類型列表在Alexandrescu的Loki圖書館上面)實施了這樣的程序,並在增強郵件列表上詢問是否有人感興趣,但沒有人看到它的實用性,我並沒有真正嘗試解釋。 它實際上對日志系統非常有用,這促使我首先編寫它。 無論如何,我懷疑我沒有費心將代碼發布到該庫的保險庫,並且沒有方便,所以你需要從頭開始,除非MPL或其他一些庫實現了他們自己的類似“容器”同時(或事先......?)。

您不能使用模板,僅鍵入類型或某些類型的值來指定名稱。 您可以使用宏來完成它,但是在語言中嘗試做太多是我陷入了太多次的陷阱......還有另一條路可能對您有用: 代碼生成

考慮編寫一個腳本來讀取某些配置並吐出類的定義。 將腳本添加到構建過程中。 這可能比模板元編程或宏觀技術的黑色藝術更容易維護和理解。

python是我用於腳本的內容,每個類配置都像json一樣容易解析 - 但這些都是副作用

在我目前的項目中,我們有數千行生成的文件,分布在100多個文件中......並且這些生成腳本相對定期地進行修改,並且無痛苦地進行修改。

我記得Andrei Alexandrescu在他的“Modern C ++”一書中描述了類似的東西。 我沒有這里的副本,所以我無法准確說出它是什么以及它在哪里。

正如其他人所指出的那樣,不可能將名稱作為模板參數,但是他創建了一個可以像data.get<T1>()那樣訪問的結構。 如果有一種類型的多個數據,則可以執行data.get<T1,2>()

也許這有幫助。

我發現這個問題沒有明確說明,目的還不清楚。

對於序列化,我會考慮Boost庫的序列化支持

對於命名的,嚴格類型化的可選參數,一種可能性是使用Boost參數庫 ,第二種更簡單易用的可能性是我自己的選項包支持 它本質上是一組宏,通過一些不可穿透的內部模板黑魔法,生成像你要求的類。 我在Dr. Dobbs Journal上寫了一篇關於它的文章,但是這里有一個用例說明了一個主要優點,即生成的選項類可以與另一個類層次結構並行擴展:

#include <iostream>
#include <progrock/cppx/arguments/options_boosted.h>

struct AbstractButton
{
    // These members are not part of the cppx options scheme: in actual
    // usage you will instead have e.g. some API level widget states.
    int     hTextAlign;
    int     vTextAlign;
    int     buttonPlacement;

    // Defines a local class 'Options' with specified options & defaults.
    CPPX_DEFINE_OPTIONCLASS( Options, CPPX_OPTIONS_NO_BASE,
        ( hTextAlign,       int,        0   )
        ( vTextAlign,       int,        0   )
        ( buttonPlacement,  int,        0   )
        )

    explicit AbstractButton( Options const& params = Options() )
        : hTextAlign( params.hTextAlign() )
        , vTextAlign( params.vTextAlign() )
        , buttonPlacement( params.buttonPlacement() )
    {}
};

struct CheckBox: AbstractButton
{
    bool    isAuto;
    bool    is3State;

    // Defines an extension of the base class' 'Options' class.
    CPPX_DEFINE_OPTIONCLASS( Options, AbstractButton::Options,
        ( isAuto ,          bool,       true    )
        ( is3State,         bool,       false   )
        )

    explicit CheckBox( Options const& params = Options() )
        : AbstractButton( params )
        , isAuto( params.isAuto() )
        , is3State( params.is3State() )
    {}
};

void show( CheckBox const& cb )
{
    std::cout
        << std::boolalpha
        << "hTextAlign = " << cb.hTextAlign
        << ", isAuto = " << cb.isAuto << ".\n";
}

int main()
{
    typedef CheckBox::Options   CBOptions;

    CheckBox        widget1;
    show( widget1 );                // 0, true (the default values)

    CheckBox        widget2( CBOptions().hTextAlign( 1 ) );
    show( widget2 );                // 1, true

    CheckBox        widget3( CBOptions().hTextAlign( 1 ).isAuto( false ) );
    show( widget3 );                // 1, false
}

上面的代碼使用一些未記錄的Boost魔法來提供C ++ 98可變參數宏。 :-)

還有一個更基本的非Boosted宏集,它消除了Boost依賴性,代價是必須指定每個生成的類中的成員數。

干杯&hth。,

- 阿爾夫

模板無法指定變量名稱。 如果您在編譯時決定在課程中擁有什么,您應該能夠直接在源代碼中指定它。

你可能能夠用一些宏來完成你想要的東西,但我不敢冒險進入那個黑暗的領域。

你可以做類似的事情,但他們不會有不同的名字:

template <class T, int num_t >
MyClass
{
     T var[num_T];
};

這忽略了邊界檢查,但這是另一個故事。

Loki的打字清單。 鏈接文字對我來說相當復雜。 但我認為你想要的就是用這個來完成的。

看看std::tuple

這允許任意(但在編譯時固定)數量的數據元素,以及每個索引的類型安全訪問。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM