C++ C++;11标记元组

C++ C++;11标记元组,c++,c++11,tuples,C++,C++11,Tuples,C++11元组很好,但它们对我来说有两个巨大的缺点,通过索引访问成员是非常困难的 不可读 难以维护(如果我在元组的中间添加元素,我被拧了) 本质上,我想要实现的是这一点 tagged_tuple <name, std::string, age, int, email, std::string> get_record (); {/*...*/} // And then soomewhere else std::cout << "Age: " << get_re

C++11元组很好,但它们对我来说有两个巨大的缺点,通过索引访问成员是非常困难的

  • 不可读
  • 难以维护(如果我在元组的中间添加元素,我被拧了) 本质上,我想要实现的是这一点

    tagged_tuple <name, std::string, age, int, email, std::string> get_record (); {/*...*/}
    // And then soomewhere else
    
    std::cout << "Age: " << get_record().get <age> () << std::endl;
    
    taged_tuple get_record();{/*...*/}
    //然后我去哪里了
    
    std::coutC++没有可以像
    元组那样迭代的
    struct
    类型;这是非此即彼

    最接近这一点的是通过助推,聚变。这允许您将结构用作融合序列。当然,这也使用了一系列宏,它要求您按照要迭代的顺序显式列出结构的成员。在标题中(假设您希望在多个转换单元中迭代结构)

    实际上,我的示例可能有点不现实。这个怎么样

    tagged_tuple <tag<name, std::string>, tag<age, int>, tag<email, std::string>> get_record (); {/*...*/}
    // And then somewhere else
    
    std::cout << "Age: " << get_record().get <age> () << std::endl;
    

    您可以实现类似的东西,但这些标识符实际上需要是类型、变量或其他东西。

    我不知道有任何现有的类可以实现这一点,但使用
    std::tuple
    和索引类型列表可以相当容易地将一些东西组合在一起:

    #include <tuple>
    #include <iostream>
    
    template<typename... Ts> struct typelist {
      template<typename T> using prepend = typelist<T, Ts...>;
    };
    
    template<typename T, typename... Ts> struct index;
    template<typename T, typename... Ts> struct index<T, T, Ts...>:
      std::integral_constant<int, 0> {};
    template<typename T, typename U, typename... Ts> struct index<T, U, Ts...>:
      std::integral_constant<int, index<T, Ts...>::value + 1> {};
    
    template<int n, typename... Ts> struct nth_impl;
    template<typename T, typename... Ts> struct nth_impl<0, T, Ts...> {
      using type = T; };
    template<int n, typename T, typename... Ts> struct nth_impl<n, T, Ts...> {
      using type = typename nth_impl<n - 1, Ts...>::type; };
    template<int n, typename... Ts> using nth = typename nth_impl<n, Ts...>::type;
    
    template<int n, int m, typename... Ts> struct extract_impl;
    template<int n, int m, typename T, typename... Ts>
    struct extract_impl<n, m, T, Ts...>: extract_impl<n, m - 1, Ts...> {};
    template<int n, typename T, typename... Ts>
    struct extract_impl<n, 0, T, Ts...> { using types = typename
      extract_impl<n, n - 1, Ts...>::types::template prepend<T>; };
    template<int n, int m> struct extract_impl<n, m> {
      using types = typelist<>; };
    template<int n, int m, typename... Ts> using extract = typename
      extract_impl<n, m, Ts...>::types;
    
    template<typename S, typename T> struct tt_impl;
    template<typename... Ss, typename... Ts>
    struct tt_impl<typelist<Ss...>, typelist<Ts...>>:
      public std::tuple<Ts...> {
      template<typename... Args> tt_impl(Args &&...args):
        std::tuple<Ts...>(std::forward<Args>(args)...) {}
      template<typename S> nth<index<S, Ss...>::value, Ts...> get() {
        return std::get<index<S, Ss...>::value>(*this); }
    };
    template<typename... Ts> struct tagged_tuple:
      tt_impl<extract<2, 0, Ts...>, extract<2, 1, Ts...>> {
      template<typename... Args> tagged_tuple(Args &&...args):
        tt_impl<extract<2, 0, Ts...>, extract<2, 1, Ts...>>(
          std::forward<Args>(args)...) {}
    };
    
    struct name {};
    struct age {};
    struct email {};
    
    tagged_tuple<name, std::string, age, int, email, std::string> get_record() {
      return { "Bob", 32, "bob@bob.bob"};
    }
    
    int main() {
      std::cout << "Age: " << get_record().get<age>() << std::endl;
    }
    
    #包括
    #包括
    模板结构类型列表{
    使用prepend=类型列表的模板;
    };
    模板结构索引;
    模板结构索引:
    积分常数{};
    模板结构索引:
    积分常数{};
    模板结构n_impl;
    模板结构n\u impl{
    使用type=T;};
    模板结构n\u impl{
    使用type=typename n_impl::type;};
    使用nth=typename nth\u impl::type的模板;
    模板结构提取\u impl;
    模板
    结构extract_impl:extract_impl{};
    模板
    struct extract_impl{using types=typename
    extract_impl::types::template prepend;};
    模板结构提取\u impl{
    使用类型=类型列表;};
    使用extract=typename的模板
    提取_impl::类型;
    模板结构tt_impl;
    模板
    结构tt_impl:
    公共std::tuple{
    模板tt_impl(参数&&…参数):
    std::tuple(std::forward(args)…{}
    模板n获取(){
    return std::get(*this);}
    };
    模板结构标记的\u元组:
    tt_impl{
    模板标记的参数(Args&&…Args):
    tt_impl(
    标准::转发(args)…{}
    };
    结构名称{};
    结构年龄{};
    结构电子邮件{};
    标记的\u元组获取\u记录(){
    返回{“Bob”,32,“bob@bob.bob"};
    }
    int main(){
    std::cout我使用boost预处理器实现了“c++命名的tuple”。请参见下面的示例用法。通过从tuple派生,我可以免费获得比较、打印、哈希和序列化(假设它们是为tuple定义的)

    #包括
    #包括
    #定义CM_命名的_TUPLE_ELEMS_ITR(r,xxx,index,x)BOOST_PP_逗号_IF(index)BOOST_PP_TUPLE_ELEM(2,0,x)
    #为每个元素定义CM命名元素(seq)BOOST PP seq(CM命名元素“dum”,seq)
    #定义命名为元组的CM属性ITR(r,xxx,索引,x)\
    BOOST_PP_TUPLE_ELEM(2,0,x)BOOST_PP_CAT(get,BOOST_PP_TUPLE_ELEM(2,1,x))()常数{return get(*this);}\
    void BOOST_PP_CAT(set_,BOOST_PP_TUPLE_ELEM(2,1,x))(const BOOST_PP_TUPLE_ELEM(2,0,x)&oo){get(*this)=oo;}
    #为每个属性定义CM命名属性(顺序)提升属性(顺序)(CM命名属性“dum”,顺序)
    #定义cm_命名元素(Cls,seq)结构Cls:tuple{\
    typedef元组基\
    Cls(){}\
    模板Cls(Args&&…Args):基(Args…{}\
    结构哈希:std::hash{}\
    CM_命名_元组_道具(序号)\
    模板无效序列化(存档&ar,arg const unsigned int version)({\
    ar&boost::serialization::base_对象(*this)\
    }                                                                   \
    }
    //
    //例如:
    //
    //类样本{
    //公众:
    //void do_tata(){
    //用于(自动&dd:bar2_2;){
    
    //你必须解决的真正问题是:

    • 标签是强制性的还是可选的
    • 标记是否唯一?是否在编译时强制执行
    • 标记驻留在哪个范围内?您的示例似乎在声明范围内声明标记,而不是封装在类型中,这可能不是最优的
    提出了一个很好的解决方案;但是标签没有封装,而且标签声明有点笨拙。C++14将引入,这将简化他的设计并保证标签的唯一性,但不能解决它们的范围

    也可以用于类似的东西,但再次声明标记并不理想

    上有一个类似的建议,通过将名称直接关联到模板参数来简化语法

    列出了实现此功能的不同方法(包括的解决方案),并给出了此语法的不同用例。

    我已经“解决”了生产代码中的一个类似问题。首先,我有一个普通的结构(实际上是一个具有各种成员函数的类,但这里我们感兴趣的只是数据成员)

    然后在结构定义的正下方,但在任何名称空间之外,我有另一个宏:

    MYLIB_DECLARE_TUPLE(Record, (o.name, o.age, o.email))
    
    这种方法的缺点是必须列出两次成员名,但这是我所能想到的最好方法,同时仍然允许在结构自身的成员函数中使用传统的成员访问语法。宏看起来非常接近数据成员本身的定义,因此保留它们并不难彼此同步

    在另一个头文件中,我有一个类模板:

    template <class T>
    class TupleConverter;
    
    #define MYLIB_DECLARE_TUPLE(TYPE, MEMBERS) \
        template <>                            \
        class TupleConverter<TYPE>             \
        {                                      \
            friend class TYPE;                 \
            static auto toTuple(TYPE& o)       \
                -> decltype(std::tie MEMBERS)  \
            {                                  \
                return std::tie MEMBERS;       \
            }                                  \
        public:                                \
            static auto toTuple(TYPE const& o) \
                -> decltype(std::tie MEMBERS)  \
            {                                  \
                return std::tie MEMBERS;       \
            }                                  \
        };
    
    定义第二个宏是为了引入模板的专门化:

    template <class T>
    class TupleConverter;
    
    #define MYLIB_DECLARE_TUPLE(TYPE, MEMBERS) \
        template <>                            \
        class TupleConverter<TYPE>             \
        {                                      \
            friend class TYPE;                 \
            static auto toTuple(TYPE& o)       \
                -> decltype(std::tie MEMBERS)  \
            {                                  \
                return std::tie MEMBERS;       \
            }                                  \
        public:                                \
            static auto toTuple(TYPE const& o) \
                -> decltype(std::tie MEMBERS)  \
            {                                  \
                return std::tie MEMBERS;       \
            }                                  \
        };
    
    有很多方法可以扩展它,例如通过在
    TupleConverter
    中添加另一个成员函数,该函数返回一个
    std::vector#define MYLIB_DECLARE_TUPLE(TYPE, MEMBERS) \
        template <>                            \
        class TupleConverter<TYPE>             \
        {                                      \
            friend class TYPE;                 \
            static auto toTuple(TYPE& o)       \
                -> decltype(std::tie MEMBERS)  \
            {                                  \
                return std::tie MEMBERS;       \
            }                                  \
        public:                                \
            static auto toTuple(TYPE const& o) \
                -> decltype(std::tie MEMBERS)  \
            {                                  \
                return std::tie MEMBERS;       \
            }                                  \
        };
    
    // lexicographical comparison
    bool operator< (Record const& a, Record const& b)
    {
        return TupleConverter<Record>::toTuple(a) < TupleConverter<Record>::toTuple(b);
    }
    
    // serialization
    std::ostream& operator<< (std::ostream& os, Record const& r)
    {
        // requires template<class... Ts> ostream& operator<<(ostream&, tuple<Ts...>) defined elsewhere
        return os << TupleConverter<Record>::toTuple(r);
    }
    
    #include <named_tuples/tuple.hpp>
    #include <string>
    #include <iostream>
    #include <vector>
    
    namespace {
    unsigned constexpr operator "" _h(const char* c,size_t) { return named_tuples::const_hash(c); }
    template <unsigned Id> using at = named_tuples::attribute_init_int_placeholder<Id>;
    using named_tuples::make_tuple;
    }
    
    int main() {
      auto test = make_tuple( 
          at<"nom"_h>() = std::string("Roger")
          , at<"age"_h>() = 47
          , at<"taille"_h>() = 1.92
          , at<"liste"_h>() = std::vector<int>({1,2,3})
          );
    
      std::cout 
        << test.at<"nom"_h>() << "\n"
        << test.at<"age"_h>() << "\n"
        << test.at<"taille"_h>() << "\n"
        << test.at<"liste"_h>().size() << std::endl;
    
      test.at<"nom"_h>() = "Marcel";
      ++test.get<1>();
    
      std::cout 
        << test.get<0>() << "\n"
        << test.get<1>() << "\n"
        << test.get<2>() << "\n"
        << test.get<3>().size() << std::endl;
    
      return 0;
    }
    
       using user_t = tagged_tuple<type_pair<struct name, std::string>, type_pair<struct age, int>>;
      // it's initialized the same way as a tuple created with the value types of the type pairs (so tuple<string, int> in this case)
      user_t user  { "chris", 21 };
      std::cout << "Name: " << get<name>(user) << std::endl;
      std::cout << "Age: " << get<age>(user) << std::endl;
     // you can still access properties via numeric indexes as if the class was defined as tuple<string, int>
      std::cout << "user[0] = " << get<0>(user) << std::endl;
    
    #include <iostream>
    #include <brigand/brigand.hpp>
    
    template<typename Members>
    class TaggedTuple{
    
        template<typename Type>
        struct createMember{
            using type = typename Type::second_type;
        };
    
        using DataTuple = brigand::transform<Members, createMember<brigand::_1>>;
        using Keys = brigand::keys_as_sequence<Members, brigand::list>;
        brigand::as_tuple<DataTuple> members;
    
    public:
    
        template<typename TagType>
        auto& get(){
            using index = brigand::index_of<Keys, TagType>;
            return std::get<index::value>(members);
        }
    };
    
    int main(){
    
        struct FloatTag{};
        struct IntTag{};
        struct DoubleTag{};
    
        TaggedTuple<brigand::map<
                brigand::pair<FloatTag, float>,
                brigand::pair<IntTag, int>,
                brigand::pair<DoubleTag, double>>> tagged;
    
        tagged.get<DoubleTag>() = 200;
        auto val = tagged.get<DoubleTag>();
        std::cout << val << std::endl;
    
        return 0;
    }