尝试使用>>创建一个新的事件处理程序重载的运算符,迷失在试图找出所需语法的过程中

发布于 2024-12-28 07:12:12 字数 6065 浏览 2 评论 0原文

编辑、澄清: *我希望能够使用我自己的类 EventListener 使用重载运算符 >> 设置 eventHandler,但我无法弄清楚它的语法*

问题是重载的 >>= 运算符不会匹配参数化的 eventHandler,我不知道如何正确声明它!

或者

如果我使其匹配,则将通用 T 类型转换为像样有用的 eventHandler 对象所需的信息将消失!我需要一个混合语法来声明一个泛型类型 T 和第二个泛型类型作为它的参数。

并且使用 eventHandler 类的父类,efunctor 也没有用,因为它没有参数化。

示例:

链接>> new eventHandler(&App::testFunction, app); /*其中 app 是 App 类的实例,testFunction 是 App 的方法。 */

我有一个名为 eventHandler 的类,它采用这种形式 eventHandler。现在,我想重载某个类的 >> 运算符,以允许我像这样使用它:

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

其中 App 是另一个类,而 testFunction 显然是该类的方法。编写的abose 行应该具有对链接类的>>= 运算符的调用(方便地称为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;
}

我的问题是如何从 Link 声明方法 operator>>= ,以便自动启用这种功能。

澄清: - 我希望能够使用 Link 类上的 >> 运算符向 EventListener 类添加一个新的 eventHandler,该 Link 类具有实际的 eventHandler 作为右侧操作数;

编辑1:

我还有第二个问题: 如何声明 >> 重载函数,以便使用指向 Link 类的指针调用我...这意味着如果我想使用 link 我该怎么办用 >> 而不是 link1

对第 2 部分的回答: 我的猜测是,我只需取消引用指针即可使用运算符重载。

另一个编辑

这是代码的一个版本,可以更好地解释它。我的问题是编译器在 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

EDIT, CLARIFICATION:
*I want to be able to set eventHandlers using my own class EventListener using an overloaded operator >> and I can't figure out the syntax for it*

The problem is that the overloaded >>= operator will not match a parameterized eventHandler, I do not know how to declare it properly!!

OR

If I make it match, the information required to convert the generic T type to a decent useful eventHandler object is gone!! I need a mixed syntax for declarind a generic type T and a second generic type as it parameter .

And using the parent of eventHandler class, the efunctor is also useless as it is not parameterized.

Example:

Link >> new eventHandler(&App::testFunction, app); /*where app is an istance of App class and testFunction is a method of App. */

I have a class called eventHandler that goes under this form eventHandler<class T>. Now, I want to overload the >> operator of some class to allow me to use it like this:

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

Where App is another class, and testFunction is, obviously, a method of that class. The line written abose should have as an effect a call to the >>= operator of the link's class (called, conveniently,Link).

I will post part of my code to make it more clearly:

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

MY question is about how to declare the method operator>>= from Link so that automatically will enable me that kind of functionality.

Clarifications:
- I want to be able to add to the EventListener class a new eventHandler using the >> operator on the Link class having as right hand operand an actual eventHandler;

EDIT 1:

And I have a second question:
How to declare the >> overloaded function so that I will be called with pointer to the Link class... meaning what can I do if I want to use link with >> instead of link1.

Answer to PART2: My guess is that I just have to dereference the pointer in order to use the operator overloading.

ANOTHER EDIT

This is a version of the code that might explain it a little better. My problem is where the compiler fails, at matching >>= operator inside Link class, from there if that is solved properly it might work, the problem is that I cannot find how to match it and keep the event signature.

And now, I better let the code speak for itself:

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

I will also post my events definitions:

#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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

残花月 2025-01-04 07:12:12

除了过度使用 new 之外,我认为在尝试这样的事情之前,您还需要从概念上弄清楚其他一些事情。如果需要的话,重载 operator>>() 来调用 operator>>=() 的合适成员版本实际上没有任何问题,尽管它甚至可能不会执行此操作所必需的:如果您控制移位运算符的左侧参数,则可以根据需要将其设为成员。它只是不能以这种方式与 std::istream 一起使用,因为您无法控制标准 C++ 库类 [模板]。

