我想创建一个具有可变数量的类成员的结构/类,可以在编译阶段决定(就像在模板元编程中完成)
示例:假设其中类型和变量名称都要指定为类型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 ++中的模板元编程实现类型和变量名称规范?
答案 0 :(得分:5)
使用模板元编程和少量预处理,可以实现接近理想的语法:
//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
{ };
答案 1 :(得分:3)
不可能如上所述。使用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或〜如果你可以确保每个字段都有一个不同的类型(再次,这可以通过简单的包装器模板类强制),那么还有另一个选项:我称之为“类型映射” - 你可以在这里使用类型键入有效的类型 - 不同值的关联容器,所有查找在编译时解析,并支持somefunc()实现所需的自动迭代。如果需要,可以将它与字符串组合用于运行时类型命名,但无法实现在编译时解析或验证的标识符字符串。
我在6年前(使用类型列表在Alexandrescu的Loki图书馆上面)实施了这样的程序,并在增强邮件列表上询问是否有人感兴趣,但没有人看到它的实用性,我并没有真正尝试解释。它实际上对日志系统非常有用,这促使我首先编写它。无论如何,我怀疑我没有费心将代码发布到该库的保险库,并且没有方便,所以你需要从头开始,除非MPL或其他一些库实现了他们自己的类似“容器”同时(或事先......?)。
答案 2 :(得分:2)
您不能使用模板,仅使用类型或某些类型的值来指定名称。您可以使用宏来完成它,但是在语言中尝试做太多是我陷入了太多次的陷阱......还有另一条路可能对您有用:代码生成
考虑编写一个脚本来读取某些配置并吐出类的定义。将脚本添加到构建过程中。这可能比模板元编程或宏观技术的黑色艺术更容易维护和理解。
python是我用于脚本的内容,每个类的配置都像json一样容易解析 - 但这些是副作用
在我目前的项目中,我们有数千行生成的文件,分布在100多个文件中......并且这些生成脚本会相对定期地进行修改,而且非常轻松。
答案 3 :(得分:1)
我记得Andrei Alexandrescu在他的书“Modern C ++”中描述了类似的东西。我没有这里的副本,所以我无法准确说出它是什么以及它在哪里。
正如其他人所指出的那样,不可能将名称作为模板参数,但是他创建了一个可以像data.get<T1>()
那样访问的结构。如果一种类型的数据不止一个,您可以data.get<T1,2>()
。
也许这有帮助。
答案 4 :(得分:1)
我发现问题没有明确,目前还不清楚目的是什么。
对于序列化,我会考虑the Boost library's serialization support。
对于命名的,严格类型化的可选参数,一种可能性是使用Boost parameters library,第二种更简单的可选参数是my own options pack support。它本质上是一组宏,通过一些不可穿透的内部模板黑魔法,生成像你要求的类。我在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依赖性,代价是必须指定每个生成的类中的成员数。
干杯&amp;第h。,
- Alf
答案 5 :(得分:0)
模板无法指定变量名称。如果你在编译时决定在课程中有什么,你应该能够直接在源代码中指定它。
你可以用一些宏来完成你想要的东西,但我不敢冒险进入那个黑暗的领域。
答案 6 :(得分:0)
你可以做类似的事情,但他们没有不同的名字:
template <class T, int num_t >
MyClass
{
T var[num_T];
};
这忽略了边界检查,但这是另一个故事。
答案 7 :(得分:0)
Loki的打字清单。 link text 对我来说相当复杂。但我认为你想要的就是用这个来完成的。
答案 8 :(得分:0)
看看std::tuple
。
这允许任意(但在编译时固定)数量的数据元素,以及每个索引的类型安全访问。