开发者

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

I want to implement a variant class that can store any datatype (like boost::an开发者_如何转开发y) but with the support of datatype conversion. For example,

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.

Here is the current implementation, based on the idea of 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>();
}

From the get<> template method, I only have access to an IStoredVariant pointer, but I need to know the concrete type to choose the Converter<>. Is there any design pattern or workaround to solve this problem?


This is impossible. You would need to have support for templates in virtual functions to make this happen.

In the calling context, you only have the type to be converted to, and you can't retrieve the stored type. In the called context, you only have the stored type and can't retrieve the type to be converted to.

There is no way to pass the type between them, so you can never know both types at once and therefore cannot perform any conversion.


Your problem is intractable.

If you have lost type information, then you cannot recover it (not fully) because the language itself does not support it (no reflexion/introspection).

You can still know the exact type, but you cannot get properties such as conversions to arbitrary types, because conversions mechanisms are baked in at compile-time (depending on constructors, conversion operators and language rules).

If you have only a small subset of types that you are interested in, then Boost.Variant is your best bet.

If you really wanted to have a fully dynamic language... then either ditch C++ or reimplement a dynamic language on top of C++...


You could use the typeid operator to get the type information of the type stored in the variant and compare this to the typeid of the T in get:

Extend the IStoredVariant with this interface definition:

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

Add the implementation to the concrete variant storage:

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

Use it in the Variant class:

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: You can also look at the property implementation of OGRE which does not use typeid but enums for a specific set of types. All other types are therefore unsupported.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