سؤال

لدي مؤشر وظيفة محدد بواسطة:

typedef void (*EventFunction)(int nEvent);

هل هناك طريقة للتعامل مع هذه الوظيفة بمثيل محدد لكائن C++؟

class A
{
private:
    EventFunction handler;

public:
    void SetEvent(EventFunction func) { handler = func; }

    void EventOne() { handler(1); }
};

class B
{
private:
    A a;
public:
    B() { a.SetEvent(EventFromA); }  // What do I do here?

    void EventFromA(int nEvent) { // do stuff }
};

يحرر: وأشار أوريون إلى الخيارات التي يقدمها Boost مثل:

boost::function<int (int)> f;
X x;
f = std::bind1st(
      std::mem_fun(&X::foo), &x);
f(5); // Call x.foo(5)

لسوء الحظ، Boost ليس خيارًا بالنسبة لي.هل هناك نوع من وظيفة "الكاري" التي يمكن كتابتها بلغة C++ والتي ستقوم بهذا النوع من التفاف المؤشر لوظيفة عضو في مؤشر وظيفة عادي؟

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

المحلول

أوصي بشدة بمكتبة FastDelegate الممتازة الخاصة بـ Don Clugston.فهو يوفر كل الأشياء التي تتوقعها من مندوب حقيقي ويجمعها في عدد قليل من تعليمات ASM في معظم الحالات.تعتبر المقالة المصاحبة قراءة جيدة لمؤشرات وظائف الأعضاء أيضًا.

http://www.codeproject.com/KB/cpp/FastDelegate.aspx

نصائح أخرى

يمكنك استخدام مؤشرات الدالة للفهرسة في جدول vtable لمثيل كائن معين.وهذا ما يسمى أ مؤشر وظيفة العضو.سيلزم تغيير بناء الجملة الخاص بك لاستخدام عاملي التشغيل ".*" و"&::":

class A;
class B;
typedef void (B::*EventFunction)(int nEvent)

وثم:

class A
{
private:
    EventFunction handler;

public:
    void SetEvent(EventFunction func) { handler = func; }

    void EventOne(B* delegate) { ((*delegate).*handler)(1); } // note: ".*"
};

class B
{
private:
    A a;
public:
    B() { a.SetEvent(&B::EventFromA); } // note: "&::"

    void EventFromA(int nEvent) { /* do stuff */ }
};

اهرب من مؤشرات دالة C++ الأولية واستخدمها std::function بدلاً من.

يمكنك استخدام boost::function إذا كنت تستخدم مترجمًا قديمًا مثل visual studio 2008 الذي لا يدعم C++ 11.
boost:function و std::function هي نفس الشيء - لقد قاموا بسحب قدر كبير من العناصر المعززة إلى مكتبة std لـ C++ 11.

ملحوظة:قد ترغب في قراءة وثائق وظيفة التعزيز بدلاً من وثائق Microsoft لأنها أسهل في الفهم

قد تجد الأسئلة الشائعة حول C++ بواسطة مارشال كلاين مفيدة لما تحاول تحقيقه.

أقرأ عن مؤشرات للأعضاء.لاستدعاء أسلوب ما في الفئة المشتقة، يجب الإعلان عن الأسلوب في الفئة الأساسية باعتباره افتراضيًا وتجاوزه في الفئة الأساسية ويجب أن يشير المؤشر إلى أسلوب الفئة الأساسية.المزيد عن مؤشرات للأعضاء الظاهريين.

إذا كنت تتفاعل مع مكتبة C، فلا يمكنك استخدام وظيفة عضو الفصل دون استخدام شيء مثل boost::bind.تسمح لك معظم مكتبات C التي تستخدم وظيفة رد الاتصال عادةً بتمرير وسيطة إضافية من اختيارك (عادةً من النوع void*)، والتي يمكنك استخدامها لبدء فصلك الدراسي، على النحو التالي:


class C
{
public:
  int Method1(void) { return 3; }
  int Method2(void) { return x; }

  int x;
};

// This structure will hold a thunk to
struct CCallback
{
  C *obj;  // Instance to callback on
  int (C::*callback)(void);  // Class callback method, taking no arguments and returning int
};

int CBootstrapper(CCallback *pThunk)
{
  // Call the thunk
  return ((pThunk->obj) ->* (pThunk->callback))( /* args go here */ );
}

void DoIt(C *obj, int (C::*callback)(void))
{
  // foobar() is some C library function that takes a function which takes no arguments and returns int, and it also takes a void*, and we can't change it
  struct CCallback thunk = {obj, callback};
  foobar(&CBootstrapper, &thunk);
}

int main(void)
{
  C c;
  DoIt(&c, &C::Method1);  // Essentially calls foobar() with a callback of C::Method1 on c
  DoIt(&c, &C::Method2);  // Ditto for C::Method2
}

لسوء الحظ، لا يمكن لنوع EventFunction الإشارة إلى دالة B، لأنها ليست النوع الصحيح.يمكنك جعله النوع الصحيح، ولكن ربما لا يكون هذا هو الحل الذي تريده حقًا:

typedef void (*B::EventFunction)(int nEvent);

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

