C++ 创建模板化对象时工厂模式的替代方案-C++;

C++ 创建模板化对象时工厂模式的替代方案-C++;,c++,templates,factory-pattern,C++,Templates,Factory Pattern,我想为CG项目实现一个网格类,但遇到了一些问题。 我想要做的是一个Mesh类,它向用户隐藏实现细节(比如加载到特定的API:OpenGL、DirectX、CUDA等等)。此外,由于网格类将用于研究项目,因此该网格类必须非常灵活 class Channel { virtual loadToAPI() = 0; } template <class T> class TypedChannel : public Channel { std::vector<T&g

我想为CG项目实现一个网格类,但遇到了一些问题。 我想要做的是一个Mesh类,它向用户隐藏实现细节(比如加载到特定的API:OpenGL、DirectX、CUDA等等)。此外,由于网格类将用于研究项目,因此该网格类必须非常灵活

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;
  };
};
类频道{
虚拟loadToAPI()=0;
}
模板
类别类型频道:公共频道{
std::矢量数据;
};
模板
类OpenGLChannel:PublicTypedChannel{
loadToAPI();//实现
};
类网格{
模板
虚拟类型通道*createChannel()=0;//错误:没有虚拟模板函数
矢量信道;
};
类OpenGLMesh{
模板
TypedChannel*createChannel()
{
TypedChannel*newChannel=新OpenGLChannel;
频道。推回(新频道);
返回新频道;
};
};
为了灵活性,每个网格实际上是一组通道,如一个位置通道、一个法线通道等,用于描述网格的某些方面。通道是std::vector的包装器,具有一些附加功能

为了隐藏实现细节,每个API(OpenGLMesh、DirectXMesh、CUDAMesh等)都有一个派生类来处理特定于API的代码。同样的情况也适用于通道(OpenGLChannel等,用于将通道数据加载到API)。网格充当通道对象的工厂

但问题是:由于通道是模板类,createChannel必须是模板方法,而模板方法不能是虚拟的。我需要的是类似于工厂模式的东西,用于创建模板化对象。有人对如何实现类似的目标有什么建议吗


谢谢

如果您可以从网格中提取工厂(引入一些ChannelFactory),那么您可以使用模板化工厂:

template <class T>
class ChannelFactory
{
public:
   virtual TypedChannel<T>* createChannel() = 0;
};
模板
阶级工厂
{
公众:
虚拟类型通道*createChannel()=0;
};
您可以从ChannelFactory中派生OpenGLMesh,不管是什么

这种方法的唯一限制是,您应该事先知道希望在OpenGLMesh中使用哪些模板参数


否则,您可能会对其工作原理感兴趣(
boost::any
包含任意类型的值)。

我认为您的整个设计都被破坏了

  virtual TypedChannel<T>* createChannel() = 0; // error: no virtual template functions
  std::vector<Channel*> channels;
虚拟类型通道*createChannel()=0;//错误:没有虚拟模板函数
矢量信道;
这两条线连在一起没有任何意义。不要试图修复编译器错误,请仔细考虑您的概念

首先,让CreateChannel成为虚拟成员的具体原因是什么


换句话说,C++是一种允许各种混乱的不明白的设计的语言。而且你设计了一些甚至C++认为太扭曲的东西。p> 如果要隐藏实现细节,为什么要将它们放在网格中


您希望网格具有相同的基本格式,可能在不同的情况下使用浮点、双精度或莫顿数作为模板。应该改变的不是网格,而是加载方式。

这是一个有趣的问题,但让我们先讨论编译器错误

正如编译器所说,函数不能既是虚拟函数又是模板函数。要理解原因,只需考虑实现:大多数情况下,具有虚拟函数的对象都有一个虚拟表,其中存储指向每个函数的指针

然而,对于模板,函数的数量和类型的组合一样多:那么虚拟表应该是什么样的呢?在编译时无法判断,并且类的内存布局包括虚拟表,必须在编译时决定

现在谈谈你的问题

最简单的解决方案是只为每种类型编写一个虚拟方法,当然它很快就会变得单调乏味,所以让我们假设您没有听说过这一点

如果
Mesh
不应该知道各种类型,那么肯定不需要函数是
virtual
,因为如果给定
Mesh
的实例,谁会知道调用函数的是哪种类型

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

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
你会像这样使用它:

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...)
OpenGLMesh-OpenGLMesh;
Mesh&Mesh=openGLMesh;
mesh.addChannel();//好的
mesh.addChannel();//好的
mesh.addChannel();//错误:触发断言。。。(或扔,或什么也不做…)

希望我理解了您的需要:p

谢谢您的评论-我已经考虑过该选项,但问题是mesh中的通道必须是异构的,即它们可以是TypedChannel、TypedChannel、TypedChannel等。从ChannelFactory派生OpenGLMesh时,OpenGLMesh的一个实例只能创建一种类型的通道。不——可以使用不同的参数从ChannelFactory继承多个通道。你是对的,但我必须事先知道可能使用的所有类型——如果可能的话,我想避免这种情况。然后你应该使用其他技术。看看Boost.Any(已编辑的答案和添加的链接)。为什么要将OpenGLMesh建立在ChannelFactory上?第一个是mesh,第二个是Factory,两者之间不应有父子关系。createChannel应该是虚拟的,因为从mesh派生的API特定类创建从TypedChannel派生的API特定通道(如OpenGLChannel)。API特定的部分应该对用户隐藏-用户只需要调用createChannels,并为其创建正确类型的通道。据我了解,这是工厂的设计模式。(虽然工厂不创建模板化的OBEJCT——这是我的问题。)当从Mesh派生的类将通道存储为指向基本通道类的指针时,为什么认为它们应该重写返回TypedChannel的函数?没必要
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>());
}
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...)