Пытаясь создать новый eventHandler, используя >> перегруженный оператор, я потерял себя, пытаясь выяснить необходимый синтаксис - PullRequest
1 голос
/ 22 января 2012

РЕДАКТИРОВАТЬ, УТОЧНИТЬ: * Я хочу иметь возможность устанавливать EventHandlers, используя мой собственный класс EventListener, используя перегруженный оператор >>, и я не могу понять синтаксис для него *

Проблема в том, что перегруженный оператор >>= не будет соответствовать параметризованному eventHandler, я не знаю, как его правильно объявить !!

OR

Если я сопоставлю его, информация, необходимая для преобразования общего типа T в достойный полезный объект eventHandler, будет исчез !! Мне нужен смешанный синтаксис для определения обобщенного типа T и второго универсального типа в качестве его параметра.

И, используя родителя класса eventHandler, efunctor также бесполезен, так как не параметризован.

Пример:

Ссылка >> new eventHandler (& App :: testFunction, app); /*where app is an istance of App class and testFunction is a method of App. */

У меня есть класс с именем eventHandler, который идет под этой формой eventHandler<class T>. Теперь я хочу перегрузить оператор >> некоторого класса, чтобы позволить мне использовать его следующим образом:

link >> new eventHandler(&App::testFunction, App);

Где App - это другой класс, а testFunction - это, очевидно, метод этого класса. Строка, написанная выше, должна иметь эффект вызова оператора >>= класса ссылки (обычно называемого Link).

Я опубликую часть своего кода, чтобы сделать его более понятным:

class Link
{
public:
    eventListener* EventListener;
    Link()
    {
        this->EventListener = new eventListener();
    }   

    // PROBLEM
    // I am lost here, tried different syntaxes but with no success
    //template<template<class G> class T, class F> 

    template <class T>
    Link operator>>=(T& ev)
    { 
        cout << "something";
         // Here there is no way to declare the proper eventHandler
        eventHandler<?>* event = (eventHandler<?>)ev;
         // I need something like T<F> 
        event->eventName = 'onTest';
        this->eventListener->add(event);
        return *this; 
} 

};

template<class T, class F> 
T operator>>(T& lhs, F& rhs)
{ 
    return T(lhs)>>=rhs;
} 

class App
{
public:
    void testFunction(e evt)
    {
        cout << "it works!" << "\n";
    }
};

int main()
{
    App* app = new App;
    Link* link = new link;
    Link link1;

    eventHandler<App>* ev = new eventHandler<App>(app, &App::testFunction);
    link1 >> ev;    

    // this line should echo "it works!"
    link1.EventListener->triggerEvent("onTest");

    // PART 2
    // HOW CAN I USE?
    // link >> ev; 
    // when link is a Link*

    return 0;
}

Мой вопрос касается того, как объявить метод operator>>= из Link, чтобы он автоматически включал мне такую ​​функциональность.

Разъяснения: - Я хочу иметь возможность добавить в класс EventListener новый eventHandler, используя оператор >> класса Link, имеющий в качестве правого операнда фактический eventHandler;

РЕДАКТИРОВАТЬ 1:

И у меня второй вопрос: Как объявить перегруженную функцию >> так, чтобы меня вызывали с указателем на класс Link ... что делать, если я хочу использовать link с >> вместо link1.

Ответ на PART2: Полагаю, мне просто нужно разыменовать указатель, чтобы использовать перегрузку оператора.

ДРУГОЕ РЕДАКТИРОВАНИЕ

Это версия кода, которая может объяснить его немного лучше. Моя проблема заключается в том, что при сбое компилятора при сопоставлении оператора >>= внутри класса Link оттуда, если это решено правильно, он может работать, проблема в том, что я не могу найти способ сопоставить его и сохранить подпись события.

А теперь лучше пусть код говорит сам за себя:

#include <iostream>
#include <stdio.h>
#include "events/events.h"
using namespace std;

