2010-10-01 45 views
3

我想创建一个结构/类与类成员的可变数量的其可在编译阶段(如在模板元编程完成)结构或类带构件

实施例来决定的可变数目:其在假想这两种类型和变量名都像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++中有两种类型和变量名称说明访问变量名 ?

回答

4

随着模板元编程和小预处理,可以达到接近理想的语法:

//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 
{ }; 
+0

@Pardeep:解决方案改进:不需要下划线前缀。现在,我喜欢它的外观) – Alsk 2010-10-11 08:29:54

+1

omg ...我们可以很容易地将方法和变量的用户指定名称注入库和框架的代码中,而不仅仅是类型。 Voila由像BinaryTree 这样的领域细节的抽象结构组合(用法将会非常自我记录)。 – Alsk 2010-10-11 08:54:34

0

模板不能指定变量名称。如果您在编译时决定该课程中的内容,则应该能够直接在源代码中指定它。

你或许能用某些宏完成你想要的,但我不敢冒险进入那个黑暗的领域。

2

您不能使用模板指定名称,只能使用类型或某种类型的值。您可以使用宏来做到这一点,但是在语言中尝试做太多是陷阱,我陷入了太多的困境..还有另一条路可能适用于您:代码生成

考虑编写一个脚本来读取一些配置文件并吐出你的类的定义。将脚本添加到您的构建过程。这可能比模板元编程或宏观欺骗的黑色艺术更易于维护和理解。

蟒蛇是我想的东西容易解析像JSON使用的脚本,每类配置 - 但这些都是枝节问题

在我目前的项目,我们有成千上万的生成文件的行,蔓延跨越100多个文件......并且这些生成脚本经常被修改,并且无痛。

1

我记得Andrei Alexandrescu在他的书“Modern C++”中描述了类似的东西。我在这里没有一个副本,所以我不能确切地说出它是什么以及它在哪里。

正如其他人已经指出,不可能有名称作为模板参数,但他创建了一个可以像data.get<T1>()或类似的东西访问的结构。如果有一种以上的数据可以做data.get<T1,2>()

也许这有帮助。

0

你可以做类似的事情,但他们不会有不同的名称:

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

此俯瞰边界检查,但这是另一个故事。

+0

那是好的,但类型可以是不同的,通过不同的名字来访问是必须的,我:) – Pardeep 2010-10-01 06:42:21

3

不可能如上所述。使用boost的预处理器库可能会获得相同的功能。

你要求不同于简单地说传递什么最终...

struct Members 
{ 
    int a_; 
    double b_; 
}; 

... INTO ...

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

...在DoSomething的是给予的能力迭代并打印成员,对吧?

你也可以编写一个简单的程序/脚本来读取类型和标识符列表并输出你需要的C++代码。如果你有很多领域需要处理,这可能是一个好方法。作为最小的现成的,顶级我的头轮廓,并假设输入像这样的新行执行一个简单的类型VS标识符师(让你创建数组等类型定义):

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图书馆使用类型列表上面),并在提交邮件列表中询问是否有人感兴趣,但没有人看到它的实用性,我没有真正解释。对于日志记录系统来说,这实际上非常有用,这促使我将它写在第一位。无论如何,我怀疑我没有打算将代码发布到Vault的代码中,也没有它的方便,所以你需要从头开始,除非MPL或其他库已经实现了自己的类似“容器”同时(或事先...?)。

+0

是的,你是对的。我也曾想过类似的结构派生结构,并且用不同的成员变量名称生成该结构不是点击。这些类型可以不同,通过名称访问变量是我实现中最需要的部分。 – Pardeep 2010-10-01 06:39:12

+0

@Pardeep:我明白了。你可能想查看boost序列化库,然后 - 我认为它有预处理宏来捕获有关成员的额外元数据,当你实现你自己的函数ala doSomething()时,你可能会用它来迭代它们。 – 2010-10-01 06:46:39

0

Loki的类型列表。 link text 对我来说非常复杂。但我认为你可以用这个来完成你想要的。

0

看一看std::tuple

这允许任意(但固定在编译时)数目的数据元素,并且每个索引具有类型安全的访问权限。

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 
} 

上面的代码使用了一些无证提升魔力以提供C++ 98可变宏。 :-)

还有一个更基本的未增压组宏其去除升压依赖性,在不必指定在每个生成的类成员的数量的成本。 。

干杯&心连心,

- 阿尔夫