Может ли обратный вызов слота signal2 C ++ содержать информацию о Objective-C / C ++ Class / Selector (Method)?

Это должно быть так очевидно для некоторых из вас, но я не могу найти пример этого:

Мне нужен сигнал boost :: signal2 для подключения обратного вызова слота, который является функцией-членом класса C ++ или функтором, чтобы я мог сделать обратные вызовы модели в код контроллера Objective-C / C ++.

Этот обратный вызов должен хранить класс и селектор экземпляра метода Objective-C / C ++, который можно вызывать внутри функции обратного вызова C ++. (Я предполагаю, что нет никакого реального способа предоставить адрес функции прямого обратного вызова метода Objective-C / C ++). Я предполагал, что мне нужно создать экземпляр класса / функтора C ++, чтобы содержать информацию для вызова метода Objective-C / C ++.

Я также не уверен, смогу ли я отделить Class и SEL (селектор) и сохранить их внутри экземпляра класса C ++ для обратного вызова, не передавая их как void *. Как только callback-функция C ++ вызывается функцией signal (), я ожидаю, что смогу преобразовать их в удобную (вызываемую) форму с помощью class_getInstanceMethod и method_getImplementation.

Кроме того, я, вероятно, захочу послать хотя бы один параметр с произвольной структурой («EventInfo») в слот из сигнала, который может предоставить информацию о природе сигнала.

Может кто-нибудь, пожалуйста, пролить свет на тьму?

1

Решение

Это заняло у меня много времени, но я наконец понял это. Возможно, есть более простые способы сделать это, но я обнаружил, что мне нужно создать класс C ++ в файле .mm, который действует как мост между сигналом boost :: signal2 и функцией обратного вызова Objective-C:

В CPPToCocoaModelMessageCallbacks.h:

/* ------------------------------------------------------------------------
class CPPToCocoaModelMessageCallback -
--------------------------------------------------------------------------- */
class CPPToCocoaModelMessageCallback
{
public:
CPPToCocoaModelMessageCallback( PMD_Signal_Messenger<PrefEvent> *theSignalClass,
int         whichPrefIdxToObserve,
id          pObjCClass,
SEL         pObjCMethod);

~CPPToCocoaModelMessageCallback();

void    CallBackMessage(PrefEvent* pPrefEvent);private:

id          fpObjCClass;
SEL         fpObjCMethod;

ls_index    fWhichPrefIdxToObserve;

boost::signals2::connection fTheConnection;

};  // CPPToCocoaModelMessageCallback

В CPPToCocoaModelMessageCallbacks.mm

/* ------------------------------------------------------------------------
CPPToCocoaModelMessageCallback - CONSTRUCTOR

whichPrefIdxToObserve - the preference idx to observe

Pass the id and selector of the Objective-C/C++ object & method to be
called.
--------------------------------------------------------------------------- */
CPPToCocoaModelMessageCallback::CPPToCocoaModelMessageCallback(PMD_Signal_Messenger<PrefEvent> *theSignalClass, int whichPrefIdxToObserve, id pObjCClass, SEL pObjCMethod)
:   fpObjCClass (pObjCClass),
fpObjCMethod (pObjCMethod),
fWhichPrefIdxToObserve (whichPrefIdxToObserve)
{
fTheConnection = theSignalClass->ObserveSignal(&CPPToCocoaModelMessageCallback::CallBackMessage, this);
}

/* ------------------------------------------------------------------------
~CPPToCocoaModelMessageCallback - DESTRUCTOR

Pass the id and selector of the Objective-C/C++ object & method to be
called.
--------------------------------------------------------------------------- */
CPPToCocoaModelMessageCallback::~CPPToCocoaModelMessageCallback()
{
fTheConnection.disconnect();
}/* ------------------------------------------------------------------------
CPPToCocoaModelMessageCallback::CallBackMessage -

Handles single and range-type preference change events.
--------------------------------------------------------------------------- */
void CPPToCocoaModelMessageCallback::CallBackMessage(PrefEvent* pPrefEvent)
{
// Only make the callback if this event is the preference we're observing

if (pPrefEvent->GetChangedPrefIdx() == fWhichPrefIdxToObserve) {
[fpObjCClass performSelector:fpObjCMethod];
}
}

////////////////////////////////////////////////// /////////////////////////////

В вашем контроллере

// set up messaging from Model.  The message callback functions must be destructed in dealloc.
// I've done this in awakeFromNib but it could be elsewhere

- (void)awakeFromNib {

PMD_Signal_Messenger<MyEventKind>* theModelClass = GetMyModelClassPointer();

displayMenuPrefChangedCallBack = new CPPToCocoaModelMessageCallback(theModelClass, kAppPrefDictionaryDisplayShortDef, self, @selector(displayMenuChanged));
}/* ------------------------------------------------------------------------
displayMenuChanged - this gets called when the model fires a signal
(via CPPToCocoaModelMessageCallback::CallBackMessage())

--------------------------------------------------------------------------- */
- (void) displayMenuChanged
{
NSLog(@"displayMenuChanged\n");

// DO SOMETHING TO RESPOND TO THE SIGNAL (in this case I'm reloading an NSWebView):

[self reloadWebViewText];
}

////////////////////////////////////////////////// ////////////////////////////

Класс для объединения с классом модели для сигнализации наблюдателей:

PMD_Signal_Messenger.h:

/* ------------------------------------------------------------------------
class PMD_Signal_Messenger<MyEventKind> -

This class is designed to be multiple inherited with various
Model classes.
--------------------------------------------------------------------------- */
template <class MyEventKind>
class PMD_Signal_Messenger {
public:

PMD_Signal_Messenger() { }
~PMD_Signal_Messenger() { }

template<typename Fn, typename Obj>
boost::signals2::connection ObserveSignal(Fn callback, Obj &object) {
return fSignalObservers.connect(boost::bind(callback, object, _1));
}

protected:
boost::signals2::signal<void (MyEventKind*)> fSignalObservers;  // all observers of my preference changes

private:
PMD_Signal_Messenger(const PMD_Signal_Messenger& thePMD_Signal_Messenger)   { assert(false); }  // prevent copy constructor
};

В файле MODEL .cpp, где вы хотите сообщить об изменении модели:

// construct theEvent (your own struct) and fire the signal with your event structure that gets passed to CPPToCocoaModelMessageCallback::CallBackMessage()

MyEventKind theEvent(someUsefulParams);

fSignalObservers(&theEvent);
1

Другие решения

Вы можете использовать это решение:
https://github.com/godexsoft/objc_callback

#pragma once
#ifndef _OBJC_CALLBACK_H_
#define _OBJC_CALLBACK_H_

template<typename Signature> class objc_callback;

template<typename R, typename... Ts>
class objc_callback<R(Ts...)>
{
public:
typedef R (*func)(id, SEL, Ts...);

objc_callback(SEL sel, id obj)
: sel_(sel)
, obj_(obj)
, fun_((func)[obj methodForSelector:sel])
{
}

inline R operator ()(Ts... vs)
{
return fun_(obj_, sel_, vs...);
}
private:
SEL sel_;
id obj_;
func fun_;
};

#endif // _OBJC_CALLBACK_H_
1