Alternative Factory Template when creating template objects - C ++

I want to implement a Mesh class for a CG project, but have run into some problems. What I want to do is the Mesh class, which hides implementation details (for example, loading into a specific API: OpenGL, DirectX, CUDA, ...) from the user. In addition, since the Mesh class will be used in research projects, this Mesh class must 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 a collection of channels, such as a single position channel, a regular channel, etc. that describe some aspects of the grid. The channel is a wrapper around std :: vector with some added functionality.

To hide implementation details, there is a derived class for each API (OpenGLMesh, DirectXMesh, CUDAMesh, ...) that processes the API code. The same goes for channels (OpenGLChannel, etc., which handle the loading of channel data into the API). Mesh acts as a factory for channel objects.

But here's the problem: since Channels are template classes, createChannel must be a template method, and template methods cannot be virtual. I need something like a factory template to create template objects. Does anyone have any advice on how to do something like this?

thanks

+4
source share
4 answers

This is an interesting problem, but first discuss the compiler error.

As the compiler said, a function cannot be virtual and boilerplate. To understand why, just think about the implementation: in most cases, objects with virtual functions have a virtual table that stores a pointer to each function.

However, for templates, there are as many functions as type combinations: so what should be a virtual table? It is impossible to tell at compile time, and the memory layout of your class includes a virtual table and has one that will be determined at compile time.

Now about your problem.

The simplest solution would be to simply write one virtual method for each type, of course, it can soon become tedious, so let it pretend that you have not heard this.

If Mesh does not need to know about the various types, then you will not need the virtual function, because someone knows, given the Mesh instance, with which type the function is calling?

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

On the other hand, I believe that OpenGLMesh knows exactly what type 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 

He demonstrates a pretty strong idiom known as type erasure . You probably used it 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 will 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...) 

I hope I understand what you need: p

+2
source

If you can extract the factory from the Mesh (introducing ChannelFactory), you can use the templatized factory:

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

Than you could receive the OpenGLMesh from ChannelFactory ,, anything.

The only limitation of this approach is that you must know in advance which template parameters you want to use in OpenGLMesh.

Otherwise, you might be wondering how Boost.Any works ( boost::any contains arbitrary type values).

0
source

I would say that your whole structure 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. Do not try to fix the compiler error, think about your concepts.

To get started, what exactly do you think of making CreateChannel a virtual member?

In other words, C ++ is a language known for resolving all tangled, illegible constructs. And you managed to create something that even C ++ considers too twisted.

0
source

By channel do you mean "spatial index"?

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

You want the grid to be the same basic format, maybe a pattern for floating point numbers, double or dead in different cases. This is not a mesh that needs to change, just as it loads.

0
source

Source: https://habr.com/ru/post/1300414/


All Articles