  • اجعل B فئة أساسية، ثم قم بتجاوز وظيفة افتراضية لكل فئة أخرى قد يتم استدعاؤها.ثم يقوم A بتخزين مؤشر إلى B بدلاً من مؤشر الوظيفة.أنظف بكثير.
  • إذا كنت لا ترغب في ربط الوظيفة بنوع فئة معين، حتى فئة أساسية (ولن ألومك)، فأنا أقترح عليك إنشاء الوظيفة التي تسمى وظيفة ثابتة:"static void EventFrom A(int nEvent);".ثم يمكنك تسميته مباشرة، بدون كائن B.ولكن ربما تريد أن تقوم باستدعاء مثيل محدد لـ B (ما لم يكن B عبارة عن مفردة).
  • لذلك، إذا كنت تريد أن تكون قادرًا على استدعاء مثيل معين من B، ولكن تكون قادرًا على الاتصال بمثيلات غير B أيضًا، فأنت بحاجة إلى تمرير شيء آخر إلى وظيفة رد الاتصال الخاصة بك حتى تتمكن وظيفة رد الاتصال من استدعاء الكائن الصحيح.اجعل وظيفتك ثابتة، كما هو مذكور أعلاه، وأضف معلمة void* والتي ستجعل منها مؤشرًا إلى B.

من الناحية العملية ترى حلين لهذه المشكلة:أنظمة مخصصة حيث يمكنك تمرير الفراغ* والحدث، والتسلسلات الهرمية ذات الوظائف الافتراضية في فئة أساسية، مثل أنظمة النوافذ

لقد ذكرت أن التعزيز ليس خيارًا بالنسبة لك، ولكن هل لديك TR1 متاح لك؟

يقدم TR1 كائنات الوظائف والربط وmem_fn استنادًا إلى مكتبة التعزيز، وقد تكون موجودة بالفعل مع المترجم الخاص بك.إنه ليس قياسيًا بعد، ولكن على الأقل اثنين من المترجمين الذين استخدمتهم مؤخرًا حصلوا عليه.

http://en.wikipedia.org/wiki/Technical_Report_1
http://msdn.microsoft.com/en-us/library/bb982702.aspx

من غير الواضح إلى حد ما ما تحاول تحقيقه هنا.ما هو واضح هو أن المؤشرات الوظيفية ليست هي الطريق.

ربما ما تبحث عنه هو مؤشر للطريقة.

لدي مجموعة من الفصول لهذا الشيء بالضبط الذي أستخدمه في إطار عمل c++ الخاص بي.

http://code.google.com/p/kgui/source/browse/trunk/kgui.h

كيف أتعامل معها هي أن كل وظيفة فئة يمكن استخدامها كرد اتصال تحتاج إلى وظيفة ثابتة تربط نوع الكائن بها.لدي مجموعة من وحدات الماكرو التي تقوم بذلك تلقائيًا.يقوم بإنشاء وظيفة ثابتة بنفس الاسم باستثناء البادئة "CB_" والمعلمة الأولى الإضافية وهي مؤشر كائن الفئة.

تحقق من أنواع الفئات kGUICallBack وإصدارات القوالب المختلفة منها للتعامل مع مجموعات المعلمات المختلفة.

#define CALLBACKGLUE(classname , func) static void CB_ ## func(void *obj) {static_cast< classname *>(obj)->func();}
#define CALLBACKGLUEPTR(classname , func, type) static void CB_ ## func(void *obj,type *name) {static_cast< classname *>(obj)->func(name);}
#define CALLBACKGLUEPTRPTR(classname , func, type,type2) static void CB_ ## func(void *obj,type *name,type2 *name2) {static_cast< classname *>(obj)->func(name,name2);}
#define CALLBACKGLUEPTRPTRPTR(classname , func, type,type2,type3) static void CB_ ## func(void *obj,type *name,type2 *name2,type3 *name3) {static_cast< classname *>(obj)->func(name,name2,name3);}
#define CALLBACKGLUEVAL(classname , func, type) static void CB_ ## func(void *obj,type val) {static_cast< classname *>(obj)->func(val);}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top