C++ type punning with classes - c++

I am writing some C++ code which wraps the std::unordered_map type, where I want to hide the underlying type and present it as another type. More specifically, I want to wrap the std::pair from the std::unordered_map with another type. For the sake of argument, lets suppose the wrapper looks like this...
template <typename ActualT >
class wrapper final
{
private:
ActualT actual_;
public:
//Some constructors...
typename ActualT::first_type & get_first()
{
return actual_.first;
}
typename ActualT::second_type & get_second()
{
return actual_.second;
}
};
My reasoning is that since the wrapper class only has a member which is the exact type which it is wrapping, converting a reference from the original type to the wrapper type should be fine, but the type compatibility for structs states that the members should have the same type and name for the types to be compatible. Would using type-punning in this fashion potentially cause undefined behaviour or alignment issues?
using my_map = std::unordered_map < int, int >;
my_map m;
//Do some inserts...
reinterpret_cast<wrapper<typename my_map::value_type>&>(*m.find(10)).get_second() = 1.0;
I want client code to be allowed to access the entries of a map without knowing about the pair which is returned by the map. I also want to write a custom forward iterator, hence I need to return a reference to the entry. Would converting the reference to the pair to a reference to a class which act as a wrapper be considered dangerous?
Is there perhaps a better approach to accomplishing this?

This absolutely is undefined behaviour.
Seriously rethink your priorities.
Some free functions of the form
const my_map::key_type & MeaningfulNameHere(my_map::reference)
will go a long way to giving you meaningful names.
If you must wrap the standard library with different names, just use a non-explicit constructor, and store references.
template <typename Map>
class entry final
{
private:
typename Map::reference ref;
public:
entry(Map::reference ref) : ref(ref) {}
const typename Map::key_type & key()
{
return ref.first;
}
typename Map::mapped_type & value()
{
return ref.second;
}
};
If you really need the iterator to dereference to entry you can. But you can just implicitly instantiate entrys from the Map::references returned by Map::iterator::operator*, you don't need a custom iterator.
template <typename Map>
class entry_iterator
{
private:
typename Map::iterator it;
entry<Map> entry;
public:
entry<Map>& operator*() { return entry; }
entry_iterator operator++() { ++it; entry = *it; return *this; }
// etc
}

So you could clean this up, but I wouldn't suggest it:
#include <unordered_map>
#include <iostream>
using namespace std;
template <class Key, class Value>
class wrapper
{
public:
explicit wrapper(std::pair<const Key, Value>& kvp)
: _key{kvp.first}
, _value{kvp.second}
{}
const Key& key() const { return _key; }
Value& value() { return _value; }
private:
const Key& _key;
Value& _value;
};
int main()
{
unordered_map<int,int> m;
m[1] = 1;
m[3] = 3;
auto it = m.find(1);
wrapper w{*it};
w.value() = 30;
std::cout << w.key() << " -> " << w.value() << '\n';
}
The above effectively hides the pair from users of your class. It doesn't deal with exceptions (find() returning end() for example), and makes no guarantees about lifetimes. It's marginally better than what you have because it doesn't require a reinterpret_cast to an unrelated type.
However, map, unordered_map, set, etc. storing returning iterators as pairs is just part of library -- it's the canonical form and I don't see the benefit of shielding people from it.

Related

Can static polymorphism (templates) be used despite type erasure?