class Link
{
public:
    eventListener* EventListener;

public:
    Link()
    {
        this->EventListener = new eventListener();
    }   

    template<class T>
    Link operator>>=(const T& ev)
    { 
        ev->eventName = "onReceive";
        this->EventListener->add(ev);
        return *this; 
    }
};

template<class T, class F> 
T operator>>(T& lhs, const F& rhs)
{ 
    return T(lhs)<<=rhs;
} 

class App
{
public:
    void testReceive(e evt)
    {
        cout << "it works" << "\n" << evt.value;
    }

};

class demo
{
public:
    Link* parent;
    void testit(char* msg)
    {
        parent->EventListener->triggerEvent("onReceive", this, msg);
    }
};

int main()
{
    App* app = new App;
    Link link;

    eventHandler<App>* ev = new eventHandler<App>(app, &App::testReceive);
    link >> ev; 

    demo d;
    d.parent = &link;
    // should output "it works!"
    d.testit("here");

    return 0;
}

Я также опубликую свои определения событий:

#ifndef EVENTS_H
#define EVENTS_H
#include <stdio.h>
#include <string.h>
#include "clist.h"
#include <string>

enum scope {global = 0, scoped};

struct e
{
    void* target;
    void* value;
};

class efunctor //abstract
{
public:
    std::string eventname;
    virtual void operator()(e evt)
    { }

    virtual void Call(e evt)
    { }

};

template <class T>
class eventHandler : public efunctor
{
private:
    T* scope;
    void (T::*eventMethod)(e);
public:
    std::string name;
    std::string eventname;
    eventHandler(std::string eventnam, T* objscope,  void(T::*func)(e)) 
    { 
        this->scope = objscope;
        this->eventMethod = func;
        this->eventname = eventnam;
    }

    eventHandler(T* objscope,  void(T::*func)(e)) 
    { 
        this->scope = objscope;
        this->eventMethod = func;
    }

    eventHandler(void(T::*func)(e)) 
    {
        this->eventMethod = func;
    }

    void operator()(e evt)
    {
        (scope->*eventMethod)(evt);
    }          

    void Call(e evt)
    { 
        (scope->*eventMethod)(evt);
    }
};

class eventListener
{
private:
    clist< clist<efunctor* > > methods;
public:

    template <class T>
    void add(T other)
    {
        other->name = ToString(this->methods[other->eventname].length());
        methods[other->eventname][methods[other->eventname].length()] = other;
    }

    template <class T>
    void remove(T other)
    {
        methods[other->eventname]->remove(other->name);
    }

    template <class F>
    void triggerEvent(std::string name, void* target, F result)
    {
        e evt;
        evt.target = target;
        evt.value = (char*)result;
        for(methods[name].iterateStart(); 
                    !methods[name].eoi(); 
                    methods[name].next())
        {
            (*(methods[name].getCurrentIteration()))(evt);
        }
    }
};
#endif

Ответы [ 2 ]

1 голос
/ 22 января 2012

Полагаю, вам нужно, чтобы ваш eventHandler<T> был производным от класса, подобного eventHandlerBase, чтобы вы могли ссылаться на любой eventHandler<T> любого типа T, имея указатель на eventHandlerBase ... то есть Link::EventListener как тип eventHandlerBase *.

Тогда что-то вроде этого может сработать:

template<typename T>
const link &operator>>=(const eventHandler<T>& ev)
{ 
    ev->eventName = "onTest";
    this->EventListener = &ev;
}

(и без оператора new для Link::EventListener в Link().)

1 голос
/ 22 января 2012

Помимо чрезмерного использования new есть и другие вещи, которые, я думаю, вам необходимо прояснить концептуально, прежде чем пытаться что-то подобное.На самом деле нет никакой проблемы в перегрузке operator>>() для вызова подходящей версии члена operator>>=(), если это необходимо, хотя это может даже не потребоваться: если вы управляете левым аргументом своего оператора сдвига, вы можете сделать эточлен, если вы хотите.Это просто не работает с std::istream таким образом, потому что вы не можете управлять стандартным классом библиотеки C ++ [template].

