2011-12-20 22 views
6

Utilizando C++, intento crear una clase de contenedor genérica para manejar múltiples tipos de datos. Es un problema común con una variedad de soluciones, pero no he encontrado nada tan ... intuitivo como me he acostumbrado en lenguajes como Python o incluso VB/VBA ...Contenedor genérico para múltiples tipos de datos en C++

Así que aquí está mi escenario:

He creado una clase DataContainer basada en boost :: cualquiera que utilizo para almacenar múltiples tipos de datos de múltiples elementos. Yo uso un mapa declarado:

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

donde DataContainer es una clase que encapsula un objeto del tipo:

std::list<boost::any> 

junto con funciones de confort para la gestión/acceso a la lista.

Sin embargo, al final, todavía estoy obligado a hacer conversiones de tipo fuera del contenedor de datos.

Por ejemplo, si tuviera que almacenar una lista de valores int en el mapa, para acceder a ellos se requieren:

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

prefiero el código impulso estar contenida enteramente dentro de la estructura del contenedor de datos, por lo Sólo necesitaría tipo:

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

o, en el peor de los casos:

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

Por supuesto, yo co ULD enumerar mis tipos de datos y hacer algo como:

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

o escribir get-tipo específico() funciones:

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

El problema con las dos últimas opciones es que son un poco inflexible, que me exigía declarar explícitamente cada tipo que deseo almacenar en el contenedor. La solución any_cast (que he implementado y funciona) supongo que está bien, es solo ... ¿poco elegante? No se. Parece que no debería necesitar emplear la mecánica interna externamente también.

Como lo veo, pasar el valor sin declarar el tipo de valor en la llamada a la función miembro de DataContainer requeriría una solución void * (que es indeseable por razones obvias), y usar una llamada "get()" requieren (por lo que puedo decir) una función de miembro de "plantilla virtual" definida en el nivel de clase base, que, por supuesto, no está permitida.

Como lo tengo, tengo una solución viable, y realmente, mi uso en este caso es lo suficientemente limitado como para que la mayoría de las soluciones funcionen bien. Pero me pregunto si tal vez haya una manera más flexible de administrar un contenedor de datos genérico de varios tipos que este.

+1

echar un vistazo a 'impulso :: variant' también, tal vez esto es lo que estás buscando (?). – Kos

+0

¿Qué pasa con solo un 'std :: map '? –

+0

Además, dado que su 'DataContainer' está bajo su control, ¿por qué no simplemente agrega una plantilla de función' plantilla get_as() {return boost :: any_cast (get()); } 'para envolver el any-cast? Luego puede decir 'm [" abc "] -> get_as ()', como sugirió. Suena bastante simple. –

Respuesta

6

Si se quiere introducir un poco de azúcar para esto:

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

entonces es posible que desee hacer la función get() para devolver un objeto proxy, definido + - de esta manera:

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

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

Entonces esta sintaxis funcionaría:

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

Sin embargo, recomiendo mantener las cosas de forma explícita y sencilla utilizar esa sintaxis:

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

Aquí get no devuelve un objeto proxy con un método de la plantilla, pero es un método propia plantilla (pero hace lo mismo que el operador de conversión plantilla se muestra más arriba).

+0

@JoelGraff Si no estoy dispuesto a mucho, incluso podría salirse con 'get()' en el segundo caso gracias a la deducción automática de parámetros de plantilla. puede considerar almacenar el DataCOntainer directamente para guardar un nivel de direccionamiento indirecto. Un instante de lista en sí mismo sería bastante pequeño (solo la cabeza), los elementos están en el montón de todos modos. – ted

+0

Creo que la deducción automática de parámetros solo funciona para los parámetros, no para el tipo de devolución. – Kos

+0

tiene razón, mire este http://stackoverflow.com/a/2612979/258418, también señala maneras de omitir un tipo a través de soluciones, sin embargo, esto solo es interesante para tipos bastante complejos – ted

1

Hoy he hecho algunos códigos fuente para el propósito que desea. Sé que esta pregunta es muy antigua, pero tal vez este pequeño fragmento de código sea útil para alguien. Se basa principalmente en el impulso: cualquiera.

/* 
* 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_ */ 

Un ejemplo de uso simple:

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);