Dynamic class parametrization in C++

Dec. 8, 2017

Despite the fact that it’s not the best coding style sometimes the design is much more elegant if you could define class properties dynamically.

What do I mean by that? No member variables which might be defined at compile time and can be accessed in normal style but aggregated data of arbitrary type which has to be accessed through a special “key”. The disadvantage is of course the fact that you hardly depend on runtime type information. You do not have any type safety during compilation as the types aren’t fixed at compile time.

The design goal was to create something like the following:

MyClass my_class;
std::vector<int> a_vector;
int an_int = 0;

my_class.add("a_vector",a_vector);
my_class.add("an_int",an_int);

std::cout << my_class.get<std::vector>("a_vector") << std::endl;
std::cout << my_class.get<int>("an_int") << std::endl;

The best way for me was to implement this with the help of boost::any and here is my suggestion:

#include <map>
#include <iostream>
#include <string>

#include <boost/any.hpp>

//------------------------------------------------------------------
struct AnyMap {
  void addAnyPair( const std::string& key , boost::any& value );

  template<typename T>
  T& get( const std::string key ) {
    return( boost::any_cast<T&>(map_[key]) );
  }

  std::map<const std::string, boost::any> map_;
};

void AnyMap::addAnyPair( const std::string& key , boost::any& value ) {
  map_.insert( std::make_pair( key, value ) );
}

//------------------------------------------------------------------
struct TestType {
  TestType():
    value_(3) {}

  void print() {
    std::cout << "We can call methods by reference! - value = " << value_ << std::endl;
  }

  int value_;
};

//------------------------------------------------------------------
int main()
{
  std::cout << "Dynamic class parametrization!" << std::endl;
  AnyMap any_map;

  std::string key = "key_1";
  boost::any value = 23;
  any_map.addAnyPair( key,value);
  key = "key_2";
  value = 45.3;
  any_map.addAnyPair( key,value);

  std::cout << "Retrieve int:    " << any_map.get<int>("key_1") << std::endl;
  std::cout << "Retrieve double: " << any_map.get<double>("key_2") << std::endl;
  any_map.get<int>("key_1") = 35;
  std::cout << "Modified value:  " << any_map.get<int>("key_1") << std::endl;

  key="key_3";
  boost::any test_type = TestType();
  any_map.addAnyPair(key,test_type);
  any_map.get<TestType>("key_3").print();
  any_map.get<TestType>("key_3").value_ = 5;
  any_map.get<TestType>("key_3").print();

  key="key_4";
  char* test_pointer = new char;
  *test_pointer = 'A';
  boost::any test_any_pointer(test_pointer);
  any_map.addAnyPair(key,test_any_pointer);
  std::cout << "Print pointer content: " << *any_map.get<char*>(key) << std::endl;
}