Having returned relatively recently to C++ after decades of Java, I am currently struggling with a template-based approach to data conversion for instances where type erasure has been applied. Please bear with me, my nomenclature may still be off for C++-natives.
This is what I am trying to achieve:
Implement dynamic variables which are able to hold essentially any value type
Access the content of those variables using various other representations (string, ints, binary, ...)
Be able to hold variable instances in containers, independent of their value type
Convert between variable value and representation using conversion functions
Be able to introduce new representations just by providing new conversion functions
Constraints: use only C++-11 features if possible, no use of libraries like boost::any etc.
A rough sketch of this might look like this:
#include <iostream>
#include <vector>
void convert(const std::string &f, std::string &t) { t = f; }
void convert(const int &f, std::string &t) { t = std::to_string(f); }
void convert(const std::string &f, int &t) { t = std::stoi(f); }
void convert(const int &f, int &t) { t = f; }
struct Variable {
virtual void get(int &i) = 0;
virtual void get(std::string &s) = 0;
};
template <typename T> struct VariableImpl : Variable {
T value;
VariableImpl(const T &v) : value{v} {};
void get(int &i) { convert(value, i); };
void get(std::string &s) { convert(value, s); };
};
int main() {
VariableImpl<int> v1{42};
VariableImpl<std::string> v2{"1234"};
std::vector<Variable *> vars{&v1, &v2};
for (auto &v : vars) {
int i;
v->get(i);
std::string s;
v->get(s);
std::cout << "int representation: " << i <<
", string representation: " << s << std::endl;
}
return 0;
}
The code does what it is supposed to do, but obvoiusly I would like to get rid of Variable::get(int/std::string/...) and instead template them, because otherwise every new representation requires a definition and an implementation with the latter being exactly the same as all the others.
I've played with various approaches so far, like virtual templated, methods, applying the CRDT with intermediate type, various forms of wrappers, yet in all of them I get bitten by the erased value type of VariableImpl. On one hand, I think there might not be a solution, because after type erasure, the compiler cannot possibly know what templated getters and converter calls it must generate. On the other hand I think i might be missing something really essential here and there should be a solution despite the constraints mentioned above.
This is a classical double dispatch problem. The usual solution to this problem is to have some kind of dispatcher class with multiple implementations of the function you want to dispatch (get in your case). This is called the visitor pattern. The well-known drawback of it is the dependency cycle it creates (each class in the hierarchy depends on all other classes in the hierarchy). Thus there's a need to revisit it each time a new type is added. No amount of template wizardry eliminates it.
You don't have a specialised Visitor class, your Variable serves as a Visitor of itself, but this is a minor detail.
Since you don't like this solution, there is another one. It uses a registry of functions populated at run time and keyed on type identification of their arguments. This is sometimes called "Acyclic Visitor".
Here's a half-baked C++11-friendly implementation for your case.
#include <map>
#include <vector>
#include <typeinfo>
#include <typeindex>
#include <utility>
#include <functional>
#include <string>
#include <stdexcept>
struct Variable
{
virtual void convertValue(Variable& to) const = 0;
virtual ~Variable() {};
virtual std::type_index getTypeIdx() const = 0;
template <typename K> K get() const;
static std::map<std::pair<std::type_index, std::type_index>,
std::function<void(const Variable&, Variable&)>>
conversionMap;
template <typename T, typename K>
static void registerConversion(K (*fn)(const T&));
};
template <typename T>
struct VariableImpl : Variable
{
T value;
VariableImpl(const T &v) : value{v} {};
VariableImpl() : value{} {}; // this is needed for a declaration of
// `VariableImpl<K> below
// It can be avoided but it is
// a story for another day
void convertValue(Variable& to) const override
{
auto typeIdxFrom = getTypeIdx();
auto typeIdxTo = to.getTypeIdx();
if (typeIdxFrom == typeIdxTo) // no conversion needed
{
dynamic_cast<VariableImpl<T>&>(to).value = value;
}
else
{
auto fcnIter = conversionMap.find({getTypeIdx(), to.getTypeIdx()});
if (fcnIter != conversionMap.end())
{
fcnIter->second(*this, to);
}
else
throw std::logic_error("no conversion");
}
}
std::type_index getTypeIdx() const override
{
return std::type_index(typeid(T));
}
};
template <typename K> K Variable::get() const
{
VariableImpl<K> vk;
convertValue(vk);
return vk.value;
}
template <typename T, typename K>
void Variable::registerConversion(K (*fn)(const T&))
{
// add a mutex if you ever spread this over multiple threads
conversionMap[{std::type_index(typeid(T)), std::type_index(typeid(K))}] =
[fn](const Variable& from, Variable& to) {
dynamic_cast<VariableImpl<K>&>(to).value =
fn(dynamic_cast<const VariableImpl<T>&>(from).value);
};
}
Now of course you need to call registerConversion e.g. at the beginning of main and pass it each conversion function.
Variable::registerConversion(int_to_string);
Variable::registerConversion(string_to_int);
This is not ideal, but hardly anything is ever ideal.
Having said all that, I would recommend you revisit your design. Do you really need all these conversions? Why not pick one representation and stick with it?
Implement dynamic variables which are able to hold essentially any value type
Be able to hold variable instances in containers, independent of their value type
These two requirements are quite challenging on its own. The class templates don't really encourage inheritance, and you already did the right thing to hold what you asked for: introduced a common base class for the class template, which you can later refer to in order to store pointers of the said type in a collection.
Access the content of those variables using various other representations (string, ints, binary, ...)
Be able to introduce new representations just by providing new conversion functions
This is where it breaks. Function templates assume common implementation for different types, while inheritance assumes different implementation for the same types.
You goal is to introduce different implementation for different types, and in order to make your requirements viable you have to switch to one of those two options instead (or put up with a number of functions for each case which you have already introduced yourself)
Edit:
One of the strategies you may employ to enforce inheritance approach is generalisation of the arguments to the extent where they can be used interchangeably by the abstract interface. E.g. you may wrap the converting arguments inside of a union like this:
struct Variable {
struct converter_type {
enum { INT, STRING } type;
union {
int* m_int;
std::string* m_string;
};
};
virtual void get(converter_type& var) = 0;
virtual ~Variable() = default;
};
And then take whatever part of it inside of the implementation:
void get(converter_type& var) override {
switch (var.type) {
case converter_type::INT:
convert(value, var.m_int);
break;
case converter_type::STRING:
convert(value, var.m_string);
break;
}
}
To be honest I don't think this is a less verbose approach compared to just having a number of functions for each type combination, but i think you got the idea that you can just wrap your arguments somehow to cement the abstract class interface.
Implement std::any. It is similar to boost::any.
Create a conversion dispatcher based off typeids. Store your any alongside the conversion dispatcher.
"new conversion functions" have to be passed to the dispatcher.
When asked to convert to a type, pass that typeid to the dispatcher.
So we start with these 3 types:
using any = std::any; // implement this
using converter = std::function<any(any const&)>;
using convert_table = std::map<std::type_index, converter>;
using convert_lookup = convert_table(*)();
template<class T>
convert_table& lookup_convert_table() {
static convert_table t;
return t;
}
struct converter_any: any {
template<class T,
typename std::enable_if<
!std::is_same<typename std::decay<T>::type, converter_any>::value, bool
>::type = true
>
converter_any( T&& t ):
any(std::forward<T>(t)),
table(&lookup_convert_table<typename std::decay<T>::type>())
{}
converter_any(converter_any const&)=default;
converter_any(converter_any &&)=default;
converter_any& operator=(converter_any const&)=default;
converter_any& operator=(converter_any&&)=default;
~converter_any()=default;
converter_any()=default;
convert_table const* table = nullptr;
template<class U>
U convert_to() const {
if (!table)
throw 1; // make a better exception than int
auto it = table->find(typeid(U));
if (it == table->end())
throw 2; // make a better exception than int
any const& self = *this;
return any_cast<U>((it->second)(self));
}
};
template<class Dest, class Src>
bool add_converter_to_table( Dest(*f)(Src const&) ) {
lookup_convert_table<Src>()[typeid(Dest)] = [f](any const& s)->any {
Src src = std::any_cast<Src>(s);
auto r = f(src);
return r;
};
return true;
}
now your code looks like:
const bool bStringRegistered =
add_converter_to_table(+[](std::string const& f)->std::string{ return f; })
&& add_converter_to_table(+[](std::string const& f)->int{ return std::stoi(f); });
const bool bIntRegistered =
add_converter_to_table(+[](int const& i)->int{ return i; })
&& add_converter_to_table(+[](int const& i)->std::string{ return std::to_string(i); });
int main() {
converter_any v1{42};
converter_any v2{std::string("1234")};
std::vector<converter_any> vars{v1, v2}; // copies!
for (auto &v : vars) {
int i = v.convert_to<int>();
std::string s = v.convert_to<std::string>();
std::cout << "int representation: " << i <<
", string representation: " << s << std::endl;
}
}
live example.
...
Ok, what did I do?
I used any to be a smart void* that can store anything. Rewriting this is a bad idea, use someone else's implementation.
Then, I augmented it with a manually written virtual function table. Which table I add is determined by the constructor of my converter_any; here, I know the type stored, so I can store the right table.
Typically when using this technique, I'd know what functions are in there. For your implementation we do not; so the table is a map from the type id of the destination, to a conversion function.
The conversion function takes anys and returns anys -- again, don't repeat this work. And now it has a fixed signature.
To add support for a type, you independently register conversion functions. Here, my conversion function registration helper deduces the from type (to determine which table to register it in) and the destination type (to determine which entry in the table), and then automatically writes the any boxing/unboxing code for you.
...
At a higher level, what I'm doing is writing my own type erasure and object model. C++ has enough power that you can write your own object models, and when you want features that the default object model doesn't solve, well, roll a new object model.
Second, I'm using value types. A Java programmer isn't used to value types having polymorphic behavior, but much of C++ works much better if you write your code using value types.
So my converter_any is a polymorphic value type. You can store copies of them in vectors etc, and it just works.

