views:

239

answers:

4

Hi,

I want to implement a Mesh class for a CG project, but have run into some problems. What I want to do is a Mesh class that hides implementation details (like loading to a specific API: OpenGL, DirectX, CUDA, ...) from the user. Additionally, since the Mesh class will be used in research projects, this Mesh class has to be very flexible.

class Channel {
  virtual loadToAPI() = 0;      
}

template <class T>
class TypedChannel : public Channel {

  std::vector<T> data;
};

template <class T>
class OpenGLChannel : public TypedChannel<T> {

  loadToAPI(); // implementation
};

class Mesh {

  template<class T>
  virtual TypedChannel<T>* createChannel() = 0; // error: no virtual template functions

  std::vector<Channel*> channels;
};

class OpenGLMesh {

  template<class T>
  TypedChannel<T>* createChannel()
  {
    TypedChannel<T>* newChannel = new OpenGLChannel<T>;
    channels.push_back(newChannel);
    return newChannel;
  };
};

For flexibility, each Mesh is really a collection of channels, like one position channel, a normal channel, etc. that describe some aspects of the mesh. A channel is a wrapper around a std::vector with some added functionality.

To hide implementation details, there is a derived class for each API (OpenGLMesh, DirectXMesh, CUDAMesh, ...) that handles API-specific code. The same goes for the Channels (OpenGLChannel, etc. that handle loading of the Channel data to the API). The Mesh acts as a factory for the Channel objects.

But here is the problem: Since the Channels are template classes, createChannel must be a template method, and template methods cannot be virtual. What I would need is something like a Factory Pattern for creating templated objects. Does anyone have advice on how something similar could be accomplished?

Thanks

+1  A: 

If you could extract factory from Mesh (introducing some ChannelFactory), then you can use templatized factory:

template <class T>
class ChannelFactory
{
public:
   virtual TypedChannel<T>* createChannel() = 0;
};

Than you could derive your OpenGLMesh from ChannelFactory, , , whatever.

The only limitation of this approach is that you should know beforehand which template parameters you want use in OpenGLMesh.

Otherwise you could be interested how Boost.Any works (boost::any holds values of arbitrary type).

Alexander Poluektov
Thanks for the comment - I have thought about that option already, but the problem is that the channels in mesh must be heterogenous, i.e. they can be TypedChannel<int>, TypedChannel<Vector3>, TypedChannel<float>, etc.When deriving OpenGLMesh from ChannelFactory, an instance of an OpenGLMesh could only create Channels of one type.
raf
No -- you can multiple inherit from ChannelFactory with different parameters.
Alexander Poluektov
You are right, but I would have to know all types that could possibly be used in advance - I want to avoid that if possible.
raf
Then you should use some other technique. Take a look at Boost.Any (edited answer and added link).
Alexander Poluektov
Why would you want to base OpenGLMesh on ChannelFactory? First is mesh, second is Factory, there should be no parent-child relationship between two.
ima
The problem remains the same, either the user creates the Factory object with the right template parameters, but then the Factory pattern is broken, or the Mesh creates the Factory, but then the template Type(s) need to be known to the Mesh.
raf
A: 

I'd say your whole design is broken.

  virtual TypedChannel<T>* createChannel() = 0; // error: no virtual template functions
  std::vector<Channel*> channels;

These two lines just don't make any sense together. Don't try to fix compiler error, think over your concepts.

To start, what exactly is your reasoning for making CreateChannel a virtual member?

To put it another way, C++ is a language known for allowing all kinds of tangled unintelligible designs. And you managed to design something that even C++ thinks is too twisted.

