C++ C++;11标记元组
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
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;
}