views:

139

answers:

4

Hi, I used type erasure pattern in C++, i.e I hide a template class with an abstract class

class Base{

  virtual ~Base(){}

 //pure virtual methods...
};

template<typename T>
class Derived : Base{

Derived<T>(){}
~Derived(){}

//public methods...

private :
vector<T> datas;

};

problem : if I want to retrieve or modify datas, I have to use the Base class

how do I défine the accessors getDatas() and SetDatas(vector datas) ?

+1  A: 

You cannot define a SetData( vector ) since std::vector needs a type, and obiously you cannot define SetData( std::vector< T > ) in the Base if you have no definition for T.

So if you really need this and think this is the way to go, you'll have to look into type dispatching (or make a hack using void*). Boost uses type dispatching in some places, else google provides examples.

edit simple example of what it can look like; not really type dispatching but more straightforward

class Base
{
public:
  template< class T >
  bool SetData( const std::vector< T >& t )
  {
    return SetData( static_cast< const void* >( &t ), typeid( t ) );
  }

protected:
  virtual bool SetData( const void*, const std::type_info& ) = 0;
};

template< class T >
class Derived : public Base
{
protected:
  bool SetData( const void* p, const std::type_info& info )
  {
    if( info == typeid( std::vector< T > ) )
    {
      const std::vector< T >& v = *static_cast< const std::vector< T >* >( p );
      //ok same type, this should work
      //do something with data here
      return true;
    }
    else
    {
      //not good, different types
      return false;
    }
  }
};
stijn
typeof doesn't exist in C++ ?
sorry I meant typeid..
stijn
Yep, that's what you would need to do. I suppose this basically is what `boost::any` does under the hood, though. Frankly, I'd rather use `boost::any` than invent my own.
sbi
@stijn ok nicebut after all these answers, I become doubtful about my choiceif I could resolve vector<My_class<T> > without interface, this would be great
@sbi boost.any could be a solution but does it allow to access datas from the template class ?
My understanding is that you can't have pure virtual template member functions. I think `Base` has to be a template class here.
bshields
@bshields the interest of Base is that it isn't template.Maybe if virtual methods become simple methods, I can access Derived methods directly
@bshields: Ah, good catch, I hadn't seen that. Too bad. Still, something like this ought to work.
sbi
@bshields: you're completly right, fixed example so that it actually works
stijn
A: 

Ultimately, the user of the class must know what T is in order to work with datas in your derived type, even if they are accessing it through the base type.

You may be able to set up a somewhat generic interface with a void*, but the user of your class will still have to match up a each class instance to the specific type, T. If T is int, users will have to know that collection is based on int.

If you want to store any type in the vector, without worrying about which instance is which type, you could make T void*. If you do this, you could even skip the template<typename T> part of derived. It could become a non-template class.

Merlyn Morgan-Graham
but if I keep T, there must be a template<typenename T> somwhere in Derived ?
@user408535: Yeah, I'm just saying if you decide to use void*, so that you can put any object together in the vector, you wouldn't necessarily need T anymore.
Merlyn Morgan-Graham
oh, ok but isn't void* dangerous ?
@user408535: In some senses, absolutely. There is no type `safety`. This means your program is free to behave badly at runtime rather than fail to compile. If you are erasing the type, that is essentially what you hope to achieve anyhow. Void* is safe if the consumer and producer treat it correctly, just like any other pointer.
Merlyn Morgan-Graham
A: 
sbi
@sbi lol seems very complicated to me, I don't have your skills; i think I should forget the virtual methods, I can't even remember why I put them (tied to boost.serialization, maybe).By the way, I'm totally ready to choose boost.any, but the same question remains : does it allow me to access dats of my template class behind?
you were ahead of me ;P
stijn
A: 

You're trying to implement type-erasure and then asking how you can make clients operate on the types that you're erasing. The pattern that you're deploying here is only appropriate if the derived classes of Base have operations in common that can be called without reference to the concrete type of data that they store. If there are no such common operations and the only semantically useful things for clients to do through your Base interface involve using the concrete types of the derived classes, then you won't be able to use this design.

bshields
ok my initial problem before I chose this solution was that in my main I'got vector<My_class<T> >; if type-erasure is not the soltuion, wat can I do ?
@user408535 I don't really understand your initial problem as described. If clients of the interface need to know the underlying data type, then you either have to get rid of the interface or make it a template interface. If you can formulate a set of functions for the interface that don't require the underlying data type, then you could leave it as a non-template interface.
bshields
problem is vector<My_class<T> > is in declaration so I don't possess T at this time and I get a compilation error
@user208535 right, that's why you would have to templatize `Base` if you want to keep that in your declaration, there's really no other way around it. `Base` would have to have a template parameter `T` which would be the same `T` in your `My_class<T>`.
bshields
@bshields I mean that at first I had vector<Base<T> > in my int main() and with no way to provide T, so error; it became vector<Base> witha derived template classthat's the purpose of type-erasure