سؤال

هل من الممكن إنشاء مجموعة من مؤشرات الوظائف القيبوية، دون مشاحنات القيام بذلك يدويا؟ إليك مثال لتوضيح ما أتحدث عنه هيك.

دعنا نقول أن لدي وظيفة يسمى بشكل متكرر "اكتب" لدي تطبيقين (Writt0 وكتابة 1) الذي أود أن أكون قادرا على التبديل بين ديناميكيا. يتم تكرار وظائف الكتابة هذه في نوع الوسيطة. طريقة واحدة للقيام بذلك هي مجرد الحصول على وظيفة وظيفة الواجهة الأمامية القالب () والتي تستخدم داخليا في حالة IF.

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

("القواعد" الأخرى: لا يمكنني تغيير أساليب MSG للحصول على طرق الكتابة ()، ولا يمكنني تغيير رمز الموقع لاستبدال Msgs مع محولات للحصول على Msgs.)

FWIW، وجدت هذه المقالة في الأساس يقول نفس الشيء الذي أقوله هنا.

#include <iostream>
using namespace std;

template<typename T> void write0(T msg) { cout << "write0: " << msg.name() << endl; }
template<typename T> void write1(T msg) { cout << "write1: " << msg.name() << endl; }

// This isn't so bad, since it's just a conditional (which the processor will
// likely predict correctly most of the time).
bool use_write0;
template<typename T> void write(T msg) { if (use_write0) write0(msg); else write1(msg); }

struct MsgA { const char *name() { return "MsgA"; } };
struct MsgB { const char *name() { return "MsgB"; } };
struct MsgC { const char *name() { return "MsgC"; } };
struct MsgD { const char *name() { return "MsgD"; } };

// This doesn't work: templates may not be virtual.
#if 0
struct Writer { template<typename T> virtual void write(T msg) = 0; };
struct Writer0 { template<typename T> virtual void write(T msg) { cout << "write0: " << msg.name() << endl; } };
struct Writer1 { template<typename T> virtual void write(T msg) { cout << "write0: " << msg.name() << endl; } };
#endif

int main(int argc, char **argv) {
  use_write0 = argc == 1;

  // I can do this:
  write(MsgA());

  // Can I achieve the following without the verbosity (manual setup, named
  // template instantiations, etc.)?
  void (*pwriteA)(MsgA) = use_write0 ? (void(*)(MsgA)) write0<MsgA> : (void(*)(MsgA)) write1<MsgA>;
  void (*pwriteB)(MsgB) = use_write0 ? (void(*)(MsgB)) write0<MsgB> : (void(*)(MsgB)) write1<MsgB>;
  void (*pwriteC)(MsgC) = use_write0 ? (void(*)(MsgC)) write0<MsgC> : (void(*)(MsgC)) write1<MsgC>;
  void (*pwriteD)(MsgD) = use_write0 ? (void(*)(MsgD)) write0<MsgD> : (void(*)(MsgD)) write1<MsgD>;
  pwriteA(MsgA());
  pwriteB(MsgB());
  pwriteC(MsgC());
  pwriteD(MsgD());

  return 0;
}
هل كانت مفيدة؟

المحلول

إذا كنت ترغب في تبديل وظائف التسجيل ذهابا وإيابا أثناء تشغيل البرنامج، أعتقد أنه عليك تعيين مؤشر الوظيفة يدويا لكل نوع.

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

// writer functions
template<typename T> void write0(T msg) { std::cout << 0; };
template<typename T> void write1(T msg) { std::cout << 1; };

// global flag
bool use_write0;

// function pointers for all types
template<typename T>
struct dispatch {
   typedef void (*write_t)(T);
   static write_t ptr;
};

// main write function
template<typename T>
inline void write(T msg) {
   (*dispatch<T>::ptr)(msg);
}

// the fun part
template<typename T>
void autoinit(T msg) {
   if (use_write0)
      dispatch<T>::ptr = &write0<T>;
   else
      dispatch<T>::ptr = &write1<T>;
   // call again for dispatch to correct function
   write(msg);
}

// initialization
template<typename T>
typename dispatch<T>::write_t dispatch<T>::ptr = &autoinit<T>;

// usage example
int main(int argc, char **argv) {
   use_write0 = (argc == 1);
   write("abc");
   return 0;
}

