Универсальный контейнер для нескольких типов данных в C++

используя C++, я пытаюсь создать общий класс контейнера для обработки нескольких типов данных. Это обычная проблема с различными решениями,но я ничего не нашел... интуитивный, поскольку я привык к таким языкам, как Python или даже VB/VBA...

вот мой сценарий:

Я построил класс DataContainer на основе boost::любой, который я использую для хранения нескольких типов данных нескольких элементов. Я использую карту, объявленную как:

std::map<std::string, DataContainer* (or DataContainerBase*)>

где DataContainer-это класс, который инкапсулирует объект типа:

std::list<boost::any>

наряду с удобными функциями для управления / доступ к списку.

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

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

int value = boost::any_cast<int>(map["myValue"]->get());

Я бы предпочел, чтобы код boost содержался полностью в структуре контейнера данных, поэтому я понадобится только тип:

int value = map["myValue"]->get();

или, в худшем случае:

int value = map["myValue"]->get<int>();

конечно, я мог бы перечислить мои типы данных и сделать что-то вроде:

int value = map["myValue"]->get( TYPE_INT );

или напишите специфические для типа функции get ():

getInt(), getString(), getBool() ... 

проблема с последними двумя вариантами заключается в том, что они несколько негибки, требуя от меня явно объявить каждый тип, который я хочу сохранить в контейнере. Решение any_cast (которое я реализовал и работает), я полагаю, в порядке, это просто... неэлегантно? Не знаю. Кажется, мне не нужно использовать внутреннюю механику и снаружи.

как я вижу, передача значения без объявления типа значения в вызове функции-члена DataContainer потребует решения void* (что нежелательно по очевидным причинам), а использование вызова "get ()" потребует (насколько я могу судить) функции-члена "виртуального шаблона", определенной на уровне базового класса, что, конечно, не разрешено.

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

2 ответов


если вы хотите ввести сахар для этого:

int value = boost::any_cast<int>(map["myValue"]->get());

тогда вы можете сделать get() функция для возврата прокси-объекта, определенного + - вот так:

struct Proxy {
    boost::any& value;
    Proxy(boost::any& value) : value(value) {}

    template<typename T>
    operator T() {
        return boost::any_cast<T>(value);
    }
};

затем этот синтаксис будет работать:

int value = map["myValue"]->get();
// returns a proxy which gets converted by any_cast<int>

однако я рекомендую держать вещи явными и просто использовать этот синтаксис:

int value = map["myValue"]->get<int>();

здесь get не возвращает прокси-объект с шаблонным методом, но является шаблонным методом (но делает то же самое, что и шаблон оператор преобразования, показанный выше).


сегодня я сделал некоторый исходный код для цели, которую вы хотите. Я знаю, что этот вопрос настолько стар, но, возможно, этот маленький кусочек кода кому-то полезен. Он в основном основан на boost: any.

/*
 * AnyValueMap.hpp
 *
 *  Created on: Jun 3, 2013
 *      Author: alvaro
 */

#ifndef ANYVALUEMAP_HPP_
#define ANYVALUEMAP_HPP_

#include <map>
#include <boost/any.hpp>

using namespace std;

template <class T>
class AnyValueMap {

public:
    AnyValueMap(){}

    virtual ~AnyValueMap(){}

private:
    map<T, boost::any> container_;

    typedef typename map<T, boost::any>::iterator map_iterator;
    typedef typename map<T, boost::any>::const_iterator map_const_iterator;

public:

    bool containsKey(const T key) const
    {
        return container_.find(key) != container_.end();
    }

    bool remove(const T key)
    {
        map_iterator it = container_.find(key);
        if(it != container_.end())
        {
            container_.erase(it);
            return true;
        }
        return false;
    }

    template <class V>
    V getValue(const T key, const V defaultValue) const
    {
        map_const_iterator it = container_.find(key);
        if(it != container_.end())
        {
            return boost::any_cast<V>(it->second);
        }
        return defaultValue;
    }

    template <class V>
    V getValue(const T key) const
    {
        return boost::any_cast<V>(container_.at(key));
    }

    template <class V>
    void setValue(const T key, const V value)
    {
        container_[key] = value;
    }
};

#endif /* ANYVALUEMAP_HPP_ */

простой пример использования:

AnyValueMap<unsigned long> myMap;
myMap.setValue<double>(365, 1254.33);
myMap.setValue<int>(366, 55);
double storedDoubleValue = myMap.getValue<double>(365);
int storedIntValue = myMap.getValue<int>(366);