具有自动转换支持的boost::any等变体实现

Variant implementation like boost::any with auto-conversion support

本文关键字:any 实现 boost 转换 支持      更新时间:2023-10-16

我想实现一个变体类,它可以存储任何数据类型(如boost::any),但支持数据类型转换。例如

Variant v1(int(23)); can be converted to bool via v1.get<bool>()
using Conv<int, bool>, Variant v2(CustomT1()); to CustomT2
via Conv<CustomT1, CustomT2> and so on.
下面是基于boost::any: 思想的当前实现
#include <iostream>
#include <string>
#include <memory>
#include <stdexcept>
template<typename Src, typename Dest>
struct Conv
{
    /* static? */ Dest convert(const Src& src) const { throw std::runtime_error("type cast not supported"); }
};
template<> struct Conv<int, bool>
{
    bool convert(const int &src) const { return src > 0; } 
};
class IStoredVariant
{};
template<typename T>
struct variant_storage : public IStoredVariant
{
    variant_storage(const T& value) : m_value(value)
    {}
    T&       getValue(void)       { return this->m_value; }
    const T& getValue(void) const { return this->m_value; }
    template<typename U>
    U make_conversion(void) const // just an idea...
    {
        return Conv<U, T>().convert(this->getValue());
    }
protected:
    T m_value;
};
class Variant
{
public:
    template<typename T>
    Variant(const T& value) : m_storage(new variant_storage<T>(value))
    {}
    IStoredVariant&       getImpl(void)       { return *this->m_storage; }
    const IStoredVariant& getImpl(void) const { return *this->m_storage; }
    std::auto_ptr<IStoredVariant> m_storage;
    template<typename T>
    T get(void) const
    {
        const IStoredVariant &var = this->getImpl();
        // ????????????
        // How to perform conversion?
    }
    template<typename T>
    void set(const T &value)
    {
        this->m_storage.reset(new variant_storage<T>(value));
    }
};
int main(void)
{
    Variant v(int(23));
    bool i = v.get<bool>();
}

从get<>模板方法中,我只能访问IStoredVariant指针,但是我需要知道选择Converter<>的具体类型。是否有任何设计模式或变通方法来解决这个问题?

这不可能。要实现这一点,您需要在虚函数中支持模板。

调用上下文中,您只有要转换为的类型,而不能检索存储的类型。在called上下文中,您只有存储的类型,而不能检索要转换为的类型。

无法在它们之间传递类型,因此您永远无法同时知道这两种类型,因此无法执行任何转换。

你的问题很棘手。

如果丢失了类型信息,则无法(不完全)恢复它,因为语言本身不支持它(没有反射/自省)。

您仍然可以知道确切的类型,但是您不能获得诸如到任意类型的转换之类的属性,因为转换机制是在编译时内置的(取决于构造函数、转换操作符和语言规则)。

如果您感兴趣的类型只有一小部分,那么Boost.Variant是您最好的选择。

如果你真的想要一个完全动态的语言…那么要么放弃c++,要么在c++的基础上重新实现一种动态语言…

您可以使用typeid运算符来获取存储在变体中的类型的类型信息,并将其与getTtypeid进行比较:

用这个接口定义扩展IStoredVariant:

class IStoredVariant
{
    ...
    type_info getTypeId() = 0; // note the abstract definition
    ...
}

将实现添加到具体的变量存储:

template<typename T>
struct variant_storage : public IStoredVariant
{
    ...
    type_info getTypeId() { return typeid(T); }
    ...
}

在Variant类中使用:

class Variant
{
    ...
    template<typename T>
    T get(void) const
    {
        const IStoredVariant *var = this->getImpl();
        if(typeid(T) == var->getTypeId())
        {
            // type matches: cast to the type
            variant_storage<T>* my_typed_var = static_cast<variant_storage<T>* >(var);
            // do something with it
        }
    }
}

EDIT:您还可以查看OGRE的属性实现,它不使用typeid,而是对特定类型集使用枚举。因此,不支持所有其他类型。