لكل نوع T أول مكالمة write<T>() يقرر استخدام وظيفة الكتابة. المكالمات اللاحقة ثم استخدم مباشرة مؤشر الوظيفة إلى هذه الوظيفة.

نصائح أخرى

يمكنك أيضا استخدام Don Clugston fastdelegates. رأس. يولد أي وقت تشغيل العلوية على الإطلاق والمندوبين الموجهين حقا. في حين أن بناء الجملة لاستخدامها ليس مثاليا، إلا أنه أبسط قليلا من الاضطراب مع مؤشرات الوظائف الخام.

لماذا لا تستخدم مجموعة من مؤشرات الوظائف؟

#include <iostream>
using namespace std;

template<typename T> void write0(T msg) { cout << "write0: " << msg.name() << endl; }
template<typename T> void write1(T msg) { cout << "write1: " << msg.name() << endl; }

template<typename T> struct WriteSelector
{
    static void(* const s_functions[])(T msg);
};
template<typename T> void(* const WriteSelector<T>::s_functions[])(T msg)=
{
    &write0<T>,
    &write1<T>
};

unsigned write_index=0;
template<typename T> void write(T msg)
{
    WriteSelector<T>::s_functions[write_index](msg);
}


struct MsgA { const char *name() { return "MsgA"; } };
struct MsgB { const char *name() { return "MsgB"; } };
struct MsgC { const char *name() { return "MsgC"; } };
struct MsgD { const char *name() { return "MsgD"; } };

void Test()
{
    write(MsgA());
    write(MsgB());
    write(MsgC());
    write(MsgD());
}

int main()
{
    Test();
    write_index=1;
    Test();
    return 0;
}

هناك محوران من الاختلاف في الكتابة: اختيار الكتابة 0 / Witte1 و MSGA / B / C .... الاختيار.

من الناحية النظرية وهذا يعني أنك بحاجة إلى تطبيقات NXM write وظيفة. بالطبع، إذا تمت إضافة تطبيق الكتابة، أو يتم إضافة نوع الرسالة، فهذا يؤدي إلى REST. وظائف م أو ن إضافية لإضافتها.

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

يمكن القيام به من خلال إنشاء التسلسل الهرمي لفئة عنصر N مع وظائف كتابة م في كل فصل. لكنه سيصبح قريبا كابوس صيانة. ما لم يكن محتوى الرسائل هو أيضا وقت التشغيل Volymorphic. لكن السؤال هو عن تعدد الأشكال الثابتة للرسائل.

نظرا لأن تعدد الأشكال وقت التشغيل يتم استبعاده بسبب تفكيك (ولا يمكنك الحصول على وظيفة ضخمة، فإنه من شأنه أن يؤدي إلى تقليل من فقدان التجاوزات)، نحتاج إلى تنفيذ روتين لا يتم إرسال بعض الشيء، وتحويل معلومات وقت التشغيل إلى معلومات وقت الترجمة وبعد

وبشكل أكثر تحديدا: قم بتذوق الإجراء الرئيسي (في المثال يسمى Tmain) مع الكاتب للاستخدام، واتصل به مع حجة القالب اليمنى من "حقيقي" main.

هذا يغفل استخدام متغير اختيار العالم "، ومع ذلك هو موجه نحو الكائنات والموجزة.

    // twodimensionalpolymorph.cpp
    //

    #include <iostream>

    using namespace std;

    class Write0 {
        public: 
        template< typename tMsg > 
        void operator()( /*const*/ tMsg& msg ) { cout << "write0: " << msg.name() << endl; };
    };

    class Write1 {
        public: 
        template< typename tMsg > 
        void operator()( /*const*/ tMsg& msg ) { cout << "write1: "<< msg.name() << endl; };
    };

    struct MsgA { const char *name() { return "MsgA"; } };
    struct MsgB { const char *name() { return "MsgB"; } };
    struct MsgC { const char *name() { return "MsgC"; } };
    struct MsgD { const char *name() { return "MsgD"; } };

    // the Tmain does the real action
    //
    template< typename Writer >
    int Tmain( Writer& write, int argc, char** args ) {

        write( MsgA() );
        write( MsgB() );
        write( MsgB() );
        write( MsgD() );

        return 0;
    }

    // the main merely chooses the writer to use
    //
    int main( int argc, char** args ) {

        if( argc==1 )
            return Tmain( Write0(), argc, args);
        else
            return Tmain( Write1(), argc, args);

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