Sassa
Sassa

Reputation: 2023

single get method which return different type variables

I want to make a class that will have a single get template method which will receive an std::string to find in a std::map the right variable and return it.

The std::map should store any type of variable, so I used boost::any, so far the std::map looks like that:

std::map<std::string, boost::any> variables_;

for the get function, I tried something like that:

template <typename T>
T get(std::string& parameter)
{
    return variables_[parameter];
}

But no lucky, my question is, is that even possible to do? If so, how?

The basic idea is that I dont want to make an specific method to every specific variable in my class, so other classes dont need to know about every get method of it.

Thanks!

ps: For anyone asking why I want this, here is a resume of it, I have a lot of algorithms, that will run in a certain order, and it will use that for the last one already runned algorithm. So, what I want is to make an xml file, that will tell what algorithms will run, in which order and what data it will use from another algorithm.

So, for example, algorithm A have an variable named "threshold", algorithm B need that information, so, normally it will have to ask it from the A using something like A.getThreshold, but as far as I know, I can't call a object function with it name in an string (from the xml file), so my solution would be have only an get function which i pass the variable name I want and that function will return it to me.

Upvotes: 0

Views: 302

Answers (4)

bobah
bobah

Reputation: 18864

What you want is not possible as you are trying to mix compile time (template) and runtime (map lookup) code.

You either have to make it fully runtime:

struct base_type { virtual ~base_type{} };
struct derived_type: base_type { ... };
std::map<std::string, base_type*> lookup_map;
base_type* get(std::string const& key) { return lookup_map[key]; }

Or fully compile time (boost.fusion example):

#include <boost/fusion/container/map.hpp>
#include <boost/fusion/sequence/intrinsic/at_key.hpp>
#include <boost/fusion/sequence/intrinsic/value_at_key.hpp>

namespace bf=boost::fusion;

struct key_a; // analogues of string keys in compile time world
struct key_b;
struct key_c;

typedef bf::map<
  bf::pair<key_a, long>,
  bf::pair<key_b, double>,
  bf::pair<key_c, char const*>
> rtmap_t;
rtmap_t rtmap;

template <class Key>
void set_value(typename bf::result_of::value_at_key<rtmap_t, Key>::type const& val)
{
  bf::at_key<Key>(rtmap) = val;
}

template <class Key>
typename bf::result_of::at_key<rtmap_t, Key>::type get_value()
{
  return bf::at_key<Key>(rtmap);
}

#include <iostream>
int main()
{
  char const* cval = "hello metaprogramming";
  set_value<key_a>(123l);
  set_value<key_b>(456.789);
  set_value<key_c>(cval);
  std::cout << get_value<key_a>() << std::endl;
  std::cout << get_value<key_b>() << std::endl;
  std::cout << get_value<key_c>() << std::endl;

  return 0;
}

Considering the information you provided in your question I would choose runtime variant with dynamic polymorphism.

Upvotes: 0

An alternative solution would be to "wrap" the boost::any object into another object which can be automatically converted to anything you want. I don't think it's a good practice but it's the best fit according to your question.

class AnyWrapper {
    boost::any value;
public:
    AnyWrapper(const boost::any& val) : value(val) {}
    template<typename T> operator T() {
        return boost::any_cast<T>(value);
    }
}

And your getter would be something like :

AnyWrapper get(std::string& parameter)
{
    return variables_[parameter];   // variables is a std::map<std::string, boost::any> 
}

And then you should be able to retrieve your elements like that :

int integerValue = myContainer.get("age");
std::string stringValue = myContainer.get("name");

But again, this is not a clean solution. There is a reason why the boost authors chose to make the any_cast explicit :)

Upvotes: 2

Marc Plano-Lesay
Marc Plano-Lesay

Reputation: 6968

You can also return an boost::any. You lose encapsulation of your implementation, but depending on how you use the return value, it may be the better way.

Upvotes: 0

K-ballo
K-ballo

Reputation: 81399

An boost::any value won't implicitly convert to a type T, you have to request that cast manually:

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

The call will fail with a boost::bad_any_cast exception if the type stored in the any is not exactly T.

Upvotes: 1

Related Questions