Прежде чем идти дальше, давайте сначала ответим на вопрос, который вы добавили: вы не можете перегрузить любойоператор, который включает только встроенные типы.Указатели считаются встроенными типами.Конец истории.В любом случае вы должны использовать указатели: это низкоуровневая абстракция, которая вам нужна в каком-то месте, но на более высоком уровне вы хотите иметь дело с объектами соответствующих классов.Это никоим образом не усложняет ситуацию, но облегчает ее.

В вашем коде неожиданно появляется какой-то шаблон шаблона: это почти наверняка не нужно и скорее является частью вашей проблемы.Кроме того, если вы хотите, чтобы ваш получатель событий был в некотором роде полиморфным, вам понадобится небольшая иерархия наследования: вы должны определить базовый класс, который представляет использование интерфейса для запуска события, и шаблонный производный класс, который реализует несколько виртуальных функций.определяется этим интерфейсом.Когда событие вызывается, база просто вызывает виртуальную функцию и даже получает ее.Вот как эффективно работает std::function<Signature>.

Я думаю, что в вашем вопросе слишком много неопределенных вещей, чтобы ответить на него примером кода, который эффективно реализует то, что вы хотите.Лично я следовал бы модели std::function<> и определял шаблон событий, который принимает сигнатуры событий, которые должны запускаться в качестве аргумента, и объект функции с соответствующей сигнатурой в качестве получателей.То есть событие, на которое вы подписываетесь, выглядит следующим образом:

event<void(std::string)> onTest;

Если вы хотите добавить объект с определенным членом, который будет вызываться для него, вы сделаете что-то, и это обычный вариант использования, выможет создать шаблон класса EventHandler, который будет использоваться с этим, который эффективно делает что-то вроде

onTest >> std::bind(std::mem_fn(&App::testFunction), std::ref(app), _1);

(это просто для краткости использует std::bind(); если вы хотите поддерживать функцию с фиксированным числомаргументов это легко реализовать, предполагая, что вы не можете использовать std::bind() или boost::bind(), хотя вы не хотите переопределять bind(): это довольно сложная вещь).

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

#include <iostream>
#include <memory>
#include <algorithm>
#include <vector>

struct event_base
{
    virtual ~event_base() {}
    virtual void call(std::string const&) = 0;
};

template <typename T>
struct event: event_base
{
    event(T* o, void (T::*m)(std::string const&)):
        object(o),
        member(m)
    {
    }

private:
    void call(std::string const& arg) {
        (this->object->*member)(arg);
    }
    T*   object;
    void (T::*member)(std::string const&);
};


template <typename T>
event_base* make_event(T* object, void (T::*member)(std::string const&)) {
    return new event<T>(object, member);
}

class event_listener
{
public:
    ~event_listener() { std::for_each(events.begin(), events.end(), &deleter); }
    void add(event_base* event) { this->events.push_back(event); }
    void trigger(std::string const& argument) const {
        for (std::vector<event_base*>::const_iterator it(events.begin()), end(events.end());
             it != end; ++it) {
            (*it)->call(argument);
        }
    }

private:
    static void deleter(event_base* ptr) { delete ptr; }
    std::vector<event_base*> events;
};

class Link
{
public:
    event_listener listener;
    void operator>>= (event_base* ev) { this->listener.add(ev); }
};

void operator<< (Link& link, event_base* ev) {
    link >>= ev;
}

class App
{
public:
    void onTest(std::string const& arg) { std::cout << "App::onTest(" << arg << ")\n"; }
};

int main()
{
    std::auto_ptr<Link> link(new Link);
    std::auto_ptr<App>  app(new App);

    *link << make_event(app.get(), &App::onTest);
    link->listener.trigger("hello event");
}

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

...