Listening to ofEvents with objective C classes as targets?

Hi,

I want to listen to some ofEvents from an objective C class. It has to be an objective C class since it inherits from UIViewController and displays native iOS views.

I had problems with giving an objective C method as the target, since the signatures don’t match.

After some investigating (and googling), I found a scheme that works. I made a cpp class to use as an adapter:

class EventsAdapter
{
    MapUIViewController* m_target;
    
public:
    
    EventsAdapter(MapUIViewController* target):
    m_target(target){}
    
    void onLocationModeChanged(Trailze::UIController::LocationMode& locationMode)
    {
        [m_target onLocationModeChanged:locationMode];
    }
};

When registering a listener, I give the adapter:

m_eventsAdapter = std::shared_ptr<EventsAdapter>(new EventsAdapter(self));
ofAddListener(m_uiController->locationModeChangedEvent, m_eventsAdapter.get(), &EventsAdapter::onLocationModeChanged);

that calls my objective C method:

[m_target onLocationModeChanged:locationMode];

which looks like this:

- (void) onLocationModeChanged:(Trailze::UIController::LocationMode &)locationMode{
    
    ofLogVerbose("!") << "great success!";
}

It works. But that’s allot of pipework just to call one event, and it’s not scalable.

I was thinking of making a generic adapter template method that will hold a pointer to the objective C selector and will call it, but I’m not sure if it’s needed or even doable…

I would love some opinions before I give this a shot…

Thanks,

Tal

No takers on this? Well, I went ahead and made a working version of a the handler, and here it is:

template <typename ArgumentsType>
class ofEventAdapter {
    
    ofEvent<ArgumentsType>& m_ofEvent;
    id m_targetInstance;
    SEL m_targetSelector;
    
public:
    ofEventAdapter(ofEvent<ArgumentsType>& ofEvent, id targetInstance, SEL targetSelector):
        m_ofEvent(ofEvent),
        m_targetInstance(targetInstance),
        m_targetSelector(targetSelector)
    {
        ofAddListener(m_ofEvent, this, &ofEventAdapter<ArgumentsType>::eventCallback);
    }
    
    ofEventAdapter()
    {
        ofRemoveListener(m_ofEvent, this, &ofEventAdapter::eventCallback);
    }
    
    void eventCallback(ArgumentsType& args)
    {
        if (![m_targetInstance respondsToSelector:m_targetSelector])
        {
            return; //can't call selector!
        }
        
        [m_targetInstance performSelector:m_targetSelector withObject:(id)(&args)];
    }
};

Usage:

ofEventAdapter<[AnyType]>
        ([ofEvent from the corresponding type], self, @selector([your objectiveC callback method]]))

It reduced my boilerplate code drastically, but it can still be improved. I envision it as being two static methods, like the original ofAddListener/ofRemoveListener, but that will require a template method, and for each type, it will need to go to a different map of events to targets, and I don’t have the time to set such a thing up right now.

UPDATE2:

I made a seamless version that hides the details from the user. Usage:

ofAddListener([ofEventInstance], self, @selector(objectiveCMethod:));

As simple as that. Here’s the implementation if someone needs it and I plan to suggest this in a pull request sometimes.

class ofEventAdapterBase{
    
protected:
    id m_targetInstance;
    SEL m_targetSelector;
    
public:
    
    ofEventAdapterBase(id targetInstance, SEL targetSelector):
    m_targetInstance(targetInstance),
    m_targetSelector(targetSelector)
    {}
    
    id getTargetInstance(){return m_targetInstance;}
    SEL getTargetSelector(){return m_targetSelector;}
};

template <typename ArgumentsType>
class ofEventAdapter : public ofEventAdapterBase {
    
    ofEvent<ArgumentsType>& m_ofEvent;
    
    
public:
    ofEventAdapter(ofEvent<ArgumentsType>& ofEvent, id targetInstance, SEL targetSelector):
    ofEventAdapterBase(targetInstance, targetSelector),
    m_ofEvent(ofEvent)
    {
        ofAddListener(m_ofEvent, this, &ofEventAdapter<ArgumentsType>::eventCallback);
    }
    
    ~ofEventAdapter(){
        ofRemoveListener(m_ofEvent, this, &ofEventAdapter::eventCallback);
    }
   
    void eventCallback(ArgumentsType& args){
        if (![m_targetInstance respondsToSelector:m_targetSelector]){
            return; //can't call selector!
        }
        
        [m_targetInstance performSelector:m_targetSelector withObject:(id)(&args)];
    }
};

typedef std::shared_ptr<ofEventAdapterBase> Target;
typedef std::shared_ptr<std::list<Target> > Targets;
typedef std::map<void*, Targets> Events;

Events m_events;

template <typename ArgumentsType>
void ofAddListener(ofEvent<ArgumentsType>& ofEvent, id targetInstance, SEL targetSelector){
    Targets targets;
    
    if (m_events.find(&ofEvent) != m_events.end()){
        targets = m_events[&ofEvent];
    }
    else{
        targets = Targets(new std::list<Target>());
        m_events.insert(std::pair<void*, Targets>(&ofEvent, targets));
    }
    
    Target target(new ofEventAdapter<ArgumentsType>(ofEvent, targetInstance, targetSelector));

    targets->push_back(target);
}

template <typename ArgumentsType>
void ofRemoveListener(ofEvent<ArgumentsType>& ofEvent, id targetInstance, SEL targetSelector){
    if (m_events.find(&ofEvent) == m_events.end())
        return;
    
    Targets targets = m_events[&ofEvent];
    
    std::list<Target>::iterator iter;
    
    for(iter = targets->begin(); iter != targets->end(); iter++){
        
        if (iter->get()->getTargetInstance() == targetInstance &&
            iter->get()->getTargetSelector() == targetSelector)
        {
            iter = targets->erase(iter);
        }
    }
    
    if (targets->empty())
    {
        m_events.erase(&ofEvent);
    }
}