Question

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

Was it helpful?

Solution

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

OTHER TIPS

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).

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.

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top