Вариант реализации, такой как boost :: any с поддержкой автоматического преобразования - PullRequest
1 голос
/ 06 сентября 2011

Я хочу реализовать вариантный класс, который может хранить любой тип данных (например, 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, но мне нужно знать конкретный тип, чтобы выбрать конвертер <>.Есть ли какой-либо шаблон проектирования или обходной путь для решения этой проблемы?

Ответы [ 3 ]

2 голосов
/ 06 сентября 2011

Это невозможно.Для этого вам потребуется поддержка шаблонов в виртуальных функциях.

В контексте вызывая у вас есть только тип для преобразования, и вы не можете извлечь сохраненный тип.В контексте , называемом , у вас есть только сохраненный тип, и вы не можете получить тип, в который необходимо преобразовать.

Невозможно передать тип между ними, поэтому вы никогда не сможете узнать оба типа одновременно и, следовательно, не сможете выполнить какое-либо преобразование.

1 голос
/ 06 сентября 2011

Ваша проблема неразрешима.

Если вы потеряли информацию о типе, вы не сможете восстановить ее (не полностью), поскольку сам язык ее не поддерживает (без рефлексии / самоанализа).

Вы все еще можете знать точный тип, но не можете получить свойства, такие как преобразования в произвольные типы, потому что механизмы преобразования запекаются во время компиляции (в зависимости от конструкторов, операторов преобразования и правил языка).

Еслиу вас есть только небольшое подмножество типов, которые вас интересуют, тогда Boost.Variant - ваш лучший выбор.

Если вы действительно хотели иметь полностью динамический язык ... тогда либо откажитесь от C ++, либо переопределите динамический языкязык поверх C ++ ...

0 голосов
/ 06 сентября 2011

Вы можете использовать оператор typeid, чтобы получить информацию о типе, сохраненную в варианте, и сравнить ее с typeid из T в get:

Расширить 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
        }
    }
}

РЕДАКТИРОВАТЬ: Вы также можете посмотреть реализацию свойства OGRE , которая не использует typeid, но перечисляет для определенного набора типов. Поэтому все остальные типы не поддерживаются.

...