Is it possible to implement events in C++?

前端 未结 4 1333
不思量自难忘°
不思量自难忘° 2021-02-07 10:31

I wanted to implement a C# event in C++ just to see if I could do it. I got stuck, I know the bottom is wrong but what I realize my biggest problem is...

How do I overlo

4条回答
  •  灰色年华
    2021-02-07 11:26

    If you can use Boost, consider using Boost.Signals2, which provides signals-slots/events/observers functionality. It's straightforward and easy to use and is quite flexible. Boost.Signals2 also allows you to register arbitrary callable objects (like functors or bound member functions), so it's more flexible, and it has a lot of functionality to help you manage object lifetimes correctly.


    If you are trying to implement it yourself, you are on the right track. You have a problem, though: what, exactly, do you want to do with the values returned from each of the registered functions? You can only return one value from operator(), so you have to decide whether you want to return nothing, or one of the results, or somehow aggregate the results.

    Assuming we want to ignore the results, it's quite straightforward to implement this, but it's a bit easier if you take each of the parameter types as a separate template type parameter (alternatively, you could use something like Boost.TypeTraits, which allows you to easily dissect a function type):

    template 
    class MyEvent
    {
        typedef void(*FuncPtr)(TArg0);
        typedef std::deque FuncPtrSeq;
    
        FuncPtrSeq ls;
    public:
        MyEvent& operator +=(FuncPtr f)
        {
            ls.push_back(f);
            return *this;
        }
    
        void operator()(TArg0 x) 
        { 
            for (typename FuncPtrSeq::iterator it(ls.begin()); it != ls.end(); ++it)
                (*it)(x);
        }
    };
    

    This requires the registered function to have a void return type. To be able to accept functions with any return type, you can change FuncPtr to be

    typedef std::function FuncPtr;
    

    (or use boost::function or std::tr1::function if you don't have the C++0x version available). If you do want to do something with the return values, you can take the return type as another template parameter to MyEvent. That should be relatively straightforward to do.

    With the above implementation, the following should work:

    void test(float) { }
    
    int main() 
    {
        MyEvent e;
        e += test;
        e(42);
    }
    

    Another approach, which allows you to support different arities of events, would be to use a single type parameter for the function type and have several overloaded operator() overloads, each taking a different number of arguments. These overloads have to be templates, otherwise you'll get compilation errors for any overload not matching the actual arity of the event. Here's a workable example:

    template 
    class MyEvent
    {
        typedef typename std::add_pointer::type FuncPtr;
        typedef std::deque FuncPtrSeq;
    
        FuncPtrSeq ls;
    public:
        MyEvent& operator +=(FuncPtr f)
        {
            ls.push_back(f);
            return *this;
        }
    
        template 
        void operator()(TArg0 a1) 
        { 
            for (typename FuncPtrSeq::iterator it(ls.begin()); it != ls.end(); ++it)
                (*it)(a1);
        }
    
        template 
        void operator()(const TArg0& a1, const TArg1& a2)
        {
            for (typename FuncPtrSeq::iterator it(ls.begin()); it != ls.end(); ++it)
                (*it)(a1, a2);
        }
    };  
    

    (I've used std::add_pointer from C++0x here, but this type modifier can also be found in Boost and C++ TR1; it just makes it a little cleaner to use the function template since you can use a function type directly; you don't have to use a function pointer type.) Here's a usage example:

    void test1(float) { }
    void test2(float, float) { }
    
    int main()
    {
        MyEvent e1;
    
        e1 += test1;
        e1(42);
    
        MyEvent e2;
        e2 += test2;
        e2(42, 42);
    }
    

提交回复
热议问题