Reputation: 516
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
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
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
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