代码之家  ›  专栏  ›  技术社区  ›  Pardeep

成员数目可变的结构或类

  •  4
  • Pardeep  · 技术社区  · 14 年前

    我想创建一个结构/类,其中包含可变数量的类成员,这些成员可以在编译阶段决定(就像在模板元编程中那样)

    例如:假设类型和变量名都要指定,比如类型T1变量名应该是varName1,依此类推。。。。。

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

    在主代码中,可以像下面这样声明,或者以其他方式指定类型和名称

    MyClass对象;

    MyClass::somefunc()可以访问变量名 如下

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

    这是否可能通过C++中的模板元编程来同时拥有类型和变量名规范?

    9 回复  |  直到 14 年前
        1
  •  5
  •   Alsk    14 年前

    通过模板元编程和少量预处理,可以实现接近理想的语法:

    //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
    { };
    
        2
  •  3
  •   Tony Delroy    14 年前

    最终你所要求的不同于简单的说。。。

    struct Members
    {
        int a_;
        double b_;
    };
    

    …变成。。。

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

    …在这种情况下,doSomething被赋予了迭代和打印成员的能力,对吗?

    您还可以编写一个简单的程序/脚本,读取类型和标识符列表并输出所需的C++代码。如果你有很多领域要处理,这可能是一个很好的方法。作为我脑海中最简单的一个轮廓,假设输入是这样的,换行符强制执行简单的类型与标识符划分(使您为数组等创建typedef):

    std::string
    idn1
    const int*
    idn2
    my_typedef
    ind3
    

    ……你可以生成一些C++代码ala…

    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";
    

    如果您实际上不需要通过 ,如果每个字段都可以具有相同的类型(还不错-考虑boost::variant或~::any),或者如果可以确保每个字段都具有不同的类型(再次,这可以通过琐碎的包装器模板类来实现:我称之为“类型映射”(typemap)——在这里,您可以使用类型作为键,进入一个有效的类型不同值的关联容器,在编译时解析所有查找,并支持somefunc()实现所需的自动迭代。如果需要,可以将其与用于运行时类型命名的字符串相结合,但无法实现在编译时解析或验证的标识符字符串。

        3
  •  2
  •   Michael Anderson    14 年前

    不能使用模板指定名称,只能使用类型或某些类型的值。你也许可以用宏来做,但是在语言中尝试做太多的事情是一个陷阱,我已经掉进了太多次。。还有一条路可能对你有用: 代码生成

    python是我在脚本中使用的,每个类的配置都是一些易于解析的东西,比如json,但这些都是次要的问题

        4
  •  1
  •   Cheers and hth. - Alf    14 年前

    我发现这个问题没有具体说明,不清楚目的是什么。

    我会考虑 the Boost library's serialization support .

    Boost parameters library 第二个更简单的用法是 my own options pack support

    #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
    }
    

    还有一个更基本的未增强宏集,它删除了增强依赖项,代价是必须指定每个生成类中的成员数。

    阿尔夫

        5
  •  1
  •   Philipp    14 年前

    我记得Andrei Alexandrescu在他的书《现代C++》中描述了类似的东西。我这里没有副本,所以我不知道它到底是什么,在哪里。

    data.get<T1>() data.get<T1,2>() .

        6
  •  0
  •   JoshD    14 年前

    模板不能指定变量名。如果您在编译时决定类中包含什么,您应该能够直接在源代码中指定它。

        7
  •  0
  •   SingleNegationElimination    14 年前

    您可以执行类似的操作,但它们不会有不同的名称:

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

    这忽略了边界检查,但那是另一回事。

        8
  •  0
  •   Manoj R    14 年前

    洛基的打字清单。 link text 对我来说很复杂。但我认为你想要的可以用这个来实现。

        9
  •  0
  •   sbi    14 年前

    std::tuple

    这允许任意数量的数据元素(但在编译时是固定的),并允许对每个索引进行类型安全访问。