Реализация boost::any

316
31 августа 2017, 20:31

Необходимо сделать базовую реализацию boost::any. Главное условие: чтобы можно было свободно хранить в контейнерах разные типы. Требуется такое поведение:

MyAny a = 5;
cout << a.get() << endl; // a.get() -> int
a = "sdfsdf"; // Нельзя!!!
std::vector<MyAny> vec;
MyAny b = 5;
MyAny c = "sdfsdf";
MyAny d = MyClass(1, 4);
vec.push_back(b);
vec.push_back(c);
vec.push_back(d);

Подскажите, где посмотреть как реализовать подобный функционал.

Answer 1

Если в двух словах, то идея такая. Any будет хранить в себе указатель на объект, который хранит в себе то, что вы хотите положить в Any. Чтобы безопасно извлекать данные будет использоваться понижающее приведение типа dynamic_cast.

Для начала опишем небольшую иерархию "хранителей" данных:

class IValue{
public:
    virtual ~IValue(){}
};
template<class T>
class Value : public IValue{
    T _value;
public:
    explicit Value(const T &value):
        _value(value)
    {}
    const T& get() const{
        return _value;        
    }
};

Тут все просто. Базовый класс IValue, от него наследуется шаблон Value, в котором можно хранить объект любого типа(кроме тех, которые не поддерживают операцию копирования).

Теперь как будет выглядеть минималистичная реализация Any:

class Any{
    std::unique_ptr<IValue> _value;
public:
    template<class T>
    Any(const T &value):
        _value(new Value<T>(value))
    {}
    template<class T>    
    T get() const{
        Value<T> *value = dynamic_cast<Value<T>*>(_value.get());
        if(!value){
            return T ();
        }
        return value->get();
    }
};

Any хранит указатель типа IValue. Когда мы пытаемся прочитать записанные данные, выполняется понижающее приведение типа:

Value<T> *value = dynamic_cast<Value<T>*>(_value.get());

Если dynamic_cast вернет 0, значит мы пытаемся извлечь неправильный тип.

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

Полный пример

PS: Кстати, в a = "sdfsdf"; нет ничего криминального. По-моему, куда более естественно выглядит реализация в которой так делать можно. Вам придется приложить дополнительные усилия, чтоб реализовать такое ограничение.

READ ALSO
strip: Формат файла не распознан

strip: Формат файла не распознан

Столкнулся с ошибкой после выполнения команды make install в окружении cygwin при установке библиотеки:

258
Печать QTableView с сохранением форматирования

Печать QTableView с сохранением форматирования

Есть QTableView, соединенная с QSqlTableModel, у это таблицы также есть делегаты и прокси модельВозможно ли вывести данную таблицу на печать с сохранением...

310
Кросскомпиляция в Qt

Кросскомпиляция в Qt

Необходимо собрать проект под систему на 32bit arm из системы x86_64 (надеюсь, в определениях не напутал, на интеловский x64 процессор, на целевой...

382