Ryan
Ryan

Reputation: 516

Heterogeneous pointer container

I'm currently using an enum to map into an array of Base*. Each Derived type is given an index by the enum.

enum DerivedType {
    DERIVED_TYPE_1 = 0,
    DERIVED_TYPE_2,
    ...
    NUM_DERIVED_TYPES
};


class Base {

};


class Derived1 : public Base {
    static const DerivedType type;
};
const DerivedType Derived1::type = DERIVED_TYPE_1;


class Derived2 : public Base {
    static const DerivedType type;
};
const DerivedType Derived2::type = DERIVED_TYPE_2;


class Container {
    Base* obs[NUM_DERIVED_TYPES];

    template<class T>
    void addOb(T* ob) {
        obs[T::type] = ob;
    }

    template<class T>
    T* getOb() {
        return (T*) obs[T::type];
    }

    Base* getOb(DerivedType type) {
        return obs[type];
    }
};

Since the index of each derived type is known at compile time, is there a way to have the non-template getOb(DerivedType type) return the correct DerivedN pointer, maybe by looking up the typename in an int -> typename map? Or is there a better way to implement this type of pattern? Also, it would be nice to have each Derived type add itself to whatever data structure assigns it its index value.

Basically, I need a static heterogeneous pointer container that can be accessed by type or by index, all the while returning the correct Derived*. I'm guessing Boost has something that would work, but I haven't found it yet.

Thanks for any help.

Upvotes: 1

Views: 566

Answers (3)

Ise Wisteria
Ise Wisteria

Reputation: 11669

Though I'm not 100% confident that I understand the question correctly, probably what you mentioned(or similar one) can be implemented with boost::fusion and boost::mpl.
For example:

#include <boost/fusion/include/map.hpp>
#include <boost/fusion/include/at_key.hpp>
#include <boost/mpl/vector.hpp>
namespace bf = boost::fusion;
namespace bm = boost::mpl;

// This order has to match with the enumerators in DerivedType
typedef bm::vector< Derived1, Derived2 > DerivedTypes;

typedef bf::map< bf::pair< Derived1, Derived1* >
               , bf::pair< Derived2, Derived2* > > Container;

int main() {
    Container c( bf::make_pair< Derived1, Derived1* >(0)
               , bf::make_pair< Derived2, Derived2* >(0) );
    Derived1 d1;
    Derived2 d2;
    bf::at_key< Derived1 >( c ) = &d1; // access with type
    bf::at_key< Derived2 >( c ) = &d2;
    // access with enum
    bf::at_key< bm::at_c< DerivedTypes, DERIVED_TYPE_1 >::type >( c ) = &d1;
    bf::at_key< bm::at_c< DerivedTypes, DERIVED_TYPE_2 >::type >( c ) = &d2;
}

Upvotes: 2

Xeo
Xeo

Reputation: 131799

Well, what you currently have looks pretty solid to me. The only problem I see, is that you'll only have a Base* available at runtime, since a single function can only return 1 type. To allow multi-type return without having to specify an extra parameter, you could fake a function like so:

// if employed as a free function
class getOb{
  DerivedType _type;
public:
  getOb(DerivedType type)
    : _type(type) {}

  template<class T>
  operator T*() const{
    Base* ptr;
    // fetch correct pointer from wherever
    // using _type, if non-existant use 0
    return (T*) ptr;
  }
};

Usage like

Derived1* pd = getOb(DERIVED_TYPE_1);
assert(pd != 0);

The parameter is passed in the constructor, while the actual function body lies in the conversion operator. Of course this only looks good as a free standing function, so here is how it would look if you wanted to implement it as a member function

class GetObClass{
  mutable DerivedType _type;
public:
  GetObClass& operator()(DerivedType type) const{
    _type = type;
    return *this;
  }

  // template conversion operator as before
};

Usage like

class Container{
public:
  const GetObClass getOb;
};

Container c;
Derived1* pd = c.getOb(DERIVED_TYPE_1);
assert(pd != 0);

Upvotes: 0

iammilind
iammilind

Reputation: 69988

(1) is there a way to have the "non-template" getOb(DerivedType type) return the correct DerivedN pointer

Unfortunately not possible. For non-template functions, a return type can be only a single type and that has to be Base* type in your case. Current implementation of Base* getOb(DerivedType type); is proper.

(2) is there a better way to implement this type of pattern?

At least you can ease your work by using templatized intermediate class (without any overhead), which will initialize the DerivedType variable for you. For example, declare something like below:

template<DerivedType TYPE>
struct Link : Base {
  static const DerivedType type;
};
template<DerivedType TYPE>
const DerivedType Link<TYPE>::type = TYPE;

Now Link<DerivedType> should be inherited for the Derived classes, such as:

class Derived1 : public Link<DERIVED_TYPE_1> {
// no need to declare/define an explicit variable for DerivedType now.
};

Upvotes: 0

Related Questions