ima
createChannel should be virtual because the API-specific classes derived from Mesh create API-specifc channels (like OpenGLChannel) derived from TypedChannel. The API-specific parts should be hidden from the user - the user only needs to call createChannels and the right kind of channel is created for him. To my understanding, this is the Factory design pattern. (Although factories don't create templated obejcts - which is my problem.)
raf
Why do you think that classes derived from Mesh should override function returning TypedChannel<>, when they store channels as pointers to base Channel class? It can't be necessary, it's a result of some very bad decisions in your library structure.
ima
Generally, it's never necessary to mix polymorphism and templates in this way. Everything can be done with either inheritance and interfaces, or templates and template specializations.
ima
Its not strictly necessary to return TypedChannel<Type>* instead of Channel*, but it is more convenient for the user. But returning Channel* instead of TypedChannel<Type>* would not change the problem.It might be true that everything can be done without mixing polymorphism and templates in this way, but right now I don't see how it could be done in this example, that's why I am asking :)
raf
I by no means suggest doing exactly this, just for demonstration: template<typename TEngine> Mesh and client creating Mesh<OpenGLEngine>.
ima
Or go with Alexander's solution, there's really no need to inherit mesh from factory, it's a misconception.
ima
Hm, that is an interesting alternative, but i think it wouldn't work in my case, since I need to be able to treat an OpenGLMesh just like a DirectXMesh (or any other API-specific mesh) in many parts of my code, so inheritance is probabely better for that than template specialization.
raf
In Alexander's solution, someone still needs to instantiate the factory. If the user does that, he needs to instantiate an API-specific version of the factory and API-specific code should be hidden from the user. If the mesh does it, either it creates the Factory with user-specified template type (like in my example - same problem) or with a set of fixed template type(s), but then the fixed types need to be known in advance.
raf
What i started with - it's not a local issue, design of many parts of your code is broken. It's feels like you've built a house upside down and ask how to strap people to chairs so they won't fall to the ceiling below.
ima
And if you are answered "well, put chairs on the ceiling then so people at least could sit" - you reply "it's not right, ceiling is not a correct place for chairs"
ima
(Yes, I have some free time I need to stay online now ;-)
ima
Ok, but it seems there is no obvious alternative for a factory design pattern that creates templated objects - thats just what i wanted to know. Thanks for your help in any case.
raf
"Ok, but it seems there is no obvious alternative for sitting people upside-down, that's just what I wanted to know"
ima
There is nothing generally wrong with a factory design pattern that creates templated objects, it would be nice if that was possible :)(Or am I missing any non-language specific problems, like problems with type-safety etc.?) - To my understanding it is only a technical problem with the vtable size.
raf
A: 

By channel do you mean 'spacial index'?

If you want to hide implementation details, why do you have them right in your mesh?

You want the mesh to be the same basic format, maybe templating on float, double or morton numbers in different cases. It's not the mesh that should change, just the way it gets loaded.

Charles Eli Cheese
+2  A: 

It's an interesting problem, but let's discuss the compiler error first.

As the compiler said, a function cannot be both virtual and template. To understand why, just think about the implementation: most of the times, objects with virtual functions have a virtual table, which stores a pointer to each function.

For templates however, there are as many functions as combinations of type: so what should be the virtual table like ? It's impossible to tell at compilation time, and the memory layout of your class includes the virtual table and has to be decided at compilation time.

Now on to your problem.

The simplest solution would be to just write one virtual method per type, of course it can soon become tedious, so let's pretend you haven't heard that.

If Mesh is not supposed to know about the various types, then surely you don't need the function to be virtual, because who would know, given an instance of Mesh, with which type invoking the function ?

Mesh* mesh = ...;
mesh.createChannel<int>(); // has it been defined for that `Mesh` ??

On the other hand, I will suppose that OpenGLMesh does know exactly which kind of TypedChannel it will need. If so, we could use a very simple trick.

struct ChannelFactory
{
  virtual ~ChannelFactory() {}
  virtual Channel* createChannel() = 0;
};

template <class T>
struct TypedChannelFactory: ChannelFactory
{
};

And then:

class Mesh
{
public:
  template <class T>
  Channel* addChannel()
  {
    factories_type::const_iterator it = mFactories.find(typeid(T).name());
    assert(it != mFactories.end() && "Ooops!!!" && typeid(T).name());

    Channel* channel = it->second->createChannel();
    mChannels.push_back(channel);
    return channel;
  } // addChannel

protected:
  template <class T>
  void registerChannelFactory(TypedChannelFactory<T>* factory)
  {
    mFactories.insert(std::make_pair(typeid(T).name(), factory));
  } // registerChannelFactory

private:
  typedef std::map < const char*, ChannelFactory* const > factories_type;
  factories_type mFactories;

  std::vector<Channel*> mChannels;
}; // class Mesh

It demonstrates a quite powerful idiom known as type erasure. You probably used it even before you knew the name :)

Now, you can define OpenGLMesh as:

template <class T>
struct OpenGLChannelFactory: TypedChannelFactory<T>
{
  virtual Channel* createChannel() { return new OpenGLChannel<T>(); }
};

OpenGLMesh::OpenGLMesh()
{
  this->registerChannelFactory(new OpenGLChannelFactory<int>());
  this->registerChannelFactory(new OpenGLChannelFactory<float>());
}

And you'll use it like:

OpenGLMesh openGLMesh;
Mesh& mesh = openGLMesh;

mesh.addChannel<int>();    // fine
mesh.addChannel<float>();  // fine

mesh.addChannel<char>();   // ERROR: fire the assert... (or throw, or do nothing...)

Hope I understood what you needed :p

Matthieu M.
Thanks very much (and sorry for the late answer), that does seem solve my problem, because with this design the user can add arbitrary channels without changing any code on the mesh class or its derived classes.Additionally, I can have a number of predefined channels and if the user wants to add his/her own channel, the user would have to write code for loading it anyway, so writing the factory for the channel as well should be no problem (maybe I can even include the Channel Factory in the Channel class, like a clone() method).Thanks again and I hope I didn't overlook any problems with this
raf