boost学习 内嵌类型检测 与 any 的代码练习

时间:2023-03-09 18:19:46
boost学习  内嵌类型检测 与 any 的代码练习

本文是学习 boost源码的一些练习

参考文章来自

刘未鹏

C++的罗浮宫(http://blog.****.net/pongba)

目录

http://blog.****.net/pongba/article/category/37521

检测内嵌类型

检测一个类中是否存在指定的类型

那么只需要利用模板检测输入参数 根据参数的不同 导入到不同的函数中

类似

 template <typename T>
void Register(T person)
{
Register(person, typename T::person_tag());
}
    struct student_tag {};
16 struct teacher_tag {};
17
18 template<typename T>
19 void Register(T p,student_tag) {
20 std::cout << __FUNCTION__ << " student_tag"<< std::endl;
21 }
22
23 template<typename T>
24 void Register(T p, teacher_tag) {
25 std::cout << __FUNCTION__ << " teacher_tag"<<std::endl;
26 }
那么只要输入的参数含有这个tag结构 就会输入到不同的函数中 这是stl经常使用的套路 另外一种需求是检测类中是否带有指定的type 而不是如上所述 要事先在类中定义好tag
比如我们要检测任意类型中是否有我们需要关注的key_type
定义如下
 36 typedef char(&yes_type)[1]; // sizeof(yes_type)==1
37 typedef char(&no_type)[2]; // sizeof(no_type)==2
38
39 template<class T>
40 struct does_sometypedef_exists
41 {
42 template<class U>
43 static yes_type check(U, typename U::key_type* = nullptr); // #1
44 static no_type check(...);
45 static T t; // 声明
46 static const bool value = sizeof(check(t)) == sizeof(yes_type);
47 };
根据模板特性 如果输入的参数U 带有key_type 则check函数是该形式
static yes_type check(U, typename U::key_type* = nullptr);
返回 yes_type 输入参数U是其他类型 则check形式如下
 static no_type check(...);
返回 no_type 根据定义 no_type yes_type长度不同
那么只需要检测check函数的返回长度 就可以确认输入的参数U是否带有key_type
static const bool value = sizeof(check(t)) == sizeof(yes_type);
我们查看下
does_sometypedef_exists<T>::type 是否为真就能确认T是否包含key_type

//================================================================================

同样的利用模板偏特化及默认模板参数的规则也可以实现
根据输入类型T是否包含key_type 适配不同版本代码

template<class T,class>

struct check_helper

{

typedef T type;

};

template<class T,class =T>

struct does_sometypedef_exists_1

{

static const bool value=false;

};

template<class T>

struct does_sometypedef_exists_1<T,

typename check_helper<T, typename T::key_type>::type>

{

static const bool value=true;

};

完整代码如下

 // Study1.cpp: 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include <string>
#include <iostream> template <typename T>
void Register(T person)
{
Register(person, typename T::person_tag());
} struct student_tag {};
struct teacher_tag {}; template<typename T>
void Register(T p,student_tag) {
std::cout << __FUNCTION__ << " student_tag"<< std::endl;
} template<typename T>
void Register(T p, teacher_tag) {
std::cout << __FUNCTION__ << " teacher_tag"<<std::endl;
} void ModelInSTL() {
std::string person;
student_tag s;
teacher_tag t;
Register(person, s);
Register(person, t);
}
//=========================================================
typedef char(&yes_type)[]; // sizeof(yes_type)==1
typedef char(&no_type)[]; // sizeof(no_type)==2 template<class T>
struct does_sometypedef_exists
{
template<class U>
static yes_type check(U, typename U::key_type* = nullptr); // #1
static no_type check(...);
static T t; // 声明
static const bool value = sizeof(check(t)) == sizeof(yes_type);
}; struct A {};
struct B
{
typedef int key_type;
}; // key_type为成员函数
struct C { void key_type(void) {} }; // key_type为静态常量数据成员
struct D { static const bool key_type = false; }; struct E {
struct key_type
{};
}; //============================================================== template<class T, class>
struct check_helper
{
typedef T type;
}; template<class T, class = T>
struct does_sometypedef_exists_1
{
static const bool value = false;
}; template<class T>
struct does_sometypedef_exists_1<T,
typename check_helper<T, typename T::key_type>::type>
{
static const bool value = true;
}; //========================================================= int main()
{
ModelInSTL();
std::cout << does_sometypedef_exists<A>::value << std::endl;
std::cout << does_sometypedef_exists<B>::value << std::endl;
std::cout << does_sometypedef_exists<C>::value << std::endl;
std::cout << does_sometypedef_exists<D>::value << std::endl;
std::cout << does_sometypedef_exists<E>::value << std::endl;
std::cout << std::endl;
std::cout << does_sometypedef_exists_1<A>::value << std::endl;
std::cout << does_sometypedef_exists_1<B>::value << std::endl;
std::cout << does_sometypedef_exists_1<C>::value << std::endl;
std::cout << does_sometypedef_exists_1<D>::value << std::endl;
std::cout << does_sometypedef_exists_1<E>::value << std::endl; return ;
}

ANY

BOOST中有一个ANY类

可以接受任意类型的输入

示例如下

11 #include <boost/any.hpp>
12 #include <list>
13 #include <exception>
14 #include <memory>
15 //
16 //class AClass {};
17 //
18 //void BOOSTAnySample()
19 //{
20 // typedef std::list<boost::any> many;
21 // //any可存入任何类型
22 // many values;
23 // boost::any value = 1;
24 // values.push_back(value);
25 //
26 // value = "string";
27 // values.push_back(value);
28 //
29 // values.push_back(true);
30 // values.push_back(nullptr);
31 // values.push_back(AClass());
32 //} 根据使用方式 any不能定义模板 因为我们不可能使用any<int> a = 1; 那同定义 int a = 1就没区别了
所以any类中肯定有一个与输入类型相同的元素进行存储 但是any本身没有模板 那么这个存储输入类型的元素肯定是指针 但是指针也无法指定存储的类型
那么解决办法是? 就是指针是一个基类指针 同时指向一个带模板的继承基类的类
那么基本上代码就类似以下(代码来自刘未鹏的博客 http://blog.****.net/pongba/article/details/82811)
 摘自”boost/any.hpp”

 class any

 {

 public:

 class placeholder // 泛型数据容器holder的非泛型基类  

 {                   

 public:

 // 虚析构函数,为保证派生类对象能用基类指针析构

 virtual ~placeholder(){}

 public:

   // 提供关于类型的信息

 virtual const std::type_info & type() const = ;

 virtual placeholder * clone() const = ;  // 复制

 }; // placeholder

 template<typename ValueType>

 class holder : public placeholder

 {

 public:

 holder(const ValueType & value)

 : held(value)

 {}

 public:

 virtual const std::type_info & type() const

 {

   // typeid返回std::typeinfo对象引用,后者包含任意对象的类型信息, 如name,此外还提供operator==操作符你可以用typeid(oneObj)==typeid(anotherObj)来比两个对象之类型是否一致。

 return typeid(ValueType); 

 }

 virtual placeholder * clone() const

 {

 return new holder(held);  // 改写虚函数,返回自身的复制体

 }

 public:

 ValueType held; // 数据保存的地方

 }; // holder

 // 指向泛型数据容器holder的基类placeholder的指针

 placeholder * content;

 //模板构造函数,动态分配数据容器并调用其构造函数

 template<typename ValueType>

 any(const ValueType & value)

 : content(new holder<ValueType>(value))

 {}

 ...

 // 与模板构造函数一样,但使用了swap惯用手法

 template<typename ValueType>

 any & operator=(const ValueType & rhs)

 {

 // 先创建一个临时对象any(rhs),再调用下面的swap函数进行底层数据交换,注意与*this交换数据的是临时对象,所以rhs的底层数据并未被更改,只是在swap结束后临时对象拥有了*this的底层数据,而此时*this也拥有了临时对象构造时所拥有的rhs的数据的副本。然后临时对象由于生命期的结束而被自动析构,*this原来的底层数据随之烟消云散。

 any(rhs).swap(*this);

 return *this;

 }

 any & swap(any & rhs) //swap函数,交换底层数据

 {

 std::swap(content, rhs.content); // 只是简单地将两个指针的值互换

 return *this;

 }

 ~any()  //析构函数

 {

   //释放容器,用的是基类指针,这就是placeholder需要一个虚析构函数的原因

 delete content;

 }

 ...

 };

存储之后 在赋值给其他元素的过程中 我们需要一个转换过程

就是any_cast<typename T>()

代码如下

(代码来自刘未鹏的博客 http://blog.****.net/pongba/article/details/82811)

template<typename ValueType>

ValueType any_cast(const any & operand)

{

// 调用any_cast针对指针的版本。

const ValueType * result = any_cast<ValueType>(&operand);

// 如果cast失败,即实际 保存的并非ValueType型数据,则抛出一个异常。

if(!result)

throw bad_any_cast(); // 派生自std::bad_cast

return *result;

}

template<typename ValueType>

ValueType * any_cast(any * operand)

{

// 这个类型检查很重要,后面会对它作更详细的解释

return

operand &&

(operand->type()==typeid(ValueType)? // #1

&static_cast<any::holder<ValueType>*>(operand->content)->held

: 0; // 这儿有个向下类型转换

}

全部代码如下

 // UseRapidJsonSample.cpp: 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include <iostream>
#include <string>
#include "JsonStringTool.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/document.h"
#include <boost/any.hpp>
#include <list>
#include <exception>
#include <memory>
//
//class AClass {};
//
//void BOOSTAnySample()
//{
// typedef std::list<boost::any> many;
// //any可存入任何类型
// many values;
// boost::any value = 1;
// values.push_back(value);
//
// value = "string";
// values.push_back(value);
//
// values.push_back(true);
// values.push_back(nullptr);
// values.push_back(AClass());
//}
//===========================================================
class any
{
public: class placeholder // 泛型数据容器holder的非泛型基类
{
public:
// 虚析构函数,为保证派生类对象能用基类指针析构
virtual ~placeholder() {} public:
// 提供关于类型的信息
virtual const std::type_info & type() const = ;
virtual placeholder * clone() const = ; // 复制
}; // placeholder template<typename ValueType>
class holder : public placeholder
{
public:
holder(const ValueType & value)
: held(value)
{}
public:
virtual const std::type_info & type() const
{
// typeid返回std::typeinfo对象引用,后者包含任意对象的类型信息, 如name,此外还提供operator==操作符你可以用typeid(oneObj)==typeid(anotherObj)来比两个对象之类型是否一致。
return typeid(ValueType);
} virtual placeholder * clone() const
{
return new holder(held); // 改写虚函数,返回自身的复制体
} public:
ValueType held; // 数据保存的地方
}; // holder // 指向泛型数据容器holder的基类placeholder的指针
placeholder * content; //模板构造函数,动态分配数据容器并调用其构造函数
template<typename ValueType>
any(const ValueType & value)
: content(new holder<ValueType>(value))
{} // 与模板构造函数一样,但使用了swap惯用手法
template<typename ValueType>
any & operator=(const ValueType & rhs)
{
// 先创建一个临时对象any(rhs),再调用下面的swap函数进行底层数据交换,注意与*this交换数据的是临时对象,所以rhs的底层数据并未被更改,只是在swap结束后临时对象拥有了*this的底层数据,而此时*this也拥有了临时对象构造时所拥有的rhs的数据的副本。然后临时对象由于生命期的结束而被自动析构,*this原来的底层数据随之烟消云散。
any(rhs).swap(*this);
return *this;
} any & swap(any & rhs) //swap函数,交换底层数据
{
std::swap(content, rhs.content); // 只是简单地将两个指针的值互换
return *this;
} ~any() //析构函数
{
//释放容器,用的是基类指针,这就是placeholder需要一个虚析构函数的原因
delete content;
} };
//
template<typename ValueType>
ValueType * any_cast(const any * operand)
{
// 这个类型检查很重要,后面会对它作更详细的解释
return
operand &&
(operand->content->type() == typeid(ValueType)) ? // #1
&((static_cast<any::holder<ValueType>*>(operand->content))->held)
: ; // 这儿有个向下类型转换
} template<typename ValueType>
ValueType any_cast(const any & operand)
{
//// 调用any_cast针对指针的版本。 const ValueType * result = any_cast<ValueType>(&operand); // 如果cast失败,即实际 保存的并非ValueType型数据,则抛出一个异常。
if (!result)
throw std::exception("bad alloc"); // 派生自std::bad_cast
return *result;
} int main()
{ any ai();
int i = any_cast<int>(ai);
std::cout << i << std::endl; any ad(3.12222222222222222);
double d = any_cast<double>(ad);
std::cout << d << std::endl; any ab(true);
bool b = any_cast<bool>(ab);
std::cout << b << std::endl; any ac('z');
char c = any_cast<char>(ac);
std::cout << c << std::endl; return ;
}