第26课 可变参数模板(7)_any和variant类的实现
1. any类的实现
(1)any类:
①是一个特殊的,只能容纳一个元素的容器,它可以擦除类型,可以将何任类型的值赋值给它。
②使用时,需要根据实际类型将any对象转换为实际的对象。
(2)实现any的关键技术
①当赋值给any时,需要将值的类型擦除,即以一种通用的方式保存所有类型的数据。通常是通过继承去擦除类型,基类是不含模板参数的,派生类才含有所要包装对象的类型。
②赋值时,创建派生类对象赋值给基类指针,派生类携带了数据类型,通过赋值兼容原则擦除了原始数据类型。当需要取数据时,再向下转换成派生类类型,转换失败时抛异常。
③由于向any赋值时,会使用原型模式创建出一个派生类对象,这里可以用unique_ptr智能指针来管理该对象的生命期。
④设计思路小结:Any内部维护了一个基类指针,通过基类指针擦除具体类型,any_cast时再通过向下转型获取实际数据。当转型失败时打印详情。
【编程实验】any类的实现
//Any.hpp
#include <memory>
#include <typeindex>
#include <iostream> class Any //注意Any不是一个模板类!但其成员函数可能是个模板函数。
{
private:
//内部类
struct Base;
typedef std::unique_ptr<Base> BasePtr; struct Base //基类不携带被包装对象的类型信息
{
virtual BasePtr Clone() const = ; //设计模式之原型模式
virtual ~Base(){/*std::cout << "virtual ~Base()" << std::endl;*/}
}; template<typename T> //T为被包装对象的类型
struct Derived : Base //派生类
{
public:
T m_value; //被包装的对象
public:
template<typename U>
Derived(U&& value) : m_value(std::forward<U>(value)){} BasePtr Clone() const
{
//由于返回堆对象,所以用智能指针来管理
return BasePtr(new Derived<T>(m_value));
}
};
private:
BasePtr m_ptr; //使用智能指针来管理被包装对象的生命期
std::type_index m_tpIndex; BasePtr Clone() const
{
return (m_ptr == nullptr) ? nullptr : m_ptr->Clone();
}
template<typename T>
using type_origin = typename std::decay<T>::type; public:
template<class U>
bool Is() const
{
return m_tpIndex == std::type_index(typeid(U));
} //注意:std::unique_ptr重载了operator bool()类型转换函数
bool IsNull() const{return !bool(m_ptr);}
public:
Any(void) : m_tpIndex(std::type_index(typeid(void))){}
Any(Any& that) : m_ptr(that.Clone()), m_tpIndex(that.m_tpIndex){}
Any(Any&& that) : m_ptr(std::move(that.Clone())), m_tpIndex(that.m_tpIndex){} //其他类型的被包装对象value(即非Any类),则通过以下构造函数来构造。
//一般需要先通过std::decay类型来移除value的引用和cv属性
template<typename U, class = typename std::enable_if
<!std::is_same<type_origin<U>, Any>::value, U>::type>
Any(U&& value): m_ptr(new Derived<type_origin<U>>(std::forward<U>(value))),
m_tpIndex(std::type_index(typeid(type_origin<U>)))
{
//std::cout << "Any(U&& value)" << std::endl;
} //将Any转换为实际的类型
template<class U>
U& AnyCast()
{
if(!Is<U>()){
std::cout <<"can not cast "<<typeid(U).name()
<<" to " << m_tpIndex.name() << std::endl;
throw std::bad_cast();
} auto derived = dynamic_cast<Derived<U>*>(m_ptr.get());
return derived->m_value;
} //重载赋值操作符
Any& operator=(const Any& a)
{
if(m_ptr != a.m_ptr){
m_ptr = a.Clone(); //unique_ptr指针不能直接赋值给另一个unique_ptr
m_tpIndex = a.m_tpIndex;
}
//std::cout << "Any& operator=(const Any& a)" << std::endl;
return *this;
}
};
//testAny.cpp
#include <iostream>
#include "Any.hpp"
using namespace std; int main()
{
Any n;
auto r = n.IsNull(); //true;
cout << r << endl; string s1 = "hello world!"; n = s1; //先将调用Any(U&&)将s1转为Any类,再赋值给n
cout << n.AnyCast<string>() << endl; n = ; //先将调用Any(U&&)将1转为Any类,再赋值给n
cout << n.AnyCast<int>() << endl;
n.Is<int>(); //true; //n.AnyCast<string>(); //error return ;
}
2. variant类的实现
(1)variant类
①类似于union,它能代表定义的多种类型,允许赋不同类型的值给它。它的具体类型是在初始化赋值时确定的。
②variant用途之一是擦除类型,不同类型的值都统一成一个variant。如variant<int,char,double> vt;表示其可以代表三种类型,但具体哪一种,在初始化时确定。
(2)实现variant的关键技术
①找出多种类型中size最大的那个类型,并构造一个内存对齐的缓冲区用于存放variant的值。(见IntegerMax<Types…>::value)
②类型检查和缓冲区中创建对象:包含检查赋值的类型是否在己定义的类型中(见Contains<T, Types>::value)以及缓冲区中创建、析构对象。
③通过类型取值时,要判断类型是否匹配,如果不匹配,则打印详情。(见Get<T>函数)
④通过索引位置获取类型(IndexType函数)和通过类型获取索引位置(get<T>函数)
⑤使用访问者模式,定义访问variant各个类型的方法。(见visit函数)
//function_traits.hpp:与上一节相同
#ifndef _FUNCTION_TRAITS_H_
#define _FUNCTION_TRAITS_H_ #include <functional>
#include <tuple> //普通函数
//函数指针
//function/lambda
//成员函数
//函数对象 template<typename T>
struct function_traits; //前向声明 //普通函数
template<typename Ret, typename... Args>
struct function_traits<Ret(Args...)>
{
public:
enum {arity = sizeof...(Args)};//arity : 参数的数量 //函数别名
typedef Ret function_type(Args...); //<==> using function_type = Ret(Args...); typedef Ret return_type; //返回值类型
using stl_function_type = std::function<function_type>;
typedef Ret(*pointer)(Args...); //获取可变参数模板中第I个位置的参数类型。
template<size_t I, class = typename std::enable_if<(I<arity)>::type>
using args = typename std::tuple_element<I, std::tuple<Args...>>;
}; //函数指针
template<typename Ret, typename... Args>
struct function_traits<Ret(*)(Args...)> : function_traits<Ret(Args...)>{}; //std::function
template<typename Ret, typename... Args>
struct function_traits<std::function<Ret(Args...)>> : function_traits<Ret(Args...)>{}; //成员函数
#define FUNCTION_TRAITS(...) \
template <typename ReturnType, typename ClassType, typename... Args> \
struct function_traits<ReturnType(ClassType::*)(Args...) __VA_ARGS__> : function_traits<ReturnType(Args...)>{}; FUNCTION_TRAITS()
FUNCTION_TRAITS(const) //const成员函数
FUNCTION_TRAITS(volatile)
FUNCTION_TRAITS(const volatile) //函数对象
template<typename Callable>
struct function_traits : function_traits<decltype(&Callable::operator())>{}; //将lambda转为std::function
template<typename Function>
typename function_traits<Function>::stl_function_type
to_function(const Function& lambda)
{
return static_cast<typename function_traits<Function>::stl_function_type>(std::forward<Function>(lambda));
} template<typename Function>
typename function_traits<Function>::stl_function_type
to_function(Function&& lambda)
{
return static_cast<typename function_traits<Function>::stl_function_type>(lambda);
} //将lambda转为函数指针,如
template<typename Function>
typename function_traits<Function>::pointer
to_function(const Function& lambda)
{
// typedef int(*FUN)(int);
// auto f = FUN([](int x){return x + 10;});
// cout << f(10) << endl; //
return static_cast<typename function_traits<Function>::pointer>(lambda);
} #endif //_FUNCTION_TRAITS_H_
//Variant.hpp
#include <typeindex>
#include <iostream>
#include "function_traits.hpp" /** 获取最大的整数 **/
template<size_t arg, size_t...rest> //eg.IntegerMax<8, 7, 9, 10, 5, 6, 3, 4>::value == 10
struct IntegerMax : std::integral_constant<size_t, (arg > IntegerMax<rest...>::value) ?
arg : IntegerMax<rest...>::value >
{}; template<size_t arg> //递归终止
struct IntegerMax<arg> : std::integral_constant<size_t, arg>
{
}; /** 获取最大的align **/
template<typename... Args> //eg. MaxAlign<char, int, double>::value == 8
struct MaxAlign : std::integral_constant<int, IntegerMax<std::alignment_of<Args>::value...>::value>
{}; /** 是否包含某个类型 **/
template<typename T, typename...List> //eg. Contains<float, char,double,int>::value == 0
struct Contains; template<typename T, typename Head, typename... Rest>
struct Contains<T, Head, Rest...> :
std::conditional<std::is_same<T, Head>::value,
std::true_type,
Contains<T, Rest...> >::type
{}; template<typename T>
struct Contains<T> : std::false_type{}; /**获取T在List中的索引位置**/
//在展开参数包的过程中看是否匹配到特化的IndexOf<T, T, Rest...>,
//如果匹配上则终止递归将之前的value累加起来得到目标类型的索引位置,
//否则将value加1,如果所有的类型中都没有对应的类型则返回-1;
template<typename T, typename... List>
struct IndexOf; //IndexOf<int, double, short, char, int, float>::value == 3 template<typename T, typename Head, typename... Rest>
struct IndexOf<T, Head, Rest...> //T和Head不同
{
private:
enum {temp = IndexOf<T, Rest...>::value };
public:
//enum {value = (temp == -1) ? -1 : temp + 1};
//enum{value = (IndexOf<T, Rest...>::value == -1) ? -1 : IndexOf<T, Rest...>::value + 1 };
enum{value = IndexOf<T, Rest...>::value + };
}; template<typename T, typename... Rest>
struct IndexOf<T, T, Rest...> //T和Head相同
{
enum{value = };
}; template<typename T>
struct IndexOf<T>
{
enum{value = -};
}; /**获取指定位置的类型**/
template<int index, typename... Types> //eg. At<1, int, double, char>::type == double
struct At; template<int index, typename First, typename... Types>
struct At<index, First, Types...>
{
using type = typename At<index-, Types...>::type;
}; template<typename T, typename... Types>
struct At<, T, Types...>
{
using type = T;
}; //Variant类
template<typename... Types>
class Variant
{
enum
{
data_size = IntegerMax<sizeof(Types)...>::value, //类型的最大值
align_size = MaxAlign<Types...>::value //类型最大内存对齐值
};
using data_t = typename std::aligned_storage<data_size, align_size>::type; private:
data_t m_data;
std::type_index m_typeIndex;//类型ID private: //拷贝
void copy(const std::type_index& old_t, const void* old_v, void *new_v)
{
//使用lambda表达式来展开Types...以便根据当前m_typeIndex来判断是否进行拷贝
[&]{std::initializer_list<int>{(copy0<Types>(old_t, old_v, new_v),)...};}();
} template<typename T>
void copy0(const std::type_index& old_t, const void* old_v, void *new_v)
{
if(old_t == std::type_index(typeid(T)))
new (new_v) T (*reinterpret_cast<const T*>(old_v)); //placement new
} //移动
void move(const std::type_index& old_t, void* old_v, void *new_v)
{
//使用lambda表达式来展开Types...以便根据当前m_typeIndex来判断是否进行移动
[&]{std::initializer_list<int>{(move0<Types>(old_t, old_v, new_v),)...};}();
} template<typename T>
void move0(const std::type_index& old_t, void* old_v, void *new_v)
{
if(old_t == std::type_index(typeid(T)))
new (new_v) T (std::move(*reinterpret_cast<T*>(old_v))); //placement new
} //销毁
void destroy(const std::type_index& index, void * buf)
{
[&]{std::initializer_list<int>{(destroy0<Types>(index, buf),)...};}();
} template<typename T>
void destroy0(const std::type_index& index, void * buf)
{
if(index == std::type_index(typeid(T)))
reinterpret_cast<T*>(buf)->~T();
} public:
template<int index>
using IndexType = typename At<index, Types...>::type; Variant(void) : m_typeIndex(typeid(void)){} Variant(const Variant<Types...>& old) : m_typeIndex(old.m_typeIndex)
{
copy(old.m_typeIndex, &old.m_data, &m_data);
} Variant(Variant<Types...>&& old) : m_typeIndex(old.m_typeIndex)
{
move(old.m_typeIndex, &old.m_data, &m_data);
} Variant& operator=(const Variant& old)
{
copy(old.m_typeIndex, &old.m_data, &m_data);
m_typeIndex = old.m_typeIndex;
return *this;
} Variant& operator=(Variant&& old)
{
move(old.m_typeIndex, &old.m_data, &m_data);
m_typeIndex = old.m_typeIndex;
return *this;
} //当Variant被Types...中某一类型的对象初始化时,调用该构造函数
//1. enable_if确保T只能是Types...中的类型,此外,enable_if无第2个参数,则默认为void
template<class T, class = typename std::enable_if<
Contains<typename std::decay<T>::type, Types...>::value>::type>
Variant(T&& value) : m_typeIndex(typeid(void))
{
destroy(m_typeIndex, &m_data); typedef typename std::decay<T>::type U;
new(&m_data) U(std::forward<T>(value));
m_typeIndex = std::type_index(typeid(U));
} ~Variant()
{
destroy(m_typeIndex, &m_data);
} public:
template<typename T>
bool is() const //判断Variant值的类型是否为T
{
return (m_typeIndex == std::type_index(typeid(T)));
} bool Empty()const
{
return m_typeIndex == std::type_index(typeid(void));
} std::type_index type() const //获取类型ID
{
return m_typeIndex;
} //获取元素的值
template<typename T>
typename std::decay<T>::type& get()
{
using U = typename std::decay<T>::type;
if(!is<U>()){
std::cout << typeid(U).name() << " is not defined. "
<< "current type is " << m_typeIndex.name() << std::endl;
throw std::bad_cast{};
}
return *(U*)(&m_data);
} //获取类型的位置索引
template<typename T>
int indexOf()
{
return IndexOf<T, Types...>::value;
} //访问元素(访问者模式)
template<typename F>
void visit(F&& f)
{
//求出F函数的第1个参数的类型(注意由于args也是个模板,需加template)
using T = typename function_traits<F>::template args<>::type; if (is<T>())
f(get<T>()); //获取元素的值,并传入f函数中。
} template<typename F, typename... Rest>
void visit(F&& f, Rest&&... rest)
{
using T = typename function_traits<F>::template args<>::type;
if(is<T>()){
visit(std::forward<F>(f));
}else{
visit(std::forward<Rest>(rest)...);
}
}
};
//testVariant.cpp
#include "Variant.hpp"
#include <iostream> using namespace std; int main()
{
cout << IntegerMax<, , , , , , , >::value << endl; cout << MaxAlign<char, int, double>::value << endl;
cout << sizeof(double) << endl; cout << Contains<float, char, double, int>::value << endl;
cout << IndexOf<float, double, short, char, int>::value << endl; using T = At<, int, double, char>::type;
cout << typeid(T).name() << endl; //输出double typedef Variant<int, double, string, char> V;
V v1 = ; //获取索引位置为1的类型
cout << typeid(V::IndexType<>).name() << endl; //double //获取类型的索引位置
cout << v1.indexOf<string>() << endl; // cout << v1.get<int>() << endl; //10 //是否为空
cout << v1.Empty() << endl; //
cout << v1.type().name() << endl; //int V v2 = v1; //初始化,也可以在这里测试赋值操作
cout << v2.get<int>() << endl; //10 //通过一组lambda访问variant
v1.visit([&](double i){cout << i << endl;},
[&](short i){cout << i << endl;},
[&](int i){cout << i << endl;}, //
[&](string i){cout << i << endl;}); return ;
}
第26课 可变参数模板(7)_any和variant类的实现的更多相关文章
- 第27课 可变参数模板(8)_TupleHelper
1. TupleHelper的主要功能 (1)打印:由于tuple中的元素是可变参数模板,外部并不知道内部到底是什么数据,有时调试时需要知道其具体值,希望能打印出tuple中所有的元素值. (2)根据 ...
- 第25课 可变参数模板(6)_function_traits和ScopeGuard的实现
1. function_traits (1)function_traits的作用:获取函数的实际类型.返回值类型.参数个数和具体类型等.它能获取所有函数语义类型信息.可以获取普通函数.函数指针.std ...
- 第24课 可变参数模板(5)_DllHelper和lambda链式调用
1. dll帮助类 (1)dll的动态链接 ①传统的调用方式:先调用LoadLibrary来加载dll,再定义函数指针类型,接着调用GetProcAddress获取函数地址.然后通过函数指针调用函数, ...
- 第23课 可变参数模板(4)_Optional和Lazy类的实现
1. optional类的实现 (1)optional的功能 ①optional<T>的内部存储空间可能存储了T类型的值,也可能没有.只有当optional被T初始化之后,这个option ...
- C++反射机制:可变参数模板实现C++反射
1. 概要 本文描述一个通过C++可变参数模板实现C++反射机制的方法.该方法非常实用,在Nebula高性能网络框架中大量应用,实现了非常强大的动态加载动态创建功能.Nebula框架在Github ...
- C++ 0x 使用可变参数模板类 实现 C# 的委托机制
#ifndef _ZTC_DELEGATE_H_ #define _ZTC_DELEGATE_H_ #include <vector> #include <functional> ...
- c++11 可变参数模板类
c++11 可变参数模板类 #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> #inc ...
- c++11 可变参数模板函数
c++11 可变参数模板函数 #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> #in ...
- C++反射机制:可变参数模板实现C++反射(使用C++11的新特性--可变模版参数,只根据类的名字(字符串)创建类的实例。在Nebula高性能网络框架中大量应用)
1. 概要 本文描述一个通过C++可变参数模板实现C++反射机制的方法.该方法非常实用,在Nebula高性能网络框架中大量应用,实现了非常强大的动态加载动态创建功能.Nebula框架在码云的仓库地 ...
随机推荐
- 如何解析超长的protobuf zhuan
在调用protobuf的ParseFromString(str)方法时,默认情况下,如果str的长度>64MB,会返回失败. 这里给出了解释,主要是出于安全因素的考虑. 可以通过SetTotal ...
- learning makefile vpath(1)
- Linux 云服务器中安装 rinetd 进行转发端口实现
端口转发映射的程序叫rinetd,直接make编译安装即可. wget http://www.boutell.com/rinetd/http/rinetd.tar.gz&&tar -x ...
- tensorFlow(一)相关重要函数理解
1.函数及参数:tf.nn.conv2d conv2d( input, filter, strides, padding, use_cudnn_on_gpu=True, data_format='NH ...
- 高性能场景下,HashMap的优化使用建议
1. HashMap 在JDK 7 与 JDK8 下的差别 顺便理一下HashMap.get(Object key)的几个关键步骤,作为后面讨论的基础. 1.1 获取key的HashCode并二次加工 ...
- LeetCode 695 岛屿的最大面积
题目: 给定一个包含了一些 0 和 1的非空二维数组 grid , 一个 岛屿 是由四个方向 (水平或垂直) 的 1 (代表土地) 构成的组合.你可以假设二维矩阵的四个边缘都被水包围着. 找到给定的二 ...
- java readProperties
@Deprecated public static Map<String, String> readProperties(String propertiesFile) { Properti ...
- AC Challenge(状压dp)
ACM-ICPC 2018 南京赛区网络预赛E: 题目链接https://www.jisuanke.com/contest/1555?view=challenges Dlsj is competing ...
- Oracle 创建存储过程 提示权限不足或者提示表和视图不存在问题
grant create view to hospital; --授予查询权限 grant select any table to hospital; --授予权限 grant select any ...
- php优秀框架codeigniter学习系列——CI_Output类的学习
这篇文章主要介绍CI核心框架工具类CI_Output. 根据CI文档自己的定义,这个类主要就是生成返回的页面给浏览器.以下选取类中的重点方法进行说明. __construct() 在构造函数中,主要确 ...