Универсальный контейнер для нескольких типов данных в 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);