原文:http://geek.csdn.net/news/detail/61260
最近准备重构一下我的kapok库,让meta函数可以返回元素为kv的tuple,例如:
struct person
{
std::string name;
int age;
META(name, age) //定义一个支持变参的meta函数
};
int main()
{
person p = {“tom”, 20};
auto tp = p.meta();
static_assert(std::is_same(std::tuple<std::pair<std::string, int>>, decltype(tp), “not same”);
//tp在内存中是这样的 {{“name”:”tom”}, {“age”:20}};
return 0;
}
类似这个META的实现我在msgpack的库里看到了,在这里:
仅仅是宏元的代码就数百行了,看起来非常复杂,msgpack之所以用这么复杂的方式去实现恐怕是为了支持c++98/03标准。本来想看看msgpack是如何实现META函数的,但是它的宏元代码读起来比较困难,于是作罢。
后来想起群里的ddrm实现了类似的功能,据说没有msgpack这么复杂,简洁一些,于是向ddrm要来了代码(在此感谢ddrm分享的源码)。他的代码也是用宏元,但是比msgpack的代码少很多,将近一百行代码。不过,我不太喜欢这么复杂的代码,我希望用一种更简单的方式去实现这个效果。这里附上ddrm的代码,大家可以借鉴参考一下。
#ifndef TUPLE_MACRO_DEF_H
#define TUPLE_MACRO_DEF_H
#define MARCO_EXPAND(arg_list) arg_list
#define APPLY_VARIADIC_MACRO(macro,tuple) macro tuple
#define ADD_REFERENCE(t) std::reference_wrapper<decltype(t)>(t)
#define ADD_REFERENCE_CONST(t) std::reference_wrapper<std::add_const_t<decltype(t)>>(t)
#define PAIR_OBJECT(t) std::make_pair(#t, ADD_REFERENCE(t))
#define PAIR_OBJECT_CONST(t) std::make_pair(#t, ADD_REFERENCE_CONST(t))
#define MAKE_TUPLE(...) auto tuple() { return std::make_tuple(__VA_ARGS__); }
#define MAKE_TUPLE_CONST(...) auto tuple() const { return std::make_tuple(__VA_ARGS__); }
/* arg list expand macro, now support 40 args */
#define MAKE_ARG_LIST_1(op, arg, ...) op(arg)
#define MAKE_ARG_LIST_2(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_1(op, __VA_ARGS__))
#define MAKE_ARG_LIST_3(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_2(op, __VA_ARGS__))
#define MAKE_ARG_LIST_4(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_3(op, __VA_ARGS__))
#define MAKE_ARG_LIST_5(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_4(op, __VA_ARGS__))
#define MAKE_ARG_LIST_6(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_5(op, __VA_ARGS__))
#define MAKE_ARG_LIST_7(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_6(op, __VA_ARGS__))
#define MAKE_ARG_LIST_8(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_7(op, __VA_ARGS__))
#define MAKE_ARG_LIST_9(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_8(op, __VA_ARGS__))
#define MAKE_ARG_LIST_10(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_9(op, __VA_ARGS__))
#define MAKE_ARG_LIST_11(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_10(op, __VA_ARGS__))
#define MAKE_ARG_LIST_12(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_11(op, __VA_ARGS__))
#define MAKE_ARG_LIST_13(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_12(op, __VA_ARGS__))
#define MAKE_ARG_LIST_14(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_13(op, __VA_ARGS__))
#define MAKE_ARG_LIST_15(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_14(op, __VA_ARGS__))
#define MAKE_ARG_LIST_16(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_15(op, __VA_ARGS__))
#define MAKE_ARG_LIST_17(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_16(op, __VA_ARGS__))
#define MAKE_ARG_LIST_18(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_17(op, __VA_ARGS__))
#define MAKE_ARG_LIST_19(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_18(op, __VA_ARGS__))
#define MAKE_ARG_LIST_20(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_19(op, __VA_ARGS__))
#define MAKE_ARG_LIST_21(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_20(op, __VA_ARGS__))
#define MAKE_ARG_LIST_22(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_21(op, __VA_ARGS__))
#define MAKE_ARG_LIST_23(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_22(op, __VA_ARGS__))
#define MAKE_ARG_LIST_24(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_23(op, __VA_ARGS__))
#define MAKE_ARG_LIST_25(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_24(op, __VA_ARGS__))
#define MAKE_ARG_LIST_26(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_25(op, __VA_ARGS__))
#define MAKE_ARG_LIST_27(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_26(op, __VA_ARGS__))
#define MAKE_ARG_LIST_28(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_27(op, __VA_ARGS__))
#define MAKE_ARG_LIST_29(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_28(op, __VA_ARGS__))
#define MAKE_ARG_LIST_30(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_29(op, __VA_ARGS__))
#define MAKE_ARG_LIST_31(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_30(op, __VA_ARGS__))
#define MAKE_ARG_LIST_32(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_31(op, __VA_ARGS__))
#define MAKE_ARG_LIST_33(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_32(op, __VA_ARGS__))
#define MAKE_ARG_LIST_34(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_33(op, __VA_ARGS__))
#define MAKE_ARG_LIST_35(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_34(op, __VA_ARGS__))
#define MAKE_ARG_LIST_36(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_35(op, __VA_ARGS__))
#define MAKE_ARG_LIST_37(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_36(op, __VA_ARGS__))
#define MAKE_ARG_LIST_38(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_37(op, __VA_ARGS__))
#define MAKE_ARG_LIST_39(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_38(op, __VA_ARGS__))
#define MAKE_ARG_LIST_40(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_39(op, __VA_ARGS__))
/* emmbed marco, using EMMBED_TUPLE(5 , a, b, c, d, e) */
//note use MACRO_CONCAT like A##_##B direct may cause marco expand error
#define MACRO_CONCAT(A, B) MACRO_CONCAT1(A, B)
#define MACRO_CONCAT1(A, B) A##_##B
#define MAKE_ARG_LIST(N, op, arg, ...) \
MACRO_CONCAT(MAKE_ARG_LIST, N)(op, arg, __VA_ARGS__)
#define EMMBED_TUPLE(N, ...) \
MAKE_TUPLE(MAKE_ARG_LIST(N, PAIR_OBJECT, __VA_ARGS__)) \
MAKE_TUPLE_CONST(MAKE_ARG_LIST(N, PAIR_OBJECT_CONST, __VA_ARGS__))
// see http://groups.google.com/group/comp.std.c/browse_thread/thread/77ee8c8f92e4a3fb/346fc464319b1ee5
#define RSEQ_N() \
63,62,61,60, \
59,58,57,56,55,54,53,52,51,50, \
49,48,47,46,45,44,43,42,41,40, \
39,38,37,36,35,34,33,32,31,30, \
29,28,27,26,25,24,23,22,21,20, \
19,18,17,16,15,14,13,12,11,10, \
9,8,7,6,5,4,3,2,1,0
#define ARG_N( \
_1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
_11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
_21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
_31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
_41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
_51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
_61,_62,_63,N, ...) N
#define GET_ARG_COUNT(...) APPLY_VARIADIC_MACRO(GET_ARG_COUNT_INNER,(__VA_ARGS__, RSEQ_N()))
#define GET_ARG_COUNT_INNER(...) APPLY_VARIADIC_MACRO(ARG_N, (__VA_ARGS__))
#define EMMBED(...) EMMBED_TUPLE(GET_ARG_COUNT(__VA_ARGS__), __VA_ARGS__)
#endif
10行代码解决问题
我探索了一些可能的方案后,最终找到了一种满意的方案,最终的实现代码不过十来行,非常简洁,感谢c++11/14,正是现代C++才让代码变得如此简洁。
实现思路也比较简单,问题的本质是将输入的参数值变成一个pair,key是值的字面量,value是变量本身的值,所以生成的pair是这样的std::pair< std::string, someval >, 另外这个pair是每一个输入参数值都会生成一个pair,如果是一系列的参数值就要生成一系列的pair,这就要求支持变参。所以问题的关键是要在展开变参的过程中将这些pair放到一个tuple中,这对于c++11/14来说是不难办到的,下面是实现代码:
#define MAKE_PAIR(text) std::pair<std::string, decltype(text)>{#text, text}
template<typename T>
constexpr static inline auto apply(T const & args)
{
return args;
}
template<typename T, typename T1, typename... Args>
constexpr static inline auto apply(T const & t, const T1& first, const Args&... args)
{
return apply(std::tuple_cat(t, std::make_tuple(MAKE_PAIR(first))), args...);
}
#define META(...) auto meta(){ return apply(std::tuple<>(), __VA_ARGS__); }
使用现代C++之后,仅仅需要10行代码就可以实现之前需要上百行甚至数百行代码才能实现的目标,这无疑体现了现代C++的巨大威力。除了非常简洁的优点之外,还解决了一个宏元无法彻底解决的问题,宏元需要预先定义一些参数的宏,这些宏定义是有限的,如果参数超出定义的上限就会编译报错,而这个变参版本完全不用担心这个问题,支持任意个参数。
后记
这件事给了我一个启示,如果我们一直按照前人的路去走,就很难超越前人,如果我们运用新的技术,改变思路,常常会化繁为简,化腐朽为神奇。只有通过新技术、新思维去创造、创新才能超越前人。
相关推荐
本文深入探讨了 C++ 中的 std::tuple 和 std::pair,这两个在标准库中极为重要的模板类型。首先详细介绍了 std::pair 的定义、功能与基本用法,包括其构造、访问成员元素的多种方式以及在简单数据组合场景下的应用...
在C++11标准中,`std::tuple`是一个强大的工具,它允许我们将不同类型的值组合成一个单一的可管理的实体。以下是对`std::tuple`使用方法的详细解释: 1. **引入头文件**:为了使用`std::tuple`,你需要包含`<tuple>...
3. 模板元编程:std::tuple可以与模板元编程结合使用,例如实现一个通用的数据结构。 std::tuple的优点 std::tuple的优点包括: 1. 高效:std::tuple的存储和访问都是高效的。 2.灵活:std::tuple可以存储多种...
std::tuple是C++ 11中引入的一个非常有用的结构,以前我们要返回一个包含不同数据类型的返回值,一般都需要自定义一个结构体或者通过函数的参数来返回,现在std::tuple就可以帮我们搞定。 1.引用头文件 #include ...
在C++标准库STL中,`std::bind`、`std::tuple`和`std::function`是非常重要的工具,它们提供了高级的函数操作和参数管理能力。下面将详细解释这三个概念及其简单实现的关键点。 首先,我们来看`std::function`。`...
注意,`std::tuple_size_v`是一个C++17的特性,但在C++11中可以用`std::tuple_size<Tuple>::value`代替。 此外,`std::forward`被用于将`tuple`参数以右值引用的方式传递给递归调用,以确保在传递过程中保持原始的...
在C++中,`std::tuple`是从C++11标准开始引入的,而Boost库在此之前就提供了`boost::tuple`,它具有许多实用的特性,如元组元素的访问、解构、比较和操作等。 要实现一个类似的`Tuple`,我们需要考虑以下几个关键点...
- `std::tuple`自C++11起提供,可以存储任意数量和类型的元素。 - `std::function`可以表示任何可调用对象,如函数指针、成员函数、lambda表达式等。 在"LibraryFunctions.chm"这个帮助文件中,你将找到关于这些...
可以使用 std::tuple 和 std::get 来实现: ```cpp template<std::size_t I, typename... Args> inline void call_tuple(std::tuple<Args...>& t) { std::get(t)(std::get(t), std::get(t), ...); } ``` C++11 ...
在现代C++编程中,`tuple`是一种强大的数据结构,它可以容纳不同类型的数据,并提供了一种紧凑的方式来存储和操作异构数据集。`tuple`的概念源自数学中的元组,它允许我们在一个单一的实体中组合不同类型的值。在C++...
5. 增强的`std::tuple`:在C++14中,`std::tuple`可以与`std::tuple_element`和`std::make_tuple`一起使用,以更简洁的方式访问和构造元组元素,提高了代码的可读性和可维护性。 6. 新的`std::array`和`std::vector...
在C++17之前,访问`std::pair`或`std::tuple`中的元素通常需要显式地使用`.first`和`.second`(对于`std::pair`)或`std::get`(对于`std::tuple`)。例如,有一个`divide_remainder`函数返回一个`std::pair`,包含...
C++11中的`std::tuple`为程序员提供了一种强大且灵活的方式来处理不同类型的数据集合。它不仅支持基本的元组操作,还允许进行元组之间的隐式类型转换。然而,这种转换有时可能会带来不可预见的问题,因此在实际开发...
在C++标准库中,`std::tuple` 的实现基于模板元编程,特别是可变参数模板。元组的模板定义如下: ```cpp template class tuple; ``` 这意味着`std::tuple` 可以接受零个或多个类型的参数。例如,`tuple<>` 表示空...
C++ Tuple元组是一种固定大小的不同类型值的集合,是泛化的std::pair。我们也可以把它当做一个通用的结构体来用,不需要创建结构体又获取结构体的特征,在某些情况下可以取代结构体使程序更简洁、直观。 一、Tuple...
增强PFR 这是一个C ++ 14库,用于非常基本的反射,使您可以按索引访问结构元素,并为用户定义的类型提供其他std::tuple类的方法,而无需任何宏或样板代码。检测结果分行建立测试覆盖率更多信息开发: 主: 激励实例...
Kaguya是C++的一个lua绑定库,它使得C++程序可以方便地集成lua代码,实现两者之间的交互。在这个“C++ lua Kaguya应用”主题中,我们将深入探讨如何利用Kaguya在C++项目中有效地使用lua脚本。 首先,我们需要理解...
tuple_utility, 缺少的C++ tuple 功能 tuple_utilityC++ 元组的实用工具。tuple_map:#include"tuple_utility.hpp"int main(){ auto t = std::make_tuple(0, 1
总结起来,C++11引入的`std::tuple`为C++开发者提供了一种强大的工具,用于存储和操作异构数据。元组的灵活性、可比性以及与模板元编程的结合,使得它在各种场景下都能发挥重要作用,包括但不限于函数返回值、数据...