使用 boost::any 的替代方案在 C++ 中实现高性能动态类型






4.77/5 (27投票s)
一个比 boost::any
更高效的替代方案。
引言
Boost 库提供了一个非常有用的类,名为 boost::any
,它可以包含几乎任何类型的值,只要该值支持拷贝构造和赋值。这个 boost::any
类型允许动态查询包含的类型,并进行安全的类型转换。尽管它很有用,但 boost::any
的效率并不像它应该的那样高,所以我重写了自己的版本,我自豪地称之为 cdiggins::any
。
2011年更新:经过五年,我从头开始重写了这个类,以修复一些非常严重的错误并简化代码。
使用 cdiggins::any
cdiggins::any
类型可以用来保存普通值类型,并提供了一种安全且显式地转换回适当类型的机制。
any a = 42;
cout << a.cast<int>() << endl;
a = 13;
cout << a.cast<int>() << endl;
a = "hello";
cout << a.cast<const char*>() << endl;
a = std::string("1234567890");
cout << a.cast<std::string>() << endl;
int n = 42;
a = &n;
cout << *a.cast<int*>() << endl;
any b = true;
cout << b.cast<bool>() << endl;
swap(a, b);
cout << a.cast<bool>() << endl;
a.cast<bool>() = false;
cout << a.cast<bool>() << endl;
cdiggins::any 的设计
cdiggins::any
类包含两个指针:一个指向策略类 (any::policy
),另一个是指向数据的指针 (any::data
),在某些情况下,该指针可能用于直接包含数据本身(例如,如果存储的原始数据类型小于或等于指针的大小)。
策略类用于执行分配、释放、拷贝等操作,并确定是使用 any::data
来保存数据还是指向数据。
最后,不再赘述,以下是 cdiggins::any
类的实现,供您欣赏
#pragma once
/*
* (C) Copyright Christopher Diggins 2005-2011
* (C) Copyright Pablo Aguilar 2005
* (C) Copyright Kevlin Henney 2001
*
* Distributed under the Boost Software License, Version 1.0. (See
* accompanying file LICENSE_1_0.txt or copy at
* https://boost.ac.cn/LICENSE_1_0.txt
*/
#include <stdexcept>
namespace cdiggins
{
namespace anyimpl
{
struct bad_any_cast
{
};
struct empty_any
{
};
struct base_any_policy
{
virtual void static_delete(void** x) = 0;
virtual void copy_from_value(void const* src, void** dest) = 0;
virtual void clone(void* const* src, void** dest) = 0;
virtual void move(void* const* src, void** dest) = 0;
virtual void* get_value(void** src) = 0;
virtual size_t get_size() = 0;
};
template<typename T>
struct typed_base_any_policy : base_any_policy
{
virtual size_t get_size() { return sizeof(T); }
};
template<typename T>
struct small_any_policy : typed_base_any_policy<T>
{
virtual void static_delete(void** x) { }
virtual void copy_from_value(void const* src, void** dest)
{ new(dest) T(*reinterpret_cast<T const*>(src)); }
virtual void clone(void* const* src, void** dest) { *dest = *src; }
virtual void move(void* const* src, void** dest) { *dest = *src; }
virtual void* get_value(void** src) { return reinterpret_cast<void*>(src); }
};
template<typename T>
struct big_any_policy : typed_base_any_policy<T>
{
virtual void static_delete(void** x) { if (*x)
delete(*reinterpret_cast<T**>(x)); *x = NULL; }
virtual void copy_from_value(void const* src, void** dest) {
*dest = new T(*reinterpret_cast<T const*>(src)); }
virtual void clone(void* const* src, void** dest) {
*dest = new T(**reinterpret_cast<T* const*>(src)); }
virtual void move(void* const* src, void** dest) {
(*reinterpret_cast<T**>(dest))->~T();
**reinterpret_cast<T**>(dest) = **reinterpret_cast<T* const*>(src); }
virtual void* get_value(void** src) { return *src; }
};
template<typename T>
struct choose_policy
{
typedef big_any_policy<T> type;
};
template<typename T>
struct choose_policy<T*>
{
typedef small_any_policy<T*> type;
};
struct any;
/// Choosing the policy for an any type is illegal, but should never happen.
/// This is designed to throw a compiler error.
template<>
struct choose_policy<any>
{
typedef void type;
};
/// Specializations for small types.
#define SMALL_POLICY(TYPE) template<> struct
choose_policy<TYPE> { typedef small_any_policy<TYPE> type; };
SMALL_POLICY(signed char);
SMALL_POLICY(unsigned char);
SMALL_POLICY(signed short);
SMALL_POLICY(unsigned short);
SMALL_POLICY(signed int);
SMALL_POLICY(unsigned int);
SMALL_POLICY(signed long);
SMALL_POLICY(unsigned long);
SMALL_POLICY(float);
SMALL_POLICY(bool);
#undef SMALL_POLICY
/// This function will return a different policy for each type.
template<typename T>
base_any_policy* get_policy()
{
static typename choose_policy<T>::type policy;
return &policy;
};
}
struct any
{
private:
// fields
anyimpl::base_any_policy* policy;
void* object;
public:
/// Initializing constructor.
template <typename T>
any(const T& x)
: policy(anyimpl::get_policy<anyimpl::empty_any>()), object(NULL)
{
assign(x);
}
/// Empty constructor.
any()
: policy(anyimpl::get_policy<anyimpl::empty_any>()), object(NULL)
{ }
/// Special initializing constructor for string literals.
any(const char* x)
: policy(anyimpl::get_policy<anyimpl::empty_any>()), object(NULL)
{
assign(x);
}
/// Copy constructor.
any(const any& x)
: policy(anyimpl::get_policy<anyimpl::empty_any>()), object(NULL)
{
assign(x);
}
/// Destructor.
~any() {
policy->static_delete(&object);
}
/// Assignment function from another any.
any& assign(const any& x) {
reset();
policy = x.policy;
policy->clone(&x.object, &object);
return *this;
}
/// Assignment function.
template <typename T>
any& assign(const T& x) {
reset();
policy = anyimpl::get_policy<T>();
policy->copy_from_value(&x, &object);
return *this;
}
/// Assignment operator.
template<typename T>
any& operator=(const T& x) {
return assign(x);
}
/// Assignment operator, specialed for literal strings.
/// They have types like const char [6] which don't work as expected.
any& operator=(const char* x) {
return assign(x);
}
/// Utility functions
any& swap(any& x) {
std::swap(policy, x.policy);
std::swap(object, x.object);
return *this;
}
/// Cast operator. You can only cast to the original type.
template<typename T>
T& cast() {
if (policy != anyimpl::get_policy<T>())
throw anyimpl::bad_any_cast();
T* r = reinterpret_cast<T*>(policy->get_value(&object));
return *r;
}
/// Returns true if the any contains no value.
bool empty() const {
return policy == anyimpl::get_policy<anyimpl::empty_any>();
}
/// Frees any allocated memory, and sets the value to NULL.
void reset() {
policy->static_delete(&object);
policy = anyimpl::get_policy<anyimpl::empty_any>();
}
/// Returns true if the two types are the same.
bool compatible(const any& x) const {
return policy == x.policy;
}
};
}
结束语
感谢 Pablo Aguilar 帮助完成了 cdiggins::any
类的早期版本。感谢 Raute 帮助我识别了 cdiggins::any
原始版本中的关键问题。