在进一步讨论之前,让我们回答您首先添加的问题:您不能重载任何仅涉及内置类型的运算符。指针被视为内置类型。故事结束。无论如何,您应该使用指针:它们是您在某些地方需要的低级抽象,但在更高级别上您想要处理适当类的对象。这不会以任何方式让事情变得更困难,但会让事情变得更容易。

在您的代码中突然出现一些模板模板:这几乎肯定是不必要的,而是您问题的一部分。另外,如果您希望事件接收器具有某种多态性,则需要在某个地方有一个小的继承层次结构:您将定义一个基类,该基类公开用于触发事件的接口,以及一个模板化派生类,该派生类实现一些虚函数由该接口定义。当调用事件时,基类仅调用虚函数,甚至接收它。这就是 std::function有效工作的方式。

我认为您的问题中有太多未定义的内容,无法用有效实现您想要的内容的代码示例来回答。就我个人而言,我会遵循 std::function<> 的模型并定义一个事件模板,该模板将要触发的事件的签名作为参数,将具有相应签名的函数对象作为接收者。也就是说,您订阅的事件如下所示:

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

如果您想添加一个具有要在其上调用的特定成员的对象,您将执行一些操作,这是一个常见的用例,您可以创建一个 EventHandler 与 this 一起使用的类模板,它有效地执行以下操作

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");
}

请注意,有明显的扩展,例如使参数类型成为模板参数。这相当于在不同的地方添加这个模板参数,但总体方法保持不变。

Apart from the excessive use of new there are other things which I think you need to get conceptually clear before trying something like this. There isn't really any problem in overloading operator>>() to call a suitable member version of operator>>=() if necessary although it might not even necessary to do this: if you control the left hand argument of your shift operator, you can make this a member if you want to. It just doesn't work with std::istream this way because you can't control the standard C++ library class [template].

Before going any further, let's answer the question you added first: you cannot overload any operator which involves only built-in types. Pointers are considered built-in types. End of story. You should be using pointers anyway: they are a low-level abstraction you need in some place but on a higher level you want to deal with objects of appropriate classes. This doesn't make things harder in any way but it makes things easier.

In your code all of a sudden some template template shows up: this is almost certainly not necessary and rather part of your problem. Also, if you want your events receiver to be in some sort polymorphic, you will need a small inheritance hierarchy somewhere: you would define a base class which exposes the interface use to trigger an event and a templated derived class which implements the few virtual functions defined by this interface. When an event is invoked the base just calls the virtual function and the even receives it. This is how std::function<Signature> effectively works.

I think there are too many undefined things in your question to answer it with a code example which effectively implements what you want. Personally, I would follow the model of std::function<> and define an event template which take the signatures of events to be triggered as argument and function object with the corresponding signature as receivers. That is, the event you subscribe to looks like this:

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

If you want to add an object with a specific member to be called on it you would do something and this is a common use case, you can create an EventHandler class template to be used with this which effectively does something along the lines of

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

(this just uses std::bind() for brevity; if you want to support function with a fixed number of arguments this is easily implemented, assuming you cannot use std::bind() or boost::bind() although you don't want to reimplement bind(): this is a fairly hairy thing to do).

Here is a complete and working example which I think shows all the ingredients (and gets as close as what I think you actually want as possible):

#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");
}

Note, that there are obvious extensions like making the argument type a template argument. This amounts to just adding this template argument in various places but the overall approach stays the same.

提笔落墨 2025-01-04 07:12:12

我的猜测是,您需要使您的 eventHandler 派生自 eventHandlerBase 这样的类,以便您可以引用任何 eventHandler > 任何类型 T,通过指向 eventHandlerBase 的指针...即 Link::EventListener 声明为类型 eventHandlerBase *.

那么类似这样的事情可能会起作用:(

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

并且没有 Link() 中的 Link::EventListenernew 语句。)

My guess is that you will need to make your eventHandler<T> derive from a class like eventHandlerBase so you can refer to any eventHandler<T> of any type T by having a pointer to eventHandlerBase... i.e. Link::EventListener delcared as type eventHandlerBase *.

Then something like this might work:

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

(and without the new statement for Link::EventListener within Link().)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文