سؤال

أريد تنفيذ فئة شبكة لمشروع CG، لكنها تدير بعض المشاكل. ما أريد القيام به هو فئة شبكة تخفي تفاصيل التنفيذ (مثل التحميل إلى 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;
  };
};

للمرونة، كل شبكة هي حقا مجموعة من القنوات، مثل قناة موقف واحدة، قناة طبيعية، إلخ. يصف بعض جوانب الشبكة. القناة عبارة عن مجمع حول STD :: متجه مع بعض الوظائف الإضافية.

لإخفاء تفاصيل التنفيذ، هناك فئة مشتقة لكل API (OpenGlmmesh، DirectXmesh، Cudamesh، ...) التي تتعامل مع التعليمات البرمجية الخاصة ب API. الشيء نفسه ينطبق على القنوات (OpenGlchannel، إلخ. هذا التعامل مع تحميل بيانات القناة إلى API). تعمل الشبكة كصنع كائنات القناة.

ولكن هنا هي المشكلة: نظرا لأن القنوات هي فئات القوالب، يجب أن تكون creationechannel طريقة القالب، ولا يمكن أن تكون أساليب القوالب الظاهرية. ما سأحتاج إليه هو شيء مثل نمط المصنع لإنشاء كائنات قيب. هل لدى أي شخص نصيحة حول كيفية تحقيق شيء مماثل؟

شكرا

هل كانت مفيدة؟

المحلول

إنها مشكلة مثيرة للاهتمام، ولكن دعونا ننكش خطأ المحول البرمجي أولا.

كما قال المحول البرمجي، لا يمكن أن تكون وظيفة الظاهري والقالب. لفهم السبب، فقط فكر في التنفيذ: معظم الأوقات، والكائنات ذات الوظائف الافتراضية لها جدول ظاهري، يقوم بتخصيص مؤشر لكل وظيفة.

بالنسبة للقوالب، ومع ذلك، هناك العديد من الوظائف كملقيات من النوع: إذن ما الذي يجب أن يكون الجدول الظاهري؟ من المستحيل معرفة وقت التجميع، ويشمل تخطيط الذاكرة من صفك الجدول الظاهري و لديها أن تقرر في وقت التجميع.

الآن على مشكلتك.

سيكون أبسط الحل هو مجرد كتابة طريقة افتراضية واحدة لكل نوع، بالطبع يمكن أن تصبح قريبا مملة، لذلك دعونا نتظاهر بأنك لم تسمع ذلك.

إذا Mesh ليس من المفترض أن يعرف عن الأنواع المختلفة، ثم بالتأكيد لا تحتاج إلى وظيفة virtual, ، لأن من يعرف، بالنظر إلى مثيل Mesh, ، مع أي نوع استدعاء الوظيفة؟

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

من ناحية أخرى، سأفترض ذلك OpenGLMesh لا يعرف بالضبط أي نوع من TypedChannel سوف تحتاج. إذا كان الأمر كذلك، يمكننا استخدام خدعة بسيطة للغاية.

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

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

وثم:

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

إنه يوضح مصطلح قوي تماما type erasure. وبعد ربما استخدمت حتى قبل أن تعرف الاسم :)

الآن، يمكنك تحديد OpenGLMesh كما:

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

آمل أن أفهم ما تحتاجه: ص

نصائح أخرى

إذا تمكنت من استخراج المصنع من شبكة (إدخال بعض القنطيل)، فيمكنك استخدام مصنع القبيل:

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

مما كنت تستطيع أن تستمد OpenGlmmesh الخاص بك من Channelfactory،،، كل ما.

الحد الوحيد لهذا النهج هو أنه يجب أن تعرف مسبقا معلمات القالب التي تريد استخدامها في OpenGlmesh.

وإلا فقد تكون مهتما كيف boost.any. يعمل (boost::any يحمل قيم نوع التعسفي).

أود أن أقول التصميم الكامل مكسور.

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

هذان السطران فقط لا معنى له معا. لا تحاول إصلاح خطأ التحويل البرمجي، فكر في مفاهيمك.

للبدء، ماذا هو بالضبط سبب الخاص بك لجعل createchannel عضو افتراضي؟

لوضعها بطريقة أخرى، C ++ هي لغة معروفة بالسماح بجميع أنواع التصميمات المتشابكة غير المفهومة. وتمكنت من تصميم شيء يعتقد أنه حتى C ++ ملتوية للغاية.

عن طريق القناة هل تقصد "مؤشر سباكي"؟

إذا كنت ترغب في إخفاء تفاصيل التنفيذ، فلماذا لديك مباشرة في شبكك؟

تريد أن تكون الشبكة هي نفس التنسيق الأساسي، وربما يكون القالب على أرقام تعويم أو مزدوجة أو مورتون في حالات مختلفة. إنه ليس هو الشبكة التي يجب أن تتغير، فقط بالطريقة التي يتم تحميلها.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top