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

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

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

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

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

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

Кто-нибудь может пролить свет на тьму?


person SMGreenfield    schedule 05.06.2013    source источник


Ответы (2)


Это заняло у меня много времени, но я наконец понял это. Это могут быть более простые способы сделать это, но я обнаружил, что мне нужно создать класс 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];
    }
}

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

В вашем controller.mm:

// 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
};

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

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

MyEventKind theEvent(someUsefulParams);

fSignalObservers(&theEvent);
person SMGreenfield    schedule 14.06.2013

Вы можете использовать это решение: 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_
person Histrion    schedule 09.12.2015