[英]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.