In C++, how do you create a map of int and iterator of current map? [duplicate]

Is there a way to declare a std::map whose value type is an iterator to itself?
map<string, map<string, (#)>::iterator> myMap;
The above code snippet wouldn't work because the iterator type needs to know the second template argument, marked as (#). (which would be itself).
The intent is to avoid doing unnecessary find operations to access the element that is pointed to by another element — as opposed to using map<string, string>.
Such definition is not possible, since the value type and the iterator type would be mutually infinitely recursive.
It is possible to work around this using a bit of indirection. It is even possible to avoid the dynamic allocation of std::any, and the fact that std::map<K,V> is undefined unless V is complete.
But the solution is a bit tricky, and relies on some assumptions which are reasonable, but not specified by the standard. See comments in the implementation. The main trick is to defer definition of a member variable type until after definition of the enveloping class. This is achieved by reusing raw storage.
Usage first:
int main()
{
Map map;
auto [it, _] = map.emplace("first", iter_wrap{});
map.emplace("maps to first", conv::wrap(it));
// erase first mapping by only looking
// up the element that maps to it
map.erase(conv::it(map.find("maps to first")));
}
Definition
struct NoInitTag {} noInitTag;
class iter_wrap
{
public:
iter_wrap();
~iter_wrap();
iter_wrap(const iter_wrap&);
iter_wrap(iter_wrap&&);
const iter_wrap& operator=(const iter_wrap&);
const iter_wrap& operator=(iter_wrap&&);
private:
// We rely on assumption that all map iterators have the same size and alignment.
// Compiler should hopefully warn if our allocation is insufficient.
using dummy_it = std::map<int, int>::iterator;
static constexpr auto it_size = sizeof(dummy_it);
static constexpr auto it_align = alignof(dummy_it);
alignas(it_align) std::byte store[it_size];
explicit iter_wrap(NoInitTag){}
friend struct conv;
};
using Map = std::map<std::string, iter_wrap>;
using It = Map::iterator;
struct conv {
static constexpr It&
it(iter_wrap&& wrap) noexcept {
return *std::launder(reinterpret_cast<It*>(wrap.store));
}
static constexpr const It&
it(const iter_wrap& wrap) noexcept {
return *std::launder(reinterpret_cast<const It*>(wrap.store));
}
template<class It>
static const iter_wrap
wrap(It&& it) {
iter_wrap iw(noInitTag);
create(iw, std::forward<It>(it));
return iw;
}
template<class... Args>
static void
create(iter_wrap& wrap, Args&&... args) {
new(wrap.store) It(std::forward<Args>(args)...);
}
static constexpr void
destroy(iter_wrap& wrap) {
it(wrap).~It();
}
};
iter_wrap::iter_wrap() {
conv::create(*this);
}
iter_wrap::iter_wrap(const iter_wrap& other) {
conv::create(*this, conv::it(other));
}
iter_wrap::iter_wrap(iter_wrap&& other) {
conv::create(*this, std::move(conv::it(other)));
}
const iter_wrap& iter_wrap::operator=(const iter_wrap& other) {
conv::destroy(*this);
conv::create(*this, conv::it(other));
return *this;
}
const iter_wrap& iter_wrap::operator=(iter_wrap&& other) {
conv::destroy(*this);
conv::create(*this, std::move(conv::it(other)));
return *this;
}
iter_wrap::~iter_wrap() {
conv::destroy(*this);
}
Old answer; This assumed that it was not an important feature to avoid lookups while traversing stored mappings.
It appears that the data structure that you attempt to represent is a set of keys (strings), where each key maps to another key of the set. Easier way to represent that is to separate those two aspects:
using Set = std::set<std::string>;
using Map = std::map<Set::iterator, Set::iterator>;
Note that these two data structures do not automatically stay in sync. An element added to the set doesn't automatically have a mapping to another, and an element erased from the set leaves dangling iterators to the map. As such, it would be wise to write a custom container class that enforces the necessary invariants.
Only through type erasure. For example, you could use std::any
std::map<std::string, std::any> myMap;
auto inserted = myMap.emplace("foo", std::any());
// how it can be populated:
inserted.first->second = inserted.first;
using it_type = decltype(myMap.begin());
// how values can be extracted:
auto it = std::any_cast<it_type>(myMap["foo"]);
EDIT: The following also seems to work (clang-7.0.0 and gcc-8.2), but it is illegal (basically std::map does not specify that incomplete types are allowed):
struct Iter;
using Map = std::map<std::string, Iter>;
struct Iter {
Map::iterator it;
};

C++ map -- Self referencing iterator

Is there a way to declare a std::map whose value type is an iterator to itself?
map<string, map<string, (#)>::iterator> myMap;
The above code snippet wouldn't work because the iterator type needs to know the second template argument, marked as (#). (which would be itself).
The intent is to avoid doing unnecessary find operations to access the element that is pointed to by another element — as opposed to using map<string, string>.
Such definition is not possible, since the value type and the iterator type would be mutually infinitely recursive.
It is possible to work around this using a bit of indirection. It is even possible to avoid the dynamic allocation of std::any, and the fact that std::map<K,V> is undefined unless V is complete.
But the solution is a bit tricky, and relies on some assumptions which are reasonable, but not specified by the standard. See comments in the implementation. The main trick is to defer definition of a member variable type until after definition of the enveloping class. This is achieved by reusing raw storage.
Usage first:
int main()
{
Map map;
auto [it, _] = map.emplace("first", iter_wrap{});
map.emplace("maps to first", conv::wrap(it));
// erase first mapping by only looking
// up the element that maps to it
map.erase(conv::it(map.find("maps to first")));
}
Definition
struct NoInitTag {} noInitTag;
class iter_wrap
{
public:
iter_wrap();
~iter_wrap();
iter_wrap(const iter_wrap&);
iter_wrap(iter_wrap&&);
const iter_wrap& operator=(const iter_wrap&);
const iter_wrap& operator=(iter_wrap&&);
private:
// We rely on assumption that all map iterators have the same size and alignment.
// Compiler should hopefully warn if our allocation is insufficient.
using dummy_it = std::map<int, int>::iterator;
static constexpr auto it_size = sizeof(dummy_it);
static constexpr auto it_align = alignof(dummy_it);
alignas(it_align) std::byte store[it_size];
explicit iter_wrap(NoInitTag){}
friend struct conv;
};
using Map = std::map<std::string, iter_wrap>;
using It = Map::iterator;
struct conv {
static constexpr It&
it(iter_wrap&& wrap) noexcept {
return *std::launder(reinterpret_cast<It*>(wrap.store));
}
static constexpr const It&
it(const iter_wrap& wrap) noexcept {
return *std::launder(reinterpret_cast<const It*>(wrap.store));
}
template<class It>
static const iter_wrap
wrap(It&& it) {
iter_wrap iw(noInitTag);
create(iw, std::forward<It>(it));
return iw;
}
template<class... Args>
static void
create(iter_wrap& wrap, Args&&... args) {
new(wrap.store) It(std::forward<Args>(args)...);
}
static constexpr void
destroy(iter_wrap& wrap) {
it(wrap).~It();
}
};
iter_wrap::iter_wrap() {
conv::create(*this);
}
iter_wrap::iter_wrap(const iter_wrap& other) {
conv::create(*this, conv::it(other));
}
iter_wrap::iter_wrap(iter_wrap&& other) {
conv::create(*this, std::move(conv::it(other)));
}
const iter_wrap& iter_wrap::operator=(const iter_wrap& other) {
conv::destroy(*this);
conv::create(*this, conv::it(other));
return *this;
}
const iter_wrap& iter_wrap::operator=(iter_wrap&& other) {
conv::destroy(*this);
conv::create(*this, std::move(conv::it(other)));
return *this;
}
iter_wrap::~iter_wrap() {
conv::destroy(*this);
}
Old answer; This assumed that it was not an important feature to avoid lookups while traversing stored mappings.
It appears that the data structure that you attempt to represent is a set of keys (strings), where each key maps to another key of the set. Easier way to represent that is to separate those two aspects:
using Set = std::set<std::string>;
using Map = std::map<Set::iterator, Set::iterator>;
Note that these two data structures do not automatically stay in sync. An element added to the set doesn't automatically have a mapping to another, and an element erased from the set leaves dangling iterators to the map. As such, it would be wise to write a custom container class that enforces the necessary invariants.
Only through type erasure. For example, you could use std::any
std::map<std::string, std::any> myMap;
auto inserted = myMap.emplace("foo", std::any());
// how it can be populated:
inserted.first->second = inserted.first;
using it_type = decltype(myMap.begin());
// how values can be extracted:
auto it = std::any_cast<it_type>(myMap["foo"]);
EDIT: The following also seems to work (clang-7.0.0 and gcc-8.2), but it is illegal (basically std::map does not specify that incomplete types are allowed):
struct Iter;
using Map = std::map<std::string, Iter>;
struct Iter {
Map::iterator it;
};

Generic design mixed with curiously recurring template pattern. C++

Consider this kind of problem. I have a Base class and three classes derived from Base. For instance: DerivedA, DerivedB and DerivedC. Each derived class has its unique container. Hence DerivedA has std::vector<int>, DerivedB has std::set<int> and DerivedC has std::map<int, std::string>. And I want an interface in Base to access the container of derived class on which it is currently pointed to.
Base* d1 = new DerivedA;
for(std::vector<int>::iterator iter = d1->begin(); iter != d1->end(); ++iter)
{
//processing
}
I tried to wrap each container to separate class and keep a pointer of their base
in the Base class.
class CollA;
template<class T>
class traits;
template<>
class traits<CollA>
{
public:
typedef vector<int> container;
};
template<class T>
class Coll
{
public:
typedef typename traits<T>::container container;
typename container::iterator begin() const
{
}
};
class CollA : public Coll<CollA>
{
typedef traits<CollA>::container container;
public:
container::iterator begin()
{
return V.begin();
}
private:
vector<int> V;
};
class Base
{
public:
Base()
{
}
// what to do here? I must keep a pointer to Coll; But Coll itself is a template
};
Suggest me something. I am kind of lost in this horrible design.
In order to do what you want, you need to define a common type of iterator that can be returned from the different begin() and end() overrides in the derived classes.
Before that, of course, you need to decide what exactly you want that iterator to do, as Yakk explained in his comment. For starters, you need to decide what value_type will result from indirecting through such an iterator. The only common type that I can think of given your three different containers is const int, as keys in std::maps are const and std::set iterators are const iterators (since the elements are keys themselves). So, when iterating using the common iterator type, you'll only be able to observe the ints in there.
Now, the iterator implementation will need to call different code (at runtime) depending on the derived class from which it originated. This is a typical use case for type erasure. When done properly, this would allow you to wrap any kind of iterator, as long as it supports the interface you need. In your case however, you may not need to go that far, since I suppose you know the full set of containers you need to support, so the set of iterator types is well known and bounded as well.
This means you can use a boost::variant to store the wrapped iterator. This should be more efficient than a full type erasure solution, since it avoids some internal virtual function calls and possibly some heap allocations (unless the type erasure solution can use some kind of small object optimization, which is fairly possible for iterators, but is even more complicated to implement).
Here's a skeleton implementation of such an iterator, together with the class hierarchy using it and some simple test code. Note that I've only implemented the basic iterator functionality that's needed to make your loop work.
#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <iterator>
#include "boost/variant.hpp"
//Helper function object types to implement each operator on the variant iterator.
struct indirection_visitor : boost::static_visitor<const int&>
{
const int& operator()(std::vector<int>::iterator i) const { return *i; }
const int& operator()(std::set<int>::iterator i) const { return *i; }
const int& operator()(std::map<int, std::string>::iterator i) const { return i->first; }
};
struct prefix_increment_visitor : boost::static_visitor<>
{
template<typename I> void operator()(I& i) const { ++i; }
};
//The iterator itself.
//It should probably hide the internal variant, in which case the non-member operators
//should be declared as friends.
struct var_iterator : std::iterator<std::bidirectional_iterator_tag, const int>
{
var_iterator() { }
template<typename I> var_iterator(I i) : it(i) { }
boost::variant<std::vector<int>::iterator, std::set<int>::iterator, std::map<int, std::string>::iterator> it;
const int& operator*() { return boost::apply_visitor(indirection_visitor(), it); }
var_iterator& operator++()
{
boost::apply_visitor(prefix_increment_visitor(), it);
return *this;
}
};
inline bool operator==(var_iterator i1, var_iterator i2) { return i1.it == i2.it; }
inline bool operator!=(var_iterator i1, var_iterator i2) { return !(i1 == i2); }
//Here's the class hierarchy.
//We use CRTP only to avoid copying and pasting the begin() and end() overrides for each derived class.
struct Base
{
virtual var_iterator begin() = 0;
virtual var_iterator end() = 0;
};
template<typename D> struct Base_container : Base
{
var_iterator begin() override { return static_cast<D*>(this)->container.begin(); }
var_iterator end() override { return static_cast<D*>(this)->container.end(); }
};
struct DerivedA : Base_container<DerivedA>
{
std::vector<int> container;
};
struct DerivedB : Base_container<DerivedB>
{
std::set<int> container;
};
struct DerivedC : Base_container<DerivedC>
{
std::map<int, std::string> container;
};
//Quick test.
void f(Base* bp)
{
for(auto iter = bp->begin(); iter != bp->end(); ++iter)
{
std::cout << *iter << ' ';
}
std::cout << '\n';
//We have enough to make range-based for work too.
for(auto i : *bp)
std::cout << i << ' ';
std::cout << '\n';
}
int main()
{
DerivedA da;
da.container = {1, 2, 3};
f(&da);
DerivedB db;
db.container = {4, 5, 6};
f(&db);
DerivedC dc;
dc.container = std::map<int, std::string>{{7, "seven"}, {8, "eight"}, {9, "nine"}};
f(&dc);
}
Implementation notes:
As mentioned above, this is not a complete bidirectional iterator; I chose that tag as the most powerful common iterator among your container types.
I compiled and (superficially) tested the code in Clang 3.6.0 and GCC 5.1.0 in C++11 mode, and in Visual C++ 2013, using boost 1.58.0.
The code works in C++14 mode as well in the compilers above (and also in Visual C++ 2015 CTP6), but needs a small change because of a bug in boost 1.58 (I'll have to report that), otherwise you'll get an ambiguity error. You need to remove the base class of indirection_visitor and let the return type of this visitor be determined automatically. This only works in C++14, as it uses decltype(auto) internally, and it's this new code that causes the ambiguity. Earlier versions of boost don't have this problem, but don't have autodetection of return types either.
In C++14 mode and boost 1.58, you can use generic lambdas to implement simple visitors like prefix_increment_visitor, which makes the code more straightforward.
I removed the comparison visitors from my first version of the code, as boost::variant already provides a default equality operator and it's enough for this case (the example is long enough as it is).
You can add const in the required places to get true const iterator behaviour if needed (qualify begin() and end(), use static_cast<const D*> in CRTP, declare the variant to contain const_iterators, adjust the visitor).
You can, of course, implement some sort of poor-man's variant and avoid using boost, but boost::variant makes everything much easier, cleaner and safer.

Generic container for multiple data types in C++

Using C++, I'm trying to create a generic container class to handle multiple data types. It's a common problem with a variety of solutions, but I've found nothing as... intuitive as I've grown accustomed to in languages like Python or even VB/VBA...
So here's my scenario:
I've built a DataContainer class based on boost::any which I use to store multiple data types of multiple elements. I use a map declared as:
std::map<std::string, DataContainer* (or DataContainerBase*)>
where DataContainer is a class that encapsulates an object of the type:
std::list<boost::any>
along with convenience functions for managing / accessing the list.
However, in the end, I'm still forced to do type conversions outside the data container.
For example, if I were to store a list of int values in the map, accessing them would require:
int value = boost::any_cast<int>(map["myValue"]->get());
I'd rather the boost code be contained entirely within the data container structure, so I would only need type:
int value = map["myValue"]->get();
or, worst-case:
int value = map["myValue"]->get<int>();
Of course, I could enumerate 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 options is that they are somewhat inflexible, requiring me to declare explicitly each type I wish to store in the container. The any_cast solution (which I have implemented and works) I suppose is fine, it's just... inelegant? I dunno. It seems I shouldn't need to employ the internal mechanics externally as well.
As I see it, passing the value without declaring the value type in the call to the DataContainer member function would require a void* solution (which is undesirable for obvious reasons), and using a "get()" call would require (so far as I can tell) a "virtual template" member function defined at the base class level, which, of course, isn't allowed.
As it is, I have a workable solution, and really, my use in this case is limited enough in scope that most any solutions will work well. But I am wondering if perhaps there's a more flexible way to manage a generic, multi-type data container than this.
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 a proxy object, defined +- like this:
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 would work:
int value = map["myValue"]->get();
// returns a proxy which gets converted by any_cast<int>
However I recommend to keep things explicit and just use that syntax:
int value = map["myValue"]->get<int>();
Here get doesn't return a proxy object with a template method, but is a template method itself (but does the same as the template conversion operator shown above).
Today I have done some source code for the purpose you want. I know that this question is so old, but maybe this little piece of code is helpful for someone. It 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 usage example:
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);