tags:

views:

59

answers:

3

Hi,

I have a class myclass that has a private member param_map

class some_class {

 private:
  std::map<std::string,std::shared_ptr<parameter> > param_map;
};

I want to expose this map to allow other classes, I have already created add, delete, get parameter methods, which do, what you think. But I would like to expose this so other classes can traverse the map.

Q1: What would be the safest way to do this.

parameter also has a member value that I want to be either int/float/bool so one option would be to define it using templates

template<class T>
class parameter {
  public:
   T get_value { return value_; }

  private:
   T value_;
}

Q2: But how would I store this type in the map? And how would it change the definition of param_map. Also I would consider non template solutions (But I would prefer just one map)

PS: I would rather avoid using boost, I prefer to use the c++x0 std lib, but if boost is the best option then I will consider that. It would be good if you could post code examples also.

Regards

Mark

+1  A: 

Q1: You can expose an iterator that iterates over the map, just like std::map does.

Q2: You can use 3 different maps, or use boost::any to store the value. If you want to avoid boost, you can store void* in place of boost::any (but I would usually not go the void* way -- I would rather use the required parts of boost).

Amit Kumar
I would rather avoid using boost, I prefer to use the c++x0 std lib.
Mark
I think having a map for each type would suit my solution.
Mark
+1  A: 

Q1: You can provide a foreach() member function which accepts a functor as an argument. The foreach function would traverse the map and call the functor on each item in the map. The caller could then use a C++0x lambda function to create the functor.

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

using namespace std;

class container {
public:
    container() {
        _m["one"] = 1;
        _m["two"] = 2;
        _m["three"] = 3;
    }

    template <class FN>
    void foreach(FN func) {
        std::for_each(_m.begin(), _m.end(), func);
    }

private:
    map<string, int> _m;
};

void main(int, char** a)
{
    container c;
    c.foreach([](pair<string,int> p) -> void {
        cout << p.first << ", " << p.second << endl;
    });
}

I don't have a C++0x compliant compiler handy, so this may need some tweaking to compile.

Q2: I know you don't want to use boost, but I think boost.variant may be your be your best option here. You could use a discriminated union instead but I would be very hesitant to give up the type safety boost.variant gives you.

enum type { int_t, float_t, bool_t };

struct item {
    type t;
    union {
        int i;
        float f;
        bool b;
    };
};
Ferruccio
Thanks Ferruccio, I had a read over boost::variant and it looks like a good option, my only concern is this. Say I caller calls get_parameter which is will be a variant, how does it determine whether it is an int, float, or bool or string?
Mark
There are several ways. The simplest is to call the witch() method which returns a 0 based index of the type. i.e. 0 is the first type, 1 is the second. Boost.variant supports the visitor pattern, which is a little more involved but has some nice benefits (if you add a new type, it forces you to update every place the visitor is applied with the new type or it will not compile)
Ferruccio
Ok, Im actually thinking now for my purposes having maps for each type might be better.
Mark
A: 

I'd go with boost::any for the template type. You can reimplement it yourself (as an excellent exercise), but this is wasting your time.

As for Q1, why not simply make the map public? You'll end up providing accession functions for most of the methods in map, like operator[], iterators, etc. So making it public is not a bad idea:

struct MyClass 
{
    map<string, any> parameters;

    // For convenience
    template <typename T>
    T const& getParameter(string const& s) const
    {
        map<string, any>::const_iterator i = parameters.find(s);
        if (i == parameters.end()) throw /* something */;
        return any_cast<T const&>(*i);
    }

    template <typename T>
    T& getParameter(string const& s)
    {
        map<string, any>::iterator i = parameters.find(s);
        if (i == parameters.end()) throw /* something */;
        return any_cast<T&>(*i);
    }
};

MyClass x; 
x.parameters["foo"] = 2.5;
x.parameters["bar"] = string("Hey");
double xfoo = x.getParameter<double>("foo");
string const& xbar = x.getParameter<string>("bar");
for_each(x.parameters.begin(), x.parameters.end(), baz);
Alexandre C.