Generic container for multiple data types in C ++

Using C ++, I am trying to create a generic container class to handle several data types. This is a common problem with many solutions, but I did not find anything as ... intuitive as I'm used to in languages ​​like Python or even VB / VBA ...

So here is my scenario:

I built the DataContainer class based on boost :: any, which I use to store multiple data types from multiple elements. I am using a map declared as:

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

where DataContainer is a class that encapsulates an object of type:

 std::list<boost::any> 

along with convenient functions for managing / accessing the list.

However, in the end, I am still forced to do type conversions outside the data container.

For example, if I were to store a list of int values ​​on a map, accessing them would require:

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

I would prefer the boost code to be contained completely inside the data container structure, so I only need a type:

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

or, in the worst case:

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

Of course, I could list my data types and do something like:

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

or write type-specific get () functions:

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

The problem with the last two parameters is that they are somewhat inflexible, so I must explicitly specify each type that I want to store in the container. The any_cast solution (which I implemented and works), I suppose, is fine, is it just ... inelegant? I do not know. It seems I also do not need to use external mechanics outside.

As I see it, passing a value without declaring a value type in a call to the DataContainer member function will require a void * solution (which is undesirable for obvious reasons), and using the get () call will require (as far as I can tell) the member function of the "virtual template" defined at the base class level, which, of course, is not allowed.

Be that as it may, I have a workable solution, and indeed, my use in this case is quite limited in scope, that most of any solutions will work well. But I'm wondering if there could be a more flexible way to manage a universal, multi-type data container than this.

+6
source share
2 answers

If you want to introduce some sugar for this:

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

then you might want to make the get() function to return the proxy object defined by + - as follows:

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

Then this syntax will work:

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

However, I recommend keeping things explicit and just using this syntax:

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

Here, get does not return a proxy object using the template method, but the template method itself (but it does the same as the template conversion operator shown above).

+6
source

Today I made some source code for your purpose. I know this question is so old, but maybe this piece of code is useful for someone. This is mainly based on 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_ */ 

A simple use case:

 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); 
+1
source

Source: https://habr.com/ru/post/904126/


All Articles