本文已参与「新人创作礼」活动.一起开启掘金创作之路。
首先最近比较火的元宇宙,我谈谈个人观点:元宇宙的实现,只能是系统级的语言。举例就谈下c++的元组tuple,因为元组可用作反射0开销的最佳方式,所以构建元宇宙必少不了tuple元组。
元组的用途
- 实现类型列表
- 给结构体属性加索引以便拥有下标进行赋值
- 解决了无法动态获取结构体属性(以往就只能靠点属性,但很难知道属性名)
- 可以静态过滤结构体中的某些类型,实现类型过滤
- 允许静态编译期萃取结构体的某个类型(一般不用于基本类型)
- 结合类型萃取和模板技术,可以编译期枚举所有可能的类型情况
元组的本质
- 实际上std::tuple与结构体或者class有着一定的联系
- 拿class举例,class T; 假设T的第一属性是int类型的id,那么&T::id就类似于一个属于T的指针
- 如果T t; 那么 t->*&T::id 就等同于 t.id 也等同于 t->*std::get<0>(std::tuple<int T::*>)
- 结合reinterpret_cast可以做强转,在不知道属性名的情况下拿到offset偏移量来直接赋值
- *((char*)(reinterpret_cast<char*>(t)+(size_t)(&reinterpret_cast<char const volatile&>(((T*)0)->*std::get<0>(std::tuple<int T::*>)))))也就是隐式拿到了t.id【这里具体例子请在最后链接看】
//首先编译期判断类型是否正确
constexpr int count_first_falses() { return 0; }
template <typename... B> constexpr int count_first_falses(bool b1, B... b) {
if (b1) return 0; else return 1 + count_first_falses(b...);
}
我们采用count_first_falses在编译期判断B参数包到底是不是我们想要的类型,返回一个索引,以便于std::get去拿到,然后可以得到一个对象索引下的属性。再实现type_get函数,来进行编译期元组的萃取指定类型的值,返回的是该类型的值
//实现左值和右值两个版本的type_get函数
template <typename E, typename... T> decltype(auto) type_get(std::tuple<T...>& tuple) {
typedef std::decay_t<E> DE;
return std::get<count_first_falses((std::is_same<std::decay_t<T>, DE>::value)...)>(tuple);
}
template <typename E, typename... T> decltype(auto) type_get(std::tuple<T...>&& tuple) {
typedef std::decay_t<E> DE;
return std::get<count_first_falses((std::is_same<std::decay_t<T>, DE>::value)...)>(tuple);
}
tuple_filter可以过滤掉元组中的某一种类型的全部,返回的是一个元组
//得先实现tuple_filter_sequence
template <template <class> class F, typename T, typename I, typename R, typename X = void>
struct tuple_filter_sequence;
template <template <class> class F, typename... T, typename R>
struct tuple_filter_sequence<F, std::tuple<T...>, std::index_sequence<>, R> { using ret = R; };
template <template <class> class F, typename T1, typename... T, size_t I1, size_t... I, size_t... R>
struct tuple_filter_sequence<F, std::tuple<T1, T...>, std::index_sequence<I1, I...>,
std::index_sequence<R...>, std::enable_if_t<F<T1>::value>> {
using ret = typename tuple_filter_sequence<F, std::tuple<T...>, std::index_sequence<I...>,
std::index_sequence<R..., I1>>::ret;
};
template <template <class> class F, typename T1, typename... T, size_t I1, size_t... I, size_t... R>
struct tuple_filter_sequence<F, std::tuple<T1, T...>, std::index_sequence<I1, I...>,
std::index_sequence<R...>, std::enable_if_t<!F<T1>::value>> {
using ret = typename tuple_filter_sequence<F, std::tuple<T...>, std::index_sequence<I...>,
std::index_sequence<R...>>::ret;
};
template <typename T> constexpr inline auto Tuple() { return std::make_tuple(); }
template <std::size_t... I, typename T>
decltype(auto) Tuple(std::index_sequence<I...>, T&& t) { return std::make_tuple(std::get<I>(t)...); }
template <typename F, typename... M> constexpr auto tuple_filter(const std::tuple<M...>& m) {
auto fun = [](auto... e) { return Tuple<F>(e...); }; return std::apply(fun, m);
}
template <template <class> class F, typename T> decltype(auto) tuple_filter(T&& t) {
using seq = typename tuple_filter_sequence<
F, std::decay_t<T>, std::make_index_sequence<std::tuple_size<std::decay_t<T>>::value>,
std::index_sequence<>>::ret; return Tuple(seq{});
}
tuple_remove_elements可以把元组中的某些类型移除,返回的是元组
template <typename T, typename U, typename E> struct tuple_remove_element2;
template <typename... T, typename... U, typename E1>
struct tuple_remove_element2<std::tuple<E1, T...>, std::tuple<U...>, E1>
: public tuple_remove_element2<std::tuple<T...>, std::tuple<U...>, E1> {};
template <typename... T, typename... U, typename T1, typename E1>
struct tuple_remove_element2<std::tuple<T1, T...>, std::tuple<U...>, E1>
: public tuple_remove_element2<std::tuple<T...>, std::tuple<U..., T1>, E1> {};
template <typename... U, typename E1>
struct tuple_remove_element2<std::tuple<>, std::tuple<U...>, E1> { typedef std::tuple<U...> type; };
template <typename T, typename E>
struct tuple_remove_element : public tuple_remove_element2<T, std::tuple<>, E> {};
template <typename T, typename... E> struct tuple_remove_elements;
template <typename... T, typename E1, typename... E>
struct tuple_remove_elements<std::tuple<T...>, E1, E...> {
typedef typename tuple_remove_elements<typename tuple_remove_element<std::tuple<T...>, E1>::type,
E...>::type type;
};
template <typename... T> struct tuple_remove_elements<std::tuple<T...>> {
typedef std::tuple<T...> type;
};
tuple_minus获取两个元组的差集,裁剪掉元组一当中所有包含在元组二的类型,返回的是元组
template <typename A, typename B> struct tuple_minus;
template <typename... T, typename... R> struct tuple_minus<std::tuple<T...>, std::tuple<R...>> {
typedef typename tuple_remove_elements<std::tuple<T...>, R...>::type type;
};
tuple_element返回的是指定位置的编译期元组类型
template< std::size_t I, class T > struct tuple_element;
template< std::size_t I, class Head, class... Tail >
struct tuple_element<I, std::tuple<Head, Tail...>> : tuple_element<I - 1, std::tuple<Tail...>> {};
template< class Head, class... Tail >
struct tuple_element<0, std::tuple<Head, Tail...>> { typedef Head type; };
tuple_index包装tuple_element后更容易获取指定位置的编译期类型,返回的是一个类型
template<typename C> struct tuple_idex {};
template<template<typename ...T> class C, typename ...T>
struct tuple_idex<C<T...>> { template<size_t i> using type = typename tuple_element<i, std::tuple<T...> >::type; };
type_list来拿到类型列表
template <class... T>
struct type_list { template <std::size_t N> using type = typename tuple_element<N, std::tuple<T...>>::type; };
演示一下,我们呢,就可以玩转c++元组,以及反射了. 如果想知道更多,请在最下面打开连接
//上面的函数打包到一个a.h文件
#include <iostream>
#include <vector>
#include <tuple>
#include <any>
#include <cassert>
#include "a.h"
template<class T, std::size_t N> struct Get {
static void show(const T& t) { Get<T, N - 1>::show(t); std::cout << ", "; std::cout << std::get<N>(t); }
};
template<class T> struct Get<T, 0> {
static void show(const T& t) { std::cout << std::get<0>(t); }
};
template<class... A> void show(std::tuple<A...>& t) {
std::cout << "("; Get<std::tuple<A...>, sizeof...(A) - 1>::show(t); std::cout << ")\n";
}
struct My {
int a;
bool b;
float f;
friend std::ostream& operator<<(std::ostream& o, My& c) {
return o << '{' << c.a << ',' << c.b << ',' << c.f << '}';
}
friend std::ostream& operator<<(std::ostream& o, const My& c) {
return o << '{' << c.a << ',' << c.b << ',' << c.f << '}';
}
} my;
int main() {
std::cout << std::boolalpha;
typedef std::tuple<int, char, bool> wwzzgg; tuple_idex<wwzzgg>::type<2>;
wwzzgg ww; show(ww); type_list<int, char, bool>::type<0>;
std::tuple<int, float, int, My> x{ 2, 3.2f, 1, my };
show(tuple_filter<float>(x));
std::cout << typeid(tuple_remove_elements<decltype(x), float, int>::type).name();
std::cout << type_get<int>(x);
std::cout << '\n';
show(x);
//assert(std::get<0>(tuple_filter<int>(x)) == 3.2f);
return 0;
}
元组实战,FuckJSON的实现。一个利用两句宏和一个开源库nlohmann JSON实现的低代码结构体反射,序列化反序列化JSON。仅仅只需要极少的代码,就可以实现,无需人工手写。降低了手写的错误率,极大提高了生产力,以及极大简化了代码行数,极大增强了可维护性。 下面两个库都是本人的,可以根据情况自行选择。
实战二,ccORM是一个利用宏模板实现的现代c++orm库,已经支持了类似mysql,pgsql,sqlite类型的数据库,并支持一对一查询,SQL构造器查询。
目前已经完美支持了数组类型的序列化反序列化,以及指针的序列化。
性能测试仅次于rapidJSON,不过rapidJSON目前还不能做到低代码, 而且写起来极其复杂,而低代码才是趋势,所以我还是比较看好nlohmann JSON。目前正在打算利用元组反射机制实现一个c++的orm,由于难度极大,业界内尚无典范,我手头一无资源,二无人脉,三无敲门砖,这个项目是纯粹靠IQ的情况下完成的,有句俗语叫艰苦的思考胜于勤奋的堆砌代码,虽做的有点仓促,但功能完备,如有更好的业界实现,也可以留言互相讨论。