C# style Event and Delegate in C++

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
1
down vote

favorite












I'd appreciate advice regarding both the implementation detail and ways to improve usage/performance/generic.



There are two parts: XDelegate, which is I got from here, and XEvent, which is the event implementation similar to C#.



#pragma once
#include <unordered_map>
//hashing function
//TODO: replace hashing with something else if it's too expensive
template<typename return_type, typename... params>
class XDelegate;
namespace std
template<typename return_type, typename... params>
struct hash<XDelegate<return_type, params ...>>
typedef return_type(*Type)(void* callee, params...);

static size_t hash_combine(size_t seed, size_t v)

return seed ^= v + 0x9e3779b9 + (seed << 6) + (seed >> 2);

size_t operator()(const XDelegate<return_type, params ...> &pDelegate) const

size_t a = std::hash<void*>(pDelegate.fpCallee);
size_t b = std::hash<Type>(pDelegate.fpCallbackFunction);
return hash_combine(a, b);

;
;

//nice generic variadic delegate from http://blog.coldflake.com/posts/C++-delegates-on-steroids/
template<typename return_type, typename... params>
class XDelegate

friend struct std::hash<XDelegate>;

typedef return_type(*Type)(void* callee, params...);
public:
XDelegate(void* callee, Type function)
: _fpCallee(callee)
, _fpCallbackFunction(function)
template <class T, return_type(T::*TMethod)(params...)>
static XDelegate from_function(T* callee)

XDelegate d(callee, &methodCaller<T, TMethod>);
return d;

return_type operator()(params... xs) const

return (*_fpCallbackFunction)(_fpCallee, xs...);

private:
void* _fpCallee;
Type _fpCallbackFunction;
template <class T, return_type(T::*TMethod)(params...)>
static return_type methodCaller(void* callee, params... xs)

T* p = static_cast<T*>(callee);
return (p->*TMethod)(xs...);

;


//C# style delegate-event for none ref class.
template<typename return_type, typename... params>
class XEvent

friend struct std::hash<XDelegate<return_type, params ...>>;

public:
size_t add(XDelegate<return_type, params ...> pDelegate)

size_t token = std::hash<XDelegate<return_type, params ...>>(pDelegate);
auto result = _delegates.insert_or_assign(token, pDelegate);
#ifdef _DEBUG
//if assignment took place, ie) there is a hash collision, throw exception.
if (!result.second)

throw std::exception("XEvent hash collision");

#endif
return token;

void remove(size_t pToken)

_delegates.erase(pToken);

size_t size() const

return _delegates.size();

//on hindsight, I don't think C# events have return value?
std::unordered_map<size_t, return_type> operator()(params... xs)const

std::unordered_map<size_t, return_type> result;
for (auto itr = _delegates.begin(); itr != _delegates.end(); ++itr)

result[itr->first] = itr->second(xs...);


//return map of event tokens mapped to the results.
return result;

private:


std::unordered_map<size_t, XDelegate<return_type, params ...>> _delegates;
;


template<typename... params>
class XEvent<void, params ...>

friend struct std::hash<XDelegate<void, params ...>>;

public:
size_t add(XDelegate<void, params ...> pDelegate)

size_t token = std::hash<XDelegate<void, params ...>>(pDelegate);
auto result = _delegates.insert_or_assign(token, pDelegate);
#ifdef _DEBUG
//if assignment took place, ie) there is a hash collision, throw exception.
if (!result.second)

throw std::exception("XEvent hash collision");

#endif
return token;

void remove(size_t pToken)

_delegates.erase(pToken);

size_t size() const

return _delegates.size();

void operator()(params... xs)const

for (auto itr = _delegates.begin(); itr != _delegates.end(); ++itr)

itr->second(xs...);


private:
std::unordered_map<size_t, XDelegate<void, params ...>> _delegates;
;


It is used like this:



_returnEventTokens.push_back(_returnEvent.add(XDelegate<int, std::wstring>::from_function<EventTester, &EventTester::returnEventCallback1>(_tester)));
_returnEventTokens.push_back(_returnEvent.add(XDelegate<int, std::wstring>::from_function<EventTester, &EventTester::returnEventCallback2>(_tester)));
_returnEventTokens.push_back(_returnEvent.add(XDelegate<int, std::wstring>::from_function<EventTester, &EventTester::returnEventCallback3>(_tester)));


_returnEvent(L"Return event called!n");

for (auto itr = _noReturnEventTokens.begin(); itr != _noReturnEventTokens.end(); ++itr)

_noReturnEvent.remove(*itr);







share|improve this question



























    up vote
    1
    down vote

    favorite












    I'd appreciate advice regarding both the implementation detail and ways to improve usage/performance/generic.



    There are two parts: XDelegate, which is I got from here, and XEvent, which is the event implementation similar to C#.



    #pragma once
    #include <unordered_map>
    //hashing function
    //TODO: replace hashing with something else if it's too expensive
    template<typename return_type, typename... params>
    class XDelegate;
    namespace std
    template<typename return_type, typename... params>
    struct hash<XDelegate<return_type, params ...>>
    typedef return_type(*Type)(void* callee, params...);

    static size_t hash_combine(size_t seed, size_t v)

    return seed ^= v + 0x9e3779b9 + (seed << 6) + (seed >> 2);

    size_t operator()(const XDelegate<return_type, params ...> &pDelegate) const

    size_t a = std::hash<void*>(pDelegate.fpCallee);
    size_t b = std::hash<Type>(pDelegate.fpCallbackFunction);
    return hash_combine(a, b);

    ;
    ;

    //nice generic variadic delegate from http://blog.coldflake.com/posts/C++-delegates-on-steroids/
    template<typename return_type, typename... params>
    class XDelegate

    friend struct std::hash<XDelegate>;

    typedef return_type(*Type)(void* callee, params...);
    public:
    XDelegate(void* callee, Type function)
    : _fpCallee(callee)
    , _fpCallbackFunction(function)
    template <class T, return_type(T::*TMethod)(params...)>
    static XDelegate from_function(T* callee)

    XDelegate d(callee, &methodCaller<T, TMethod>);
    return d;

    return_type operator()(params... xs) const

    return (*_fpCallbackFunction)(_fpCallee, xs...);

    private:
    void* _fpCallee;
    Type _fpCallbackFunction;
    template <class T, return_type(T::*TMethod)(params...)>
    static return_type methodCaller(void* callee, params... xs)

    T* p = static_cast<T*>(callee);
    return (p->*TMethod)(xs...);

    ;


    //C# style delegate-event for none ref class.
    template<typename return_type, typename... params>
    class XEvent

    friend struct std::hash<XDelegate<return_type, params ...>>;

    public:
    size_t add(XDelegate<return_type, params ...> pDelegate)

    size_t token = std::hash<XDelegate<return_type, params ...>>(pDelegate);
    auto result = _delegates.insert_or_assign(token, pDelegate);
    #ifdef _DEBUG
    //if assignment took place, ie) there is a hash collision, throw exception.
    if (!result.second)

    throw std::exception("XEvent hash collision");

    #endif
    return token;

    void remove(size_t pToken)

    _delegates.erase(pToken);

    size_t size() const

    return _delegates.size();

    //on hindsight, I don't think C# events have return value?
    std::unordered_map<size_t, return_type> operator()(params... xs)const

    std::unordered_map<size_t, return_type> result;
    for (auto itr = _delegates.begin(); itr != _delegates.end(); ++itr)

    result[itr->first] = itr->second(xs...);


    //return map of event tokens mapped to the results.
    return result;

    private:


    std::unordered_map<size_t, XDelegate<return_type, params ...>> _delegates;
    ;


    template<typename... params>
    class XEvent<void, params ...>

    friend struct std::hash<XDelegate<void, params ...>>;

    public:
    size_t add(XDelegate<void, params ...> pDelegate)

    size_t token = std::hash<XDelegate<void, params ...>>(pDelegate);
    auto result = _delegates.insert_or_assign(token, pDelegate);
    #ifdef _DEBUG
    //if assignment took place, ie) there is a hash collision, throw exception.
    if (!result.second)

    throw std::exception("XEvent hash collision");

    #endif
    return token;

    void remove(size_t pToken)

    _delegates.erase(pToken);

    size_t size() const

    return _delegates.size();

    void operator()(params... xs)const

    for (auto itr = _delegates.begin(); itr != _delegates.end(); ++itr)

    itr->second(xs...);


    private:
    std::unordered_map<size_t, XDelegate<void, params ...>> _delegates;
    ;


    It is used like this:



    _returnEventTokens.push_back(_returnEvent.add(XDelegate<int, std::wstring>::from_function<EventTester, &EventTester::returnEventCallback1>(_tester)));
    _returnEventTokens.push_back(_returnEvent.add(XDelegate<int, std::wstring>::from_function<EventTester, &EventTester::returnEventCallback2>(_tester)));
    _returnEventTokens.push_back(_returnEvent.add(XDelegate<int, std::wstring>::from_function<EventTester, &EventTester::returnEventCallback3>(_tester)));


    _returnEvent(L"Return event called!n");

    for (auto itr = _noReturnEventTokens.begin(); itr != _noReturnEventTokens.end(); ++itr)

    _noReturnEvent.remove(*itr);







    share|improve this question























      up vote
      1
      down vote

      favorite









      up vote
      1
      down vote

      favorite











      I'd appreciate advice regarding both the implementation detail and ways to improve usage/performance/generic.



      There are two parts: XDelegate, which is I got from here, and XEvent, which is the event implementation similar to C#.



      #pragma once
      #include <unordered_map>
      //hashing function
      //TODO: replace hashing with something else if it's too expensive
      template<typename return_type, typename... params>
      class XDelegate;
      namespace std
      template<typename return_type, typename... params>
      struct hash<XDelegate<return_type, params ...>>
      typedef return_type(*Type)(void* callee, params...);

      static size_t hash_combine(size_t seed, size_t v)

      return seed ^= v + 0x9e3779b9 + (seed << 6) + (seed >> 2);

      size_t operator()(const XDelegate<return_type, params ...> &pDelegate) const

      size_t a = std::hash<void*>(pDelegate.fpCallee);
      size_t b = std::hash<Type>(pDelegate.fpCallbackFunction);
      return hash_combine(a, b);

      ;
      ;

      //nice generic variadic delegate from http://blog.coldflake.com/posts/C++-delegates-on-steroids/
      template<typename return_type, typename... params>
      class XDelegate

      friend struct std::hash<XDelegate>;

      typedef return_type(*Type)(void* callee, params...);
      public:
      XDelegate(void* callee, Type function)
      : _fpCallee(callee)
      , _fpCallbackFunction(function)
      template <class T, return_type(T::*TMethod)(params...)>
      static XDelegate from_function(T* callee)

      XDelegate d(callee, &methodCaller<T, TMethod>);
      return d;

      return_type operator()(params... xs) const

      return (*_fpCallbackFunction)(_fpCallee, xs...);

      private:
      void* _fpCallee;
      Type _fpCallbackFunction;
      template <class T, return_type(T::*TMethod)(params...)>
      static return_type methodCaller(void* callee, params... xs)

      T* p = static_cast<T*>(callee);
      return (p->*TMethod)(xs...);

      ;


      //C# style delegate-event for none ref class.
      template<typename return_type, typename... params>
      class XEvent

      friend struct std::hash<XDelegate<return_type, params ...>>;

      public:
      size_t add(XDelegate<return_type, params ...> pDelegate)

      size_t token = std::hash<XDelegate<return_type, params ...>>(pDelegate);
      auto result = _delegates.insert_or_assign(token, pDelegate);
      #ifdef _DEBUG
      //if assignment took place, ie) there is a hash collision, throw exception.
      if (!result.second)

      throw std::exception("XEvent hash collision");

      #endif
      return token;

      void remove(size_t pToken)

      _delegates.erase(pToken);

      size_t size() const

      return _delegates.size();

      //on hindsight, I don't think C# events have return value?
      std::unordered_map<size_t, return_type> operator()(params... xs)const

      std::unordered_map<size_t, return_type> result;
      for (auto itr = _delegates.begin(); itr != _delegates.end(); ++itr)

      result[itr->first] = itr->second(xs...);


      //return map of event tokens mapped to the results.
      return result;

      private:


      std::unordered_map<size_t, XDelegate<return_type, params ...>> _delegates;
      ;


      template<typename... params>
      class XEvent<void, params ...>

      friend struct std::hash<XDelegate<void, params ...>>;

      public:
      size_t add(XDelegate<void, params ...> pDelegate)

      size_t token = std::hash<XDelegate<void, params ...>>(pDelegate);
      auto result = _delegates.insert_or_assign(token, pDelegate);
      #ifdef _DEBUG
      //if assignment took place, ie) there is a hash collision, throw exception.
      if (!result.second)

      throw std::exception("XEvent hash collision");

      #endif
      return token;

      void remove(size_t pToken)

      _delegates.erase(pToken);

      size_t size() const

      return _delegates.size();

      void operator()(params... xs)const

      for (auto itr = _delegates.begin(); itr != _delegates.end(); ++itr)

      itr->second(xs...);


      private:
      std::unordered_map<size_t, XDelegate<void, params ...>> _delegates;
      ;


      It is used like this:



      _returnEventTokens.push_back(_returnEvent.add(XDelegate<int, std::wstring>::from_function<EventTester, &EventTester::returnEventCallback1>(_tester)));
      _returnEventTokens.push_back(_returnEvent.add(XDelegate<int, std::wstring>::from_function<EventTester, &EventTester::returnEventCallback2>(_tester)));
      _returnEventTokens.push_back(_returnEvent.add(XDelegate<int, std::wstring>::from_function<EventTester, &EventTester::returnEventCallback3>(_tester)));


      _returnEvent(L"Return event called!n");

      for (auto itr = _noReturnEventTokens.begin(); itr != _noReturnEventTokens.end(); ++itr)

      _noReturnEvent.remove(*itr);







      share|improve this question













      I'd appreciate advice regarding both the implementation detail and ways to improve usage/performance/generic.



      There are two parts: XDelegate, which is I got from here, and XEvent, which is the event implementation similar to C#.



      #pragma once
      #include <unordered_map>
      //hashing function
      //TODO: replace hashing with something else if it's too expensive
      template<typename return_type, typename... params>
      class XDelegate;
      namespace std
      template<typename return_type, typename... params>
      struct hash<XDelegate<return_type, params ...>>
      typedef return_type(*Type)(void* callee, params...);

      static size_t hash_combine(size_t seed, size_t v)

      return seed ^= v + 0x9e3779b9 + (seed << 6) + (seed >> 2);

      size_t operator()(const XDelegate<return_type, params ...> &pDelegate) const

      size_t a = std::hash<void*>(pDelegate.fpCallee);
      size_t b = std::hash<Type>(pDelegate.fpCallbackFunction);
      return hash_combine(a, b);

      ;
      ;

      //nice generic variadic delegate from http://blog.coldflake.com/posts/C++-delegates-on-steroids/
      template<typename return_type, typename... params>
      class XDelegate

      friend struct std::hash<XDelegate>;

      typedef return_type(*Type)(void* callee, params...);
      public:
      XDelegate(void* callee, Type function)
      : _fpCallee(callee)
      , _fpCallbackFunction(function)
      template <class T, return_type(T::*TMethod)(params...)>
      static XDelegate from_function(T* callee)

      XDelegate d(callee, &methodCaller<T, TMethod>);
      return d;

      return_type operator()(params... xs) const

      return (*_fpCallbackFunction)(_fpCallee, xs...);

      private:
      void* _fpCallee;
      Type _fpCallbackFunction;
      template <class T, return_type(T::*TMethod)(params...)>
      static return_type methodCaller(void* callee, params... xs)

      T* p = static_cast<T*>(callee);
      return (p->*TMethod)(xs...);

      ;


      //C# style delegate-event for none ref class.
      template<typename return_type, typename... params>
      class XEvent

      friend struct std::hash<XDelegate<return_type, params ...>>;

      public:
      size_t add(XDelegate<return_type, params ...> pDelegate)

      size_t token = std::hash<XDelegate<return_type, params ...>>(pDelegate);
      auto result = _delegates.insert_or_assign(token, pDelegate);
      #ifdef _DEBUG
      //if assignment took place, ie) there is a hash collision, throw exception.
      if (!result.second)

      throw std::exception("XEvent hash collision");

      #endif
      return token;

      void remove(size_t pToken)

      _delegates.erase(pToken);

      size_t size() const

      return _delegates.size();

      //on hindsight, I don't think C# events have return value?
      std::unordered_map<size_t, return_type> operator()(params... xs)const

      std::unordered_map<size_t, return_type> result;
      for (auto itr = _delegates.begin(); itr != _delegates.end(); ++itr)

      result[itr->first] = itr->second(xs...);


      //return map of event tokens mapped to the results.
      return result;

      private:


      std::unordered_map<size_t, XDelegate<return_type, params ...>> _delegates;
      ;


      template<typename... params>
      class XEvent<void, params ...>

      friend struct std::hash<XDelegate<void, params ...>>;

      public:
      size_t add(XDelegate<void, params ...> pDelegate)

      size_t token = std::hash<XDelegate<void, params ...>>(pDelegate);
      auto result = _delegates.insert_or_assign(token, pDelegate);
      #ifdef _DEBUG
      //if assignment took place, ie) there is a hash collision, throw exception.
      if (!result.second)

      throw std::exception("XEvent hash collision");

      #endif
      return token;

      void remove(size_t pToken)

      _delegates.erase(pToken);

      size_t size() const

      return _delegates.size();

      void operator()(params... xs)const

      for (auto itr = _delegates.begin(); itr != _delegates.end(); ++itr)

      itr->second(xs...);


      private:
      std::unordered_map<size_t, XDelegate<void, params ...>> _delegates;
      ;


      It is used like this:



      _returnEventTokens.push_back(_returnEvent.add(XDelegate<int, std::wstring>::from_function<EventTester, &EventTester::returnEventCallback1>(_tester)));
      _returnEventTokens.push_back(_returnEvent.add(XDelegate<int, std::wstring>::from_function<EventTester, &EventTester::returnEventCallback2>(_tester)));
      _returnEventTokens.push_back(_returnEvent.add(XDelegate<int, std::wstring>::from_function<EventTester, &EventTester::returnEventCallback3>(_tester)));


      _returnEvent(L"Return event called!n");

      for (auto itr = _noReturnEventTokens.begin(); itr != _noReturnEventTokens.end(); ++itr)

      _noReturnEvent.remove(*itr);









      share|improve this question












      share|improve this question




      share|improve this question








      edited Jun 17 at 22:14









      Jamal♦

      30.1k11114225




      30.1k11114225









      asked Apr 13 at 7:30









      legokangpalla

      83




      83




















          2 Answers
          2






          active

          oldest

          votes

















          up vote
          1
          down vote



          accepted










          Your description of "it is used like this" leaves quite a bit to the imagination. For example, what are the types of _noReturnEventTokens, _returnEventTokens, and _returnEvent? How are they declared? What do you expect to happen when you evaluate _returnEvent(L"Return event called!n")?




          I don't think the code is so "unpleasant" to read as CodeGorilla suggested; but it could definitely be improved by some minor reformatting. I strongly suggest putting a blank line between each pair of member function definitions: that is, not



          auto f()


          auto g()




          but instead



          auto f()



          auto g()




          . This is minor but would help quite a bit in "breaking up" the code visually. If you want to get the vertical real-estate back, then cuddle your braces:



          auto f() 


          auto g()




          void operator()(params... xs)const

          for (auto itr = _delegates.begin(); itr != _delegates.end(); ++itr)

          itr->second(xs...);




          The first thing to notice about this code is that it contains an old-style (non-range) for loop. Rewrite it as



           for (auto&& pair : _delegates) 
          pair.second(xs...);



          The second thing that caught my eye was the construct params... xs. Taking a parameter pack by value is rare; taking by perfect-forward (params&&... xs) is more common. But because you're doing a lot of metaprogramming here, maybe by-value is correct. So I kept looking to find out where params came from — and I think all your uses are reasonable. There was one place I thought you should have been using perfect-forwarding, but then I realized that you probably did need to avoid moving-out-of the arguments prematurely (because they would be used again by other delegates).



          Incidentally, I recommend CamelCase for template parameters. What you called params..., I would have called Args... or possibly just Ts....




          namespace std 
          template<typename return_type, typename... params>
          struct hash<XDelegate<return_type, params ...>>
          typedef return_type(*Type)(void* callee, params...);

          static size_t hash_combine(size_t seed, size_t v)

          return seed ^= v + 0x9e3779b9 + (seed << 6) + (seed >> 2);

          size_t operator()(const XDelegate<return_type, params ...> &pDelegate) const

          size_t a = std::hash<void*>(pDelegate.fpCallee);
          size_t b = std::hash<Type>(pDelegate.fpCallbackFunction);
          return hash_combine(a, b);

          ;
          ;


          That last }; should be just }, and I strongly recommend adding the comment // namespace std for readability. You'll see this "end-of-namespace comment" convention being used in most C++ codebases these days.



          But for a simple specialization of std::hash, you actually don't need to reopen the namespace — you can just specialize the qualified struct std::hash, like this:



          template<class R, class... Args>
          struct std::hash<XDelegate<R, Args...>>
          static size_t hash_combine(size_t seed, size_t v)
          seed ^= v + 0x9e3779b9 + (seed << 6) + (seed >> 2);
          return seed;


          size_t operator()(const XDelegate<R, Args...>& pDelegate) const
          using Type = R(void *, Args...);
          size_t a = std::hash<void*>(pDelegate.fpCallee);
          size_t b = std::hash<Type*>(pDelegate.fpCallbackFunction);
          return hash_combine(a, b);

          ;


          Observe the cuddling of braces, the blank line between function definitions, the CamelCase template parameter names (and much shorter — return_type becomes R), and the modernized using T = U in place of typedef U T.



          Observe that instead of declaring Type as an alias for a function-pointer type, I declared it as a function type and moved the * down into the place it was used, in std::hash<Type*>, which makes the two std::hash<X*> invocations nicely symmetrical.



          Observe that I never put two side-effects on the same source line of code. Instead of return seed ^= expr;, I write seed ^= expr; and then on the next line return (the new value of) seed. This really matters when you start getting into atomic/lock-free stuff, but it never hurts.




          friend struct std::hash<XDelegate>;


          Consider giving XDelegate a non-static member function hash(), so that if you want the hash of an object x you just say x.hash(), instead of std::hash<decltype(x)>(x). You'd still have to provide a trivial out-of-line specialization



          template<class R, class... Args>
          struct std::hash<XDelegate<R, Args...>>
          size_t operator()(const XDelegate<R, Args...>& x) const
          return x.hash();

          ;


          but it would no longer need to be a friend of the class.




          I'm sure there's more event-and-delegate-relevant kinds of things to talk about, but this is where I'll stop. :)






          share|improve this answer





















          • "Consider giving XDelegate a non-static member function hash()" ,I had a facepalm moment with that. I automatically added the std::hash specialization when I'm not using it for std::unordered_map key, heh.
            – legokangpalla
            Apr 16 at 0:48

















          up vote
          1
          down vote













          The code is not very pleasant to read, its like it has been autogenerated. I wouldn't like to have to pick up from you and fix a bug in it. It may be a very clever way of doing whatever it is you are trying to do, but I don't think its good code.



          It has very little in the way of meaningful comments, nothing to describe what the purpose of the templates and functions are. If you combine this with the links to websites in my experience it implies that the person writing it doesn't really understand what the code does.



          Why do you only throw exceptions in DEBUG builds, does the code never go wrong in RELEASE builds?



          One question I can't get out of my head is if you want C# delegates and events why not program in C#, otherwise just use what is already in C++?






          share|improve this answer





















            Your Answer




            StackExchange.ifUsing("editor", function ()
            return StackExchange.using("mathjaxEditing", function ()
            StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
            StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
            );
            );
            , "mathjax-editing");

            StackExchange.ifUsing("editor", function ()
            StackExchange.using("externalEditor", function ()
            StackExchange.using("snippets", function ()
            StackExchange.snippets.init();
            );
            );
            , "code-snippets");

            StackExchange.ready(function()
            var channelOptions =
            tags: "".split(" "),
            id: "196"
            ;
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function()
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled)
            StackExchange.using("snippets", function()
            createEditor();
            );

            else
            createEditor();

            );

            function createEditor()
            StackExchange.prepareEditor(
            heartbeatType: 'answer',
            convertImagesToLinks: false,
            noModals: false,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            );



            );








             

            draft saved


            draft discarded


















            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f191945%2fc-style-event-and-delegate-in-c%23new-answer', 'question_page');

            );

            Post as a guest






























            2 Answers
            2






            active

            oldest

            votes








            2 Answers
            2






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes








            up vote
            1
            down vote



            accepted










            Your description of "it is used like this" leaves quite a bit to the imagination. For example, what are the types of _noReturnEventTokens, _returnEventTokens, and _returnEvent? How are they declared? What do you expect to happen when you evaluate _returnEvent(L"Return event called!n")?




            I don't think the code is so "unpleasant" to read as CodeGorilla suggested; but it could definitely be improved by some minor reformatting. I strongly suggest putting a blank line between each pair of member function definitions: that is, not



            auto f()


            auto g()




            but instead



            auto f()



            auto g()




            . This is minor but would help quite a bit in "breaking up" the code visually. If you want to get the vertical real-estate back, then cuddle your braces:



            auto f() 


            auto g()




            void operator()(params... xs)const

            for (auto itr = _delegates.begin(); itr != _delegates.end(); ++itr)

            itr->second(xs...);




            The first thing to notice about this code is that it contains an old-style (non-range) for loop. Rewrite it as



             for (auto&& pair : _delegates) 
            pair.second(xs...);



            The second thing that caught my eye was the construct params... xs. Taking a parameter pack by value is rare; taking by perfect-forward (params&&... xs) is more common. But because you're doing a lot of metaprogramming here, maybe by-value is correct. So I kept looking to find out where params came from — and I think all your uses are reasonable. There was one place I thought you should have been using perfect-forwarding, but then I realized that you probably did need to avoid moving-out-of the arguments prematurely (because they would be used again by other delegates).



            Incidentally, I recommend CamelCase for template parameters. What you called params..., I would have called Args... or possibly just Ts....




            namespace std 
            template<typename return_type, typename... params>
            struct hash<XDelegate<return_type, params ...>>
            typedef return_type(*Type)(void* callee, params...);

            static size_t hash_combine(size_t seed, size_t v)

            return seed ^= v + 0x9e3779b9 + (seed << 6) + (seed >> 2);

            size_t operator()(const XDelegate<return_type, params ...> &pDelegate) const

            size_t a = std::hash<void*>(pDelegate.fpCallee);
            size_t b = std::hash<Type>(pDelegate.fpCallbackFunction);
            return hash_combine(a, b);

            ;
            ;


            That last }; should be just }, and I strongly recommend adding the comment // namespace std for readability. You'll see this "end-of-namespace comment" convention being used in most C++ codebases these days.



            But for a simple specialization of std::hash, you actually don't need to reopen the namespace — you can just specialize the qualified struct std::hash, like this:



            template<class R, class... Args>
            struct std::hash<XDelegate<R, Args...>>
            static size_t hash_combine(size_t seed, size_t v)
            seed ^= v + 0x9e3779b9 + (seed << 6) + (seed >> 2);
            return seed;


            size_t operator()(const XDelegate<R, Args...>& pDelegate) const
            using Type = R(void *, Args...);
            size_t a = std::hash<void*>(pDelegate.fpCallee);
            size_t b = std::hash<Type*>(pDelegate.fpCallbackFunction);
            return hash_combine(a, b);

            ;


            Observe the cuddling of braces, the blank line between function definitions, the CamelCase template parameter names (and much shorter — return_type becomes R), and the modernized using T = U in place of typedef U T.



            Observe that instead of declaring Type as an alias for a function-pointer type, I declared it as a function type and moved the * down into the place it was used, in std::hash<Type*>, which makes the two std::hash<X*> invocations nicely symmetrical.



            Observe that I never put two side-effects on the same source line of code. Instead of return seed ^= expr;, I write seed ^= expr; and then on the next line return (the new value of) seed. This really matters when you start getting into atomic/lock-free stuff, but it never hurts.




            friend struct std::hash<XDelegate>;


            Consider giving XDelegate a non-static member function hash(), so that if you want the hash of an object x you just say x.hash(), instead of std::hash<decltype(x)>(x). You'd still have to provide a trivial out-of-line specialization



            template<class R, class... Args>
            struct std::hash<XDelegate<R, Args...>>
            size_t operator()(const XDelegate<R, Args...>& x) const
            return x.hash();

            ;


            but it would no longer need to be a friend of the class.




            I'm sure there's more event-and-delegate-relevant kinds of things to talk about, but this is where I'll stop. :)






            share|improve this answer





















            • "Consider giving XDelegate a non-static member function hash()" ,I had a facepalm moment with that. I automatically added the std::hash specialization when I'm not using it for std::unordered_map key, heh.
              – legokangpalla
              Apr 16 at 0:48














            up vote
            1
            down vote



            accepted










            Your description of "it is used like this" leaves quite a bit to the imagination. For example, what are the types of _noReturnEventTokens, _returnEventTokens, and _returnEvent? How are they declared? What do you expect to happen when you evaluate _returnEvent(L"Return event called!n")?




            I don't think the code is so "unpleasant" to read as CodeGorilla suggested; but it could definitely be improved by some minor reformatting. I strongly suggest putting a blank line between each pair of member function definitions: that is, not



            auto f()


            auto g()




            but instead



            auto f()



            auto g()




            . This is minor but would help quite a bit in "breaking up" the code visually. If you want to get the vertical real-estate back, then cuddle your braces:



            auto f() 


            auto g()




            void operator()(params... xs)const

            for (auto itr = _delegates.begin(); itr != _delegates.end(); ++itr)

            itr->second(xs...);




            The first thing to notice about this code is that it contains an old-style (non-range) for loop. Rewrite it as



             for (auto&& pair : _delegates) 
            pair.second(xs...);



            The second thing that caught my eye was the construct params... xs. Taking a parameter pack by value is rare; taking by perfect-forward (params&&... xs) is more common. But because you're doing a lot of metaprogramming here, maybe by-value is correct. So I kept looking to find out where params came from — and I think all your uses are reasonable. There was one place I thought you should have been using perfect-forwarding, but then I realized that you probably did need to avoid moving-out-of the arguments prematurely (because they would be used again by other delegates).



            Incidentally, I recommend CamelCase for template parameters. What you called params..., I would have called Args... or possibly just Ts....




            namespace std 
            template<typename return_type, typename... params>
            struct hash<XDelegate<return_type, params ...>>
            typedef return_type(*Type)(void* callee, params...);

            static size_t hash_combine(size_t seed, size_t v)

            return seed ^= v + 0x9e3779b9 + (seed << 6) + (seed >> 2);

            size_t operator()(const XDelegate<return_type, params ...> &pDelegate) const

            size_t a = std::hash<void*>(pDelegate.fpCallee);
            size_t b = std::hash<Type>(pDelegate.fpCallbackFunction);
            return hash_combine(a, b);

            ;
            ;


            That last }; should be just }, and I strongly recommend adding the comment // namespace std for readability. You'll see this "end-of-namespace comment" convention being used in most C++ codebases these days.



            But for a simple specialization of std::hash, you actually don't need to reopen the namespace — you can just specialize the qualified struct std::hash, like this:



            template<class R, class... Args>
            struct std::hash<XDelegate<R, Args...>>
            static size_t hash_combine(size_t seed, size_t v)
            seed ^= v + 0x9e3779b9 + (seed << 6) + (seed >> 2);
            return seed;


            size_t operator()(const XDelegate<R, Args...>& pDelegate) const
            using Type = R(void *, Args...);
            size_t a = std::hash<void*>(pDelegate.fpCallee);
            size_t b = std::hash<Type*>(pDelegate.fpCallbackFunction);
            return hash_combine(a, b);

            ;


            Observe the cuddling of braces, the blank line between function definitions, the CamelCase template parameter names (and much shorter — return_type becomes R), and the modernized using T = U in place of typedef U T.



            Observe that instead of declaring Type as an alias for a function-pointer type, I declared it as a function type and moved the * down into the place it was used, in std::hash<Type*>, which makes the two std::hash<X*> invocations nicely symmetrical.



            Observe that I never put two side-effects on the same source line of code. Instead of return seed ^= expr;, I write seed ^= expr; and then on the next line return (the new value of) seed. This really matters when you start getting into atomic/lock-free stuff, but it never hurts.




            friend struct std::hash<XDelegate>;


            Consider giving XDelegate a non-static member function hash(), so that if you want the hash of an object x you just say x.hash(), instead of std::hash<decltype(x)>(x). You'd still have to provide a trivial out-of-line specialization



            template<class R, class... Args>
            struct std::hash<XDelegate<R, Args...>>
            size_t operator()(const XDelegate<R, Args...>& x) const
            return x.hash();

            ;


            but it would no longer need to be a friend of the class.




            I'm sure there's more event-and-delegate-relevant kinds of things to talk about, but this is where I'll stop. :)






            share|improve this answer





















            • "Consider giving XDelegate a non-static member function hash()" ,I had a facepalm moment with that. I automatically added the std::hash specialization when I'm not using it for std::unordered_map key, heh.
              – legokangpalla
              Apr 16 at 0:48












            up vote
            1
            down vote



            accepted







            up vote
            1
            down vote



            accepted






            Your description of "it is used like this" leaves quite a bit to the imagination. For example, what are the types of _noReturnEventTokens, _returnEventTokens, and _returnEvent? How are they declared? What do you expect to happen when you evaluate _returnEvent(L"Return event called!n")?




            I don't think the code is so "unpleasant" to read as CodeGorilla suggested; but it could definitely be improved by some minor reformatting. I strongly suggest putting a blank line between each pair of member function definitions: that is, not



            auto f()


            auto g()




            but instead



            auto f()



            auto g()




            . This is minor but would help quite a bit in "breaking up" the code visually. If you want to get the vertical real-estate back, then cuddle your braces:



            auto f() 


            auto g()




            void operator()(params... xs)const

            for (auto itr = _delegates.begin(); itr != _delegates.end(); ++itr)

            itr->second(xs...);




            The first thing to notice about this code is that it contains an old-style (non-range) for loop. Rewrite it as



             for (auto&& pair : _delegates) 
            pair.second(xs...);



            The second thing that caught my eye was the construct params... xs. Taking a parameter pack by value is rare; taking by perfect-forward (params&&... xs) is more common. But because you're doing a lot of metaprogramming here, maybe by-value is correct. So I kept looking to find out where params came from — and I think all your uses are reasonable. There was one place I thought you should have been using perfect-forwarding, but then I realized that you probably did need to avoid moving-out-of the arguments prematurely (because they would be used again by other delegates).



            Incidentally, I recommend CamelCase for template parameters. What you called params..., I would have called Args... or possibly just Ts....




            namespace std 
            template<typename return_type, typename... params>
            struct hash<XDelegate<return_type, params ...>>
            typedef return_type(*Type)(void* callee, params...);

            static size_t hash_combine(size_t seed, size_t v)

            return seed ^= v + 0x9e3779b9 + (seed << 6) + (seed >> 2);

            size_t operator()(const XDelegate<return_type, params ...> &pDelegate) const

            size_t a = std::hash<void*>(pDelegate.fpCallee);
            size_t b = std::hash<Type>(pDelegate.fpCallbackFunction);
            return hash_combine(a, b);

            ;
            ;


            That last }; should be just }, and I strongly recommend adding the comment // namespace std for readability. You'll see this "end-of-namespace comment" convention being used in most C++ codebases these days.



            But for a simple specialization of std::hash, you actually don't need to reopen the namespace — you can just specialize the qualified struct std::hash, like this:



            template<class R, class... Args>
            struct std::hash<XDelegate<R, Args...>>
            static size_t hash_combine(size_t seed, size_t v)
            seed ^= v + 0x9e3779b9 + (seed << 6) + (seed >> 2);
            return seed;


            size_t operator()(const XDelegate<R, Args...>& pDelegate) const
            using Type = R(void *, Args...);
            size_t a = std::hash<void*>(pDelegate.fpCallee);
            size_t b = std::hash<Type*>(pDelegate.fpCallbackFunction);
            return hash_combine(a, b);

            ;


            Observe the cuddling of braces, the blank line between function definitions, the CamelCase template parameter names (and much shorter — return_type becomes R), and the modernized using T = U in place of typedef U T.



            Observe that instead of declaring Type as an alias for a function-pointer type, I declared it as a function type and moved the * down into the place it was used, in std::hash<Type*>, which makes the two std::hash<X*> invocations nicely symmetrical.



            Observe that I never put two side-effects on the same source line of code. Instead of return seed ^= expr;, I write seed ^= expr; and then on the next line return (the new value of) seed. This really matters when you start getting into atomic/lock-free stuff, but it never hurts.




            friend struct std::hash<XDelegate>;


            Consider giving XDelegate a non-static member function hash(), so that if you want the hash of an object x you just say x.hash(), instead of std::hash<decltype(x)>(x). You'd still have to provide a trivial out-of-line specialization



            template<class R, class... Args>
            struct std::hash<XDelegate<R, Args...>>
            size_t operator()(const XDelegate<R, Args...>& x) const
            return x.hash();

            ;


            but it would no longer need to be a friend of the class.




            I'm sure there's more event-and-delegate-relevant kinds of things to talk about, but this is where I'll stop. :)






            share|improve this answer













            Your description of "it is used like this" leaves quite a bit to the imagination. For example, what are the types of _noReturnEventTokens, _returnEventTokens, and _returnEvent? How are they declared? What do you expect to happen when you evaluate _returnEvent(L"Return event called!n")?




            I don't think the code is so "unpleasant" to read as CodeGorilla suggested; but it could definitely be improved by some minor reformatting. I strongly suggest putting a blank line between each pair of member function definitions: that is, not



            auto f()


            auto g()




            but instead



            auto f()



            auto g()




            . This is minor but would help quite a bit in "breaking up" the code visually. If you want to get the vertical real-estate back, then cuddle your braces:



            auto f() 


            auto g()




            void operator()(params... xs)const

            for (auto itr = _delegates.begin(); itr != _delegates.end(); ++itr)

            itr->second(xs...);




            The first thing to notice about this code is that it contains an old-style (non-range) for loop. Rewrite it as



             for (auto&& pair : _delegates) 
            pair.second(xs...);



            The second thing that caught my eye was the construct params... xs. Taking a parameter pack by value is rare; taking by perfect-forward (params&&... xs) is more common. But because you're doing a lot of metaprogramming here, maybe by-value is correct. So I kept looking to find out where params came from — and I think all your uses are reasonable. There was one place I thought you should have been using perfect-forwarding, but then I realized that you probably did need to avoid moving-out-of the arguments prematurely (because they would be used again by other delegates).



            Incidentally, I recommend CamelCase for template parameters. What you called params..., I would have called Args... or possibly just Ts....




            namespace std 
            template<typename return_type, typename... params>
            struct hash<XDelegate<return_type, params ...>>
            typedef return_type(*Type)(void* callee, params...);

            static size_t hash_combine(size_t seed, size_t v)

            return seed ^= v + 0x9e3779b9 + (seed << 6) + (seed >> 2);

            size_t operator()(const XDelegate<return_type, params ...> &pDelegate) const

            size_t a = std::hash<void*>(pDelegate.fpCallee);
            size_t b = std::hash<Type>(pDelegate.fpCallbackFunction);
            return hash_combine(a, b);

            ;
            ;


            That last }; should be just }, and I strongly recommend adding the comment // namespace std for readability. You'll see this "end-of-namespace comment" convention being used in most C++ codebases these days.



            But for a simple specialization of std::hash, you actually don't need to reopen the namespace — you can just specialize the qualified struct std::hash, like this:



            template<class R, class... Args>
            struct std::hash<XDelegate<R, Args...>>
            static size_t hash_combine(size_t seed, size_t v)
            seed ^= v + 0x9e3779b9 + (seed << 6) + (seed >> 2);
            return seed;


            size_t operator()(const XDelegate<R, Args...>& pDelegate) const
            using Type = R(void *, Args...);
            size_t a = std::hash<void*>(pDelegate.fpCallee);
            size_t b = std::hash<Type*>(pDelegate.fpCallbackFunction);
            return hash_combine(a, b);

            ;


            Observe the cuddling of braces, the blank line between function definitions, the CamelCase template parameter names (and much shorter — return_type becomes R), and the modernized using T = U in place of typedef U T.



            Observe that instead of declaring Type as an alias for a function-pointer type, I declared it as a function type and moved the * down into the place it was used, in std::hash<Type*>, which makes the two std::hash<X*> invocations nicely symmetrical.



            Observe that I never put two side-effects on the same source line of code. Instead of return seed ^= expr;, I write seed ^= expr; and then on the next line return (the new value of) seed. This really matters when you start getting into atomic/lock-free stuff, but it never hurts.




            friend struct std::hash<XDelegate>;


            Consider giving XDelegate a non-static member function hash(), so that if you want the hash of an object x you just say x.hash(), instead of std::hash<decltype(x)>(x). You'd still have to provide a trivial out-of-line specialization



            template<class R, class... Args>
            struct std::hash<XDelegate<R, Args...>>
            size_t operator()(const XDelegate<R, Args...>& x) const
            return x.hash();

            ;


            but it would no longer need to be a friend of the class.




            I'm sure there's more event-and-delegate-relevant kinds of things to talk about, but this is where I'll stop. :)







            share|improve this answer













            share|improve this answer



            share|improve this answer











            answered Apr 15 at 8:18









            Quuxplusone

            9,82511451




            9,82511451











            • "Consider giving XDelegate a non-static member function hash()" ,I had a facepalm moment with that. I automatically added the std::hash specialization when I'm not using it for std::unordered_map key, heh.
              – legokangpalla
              Apr 16 at 0:48
















            • "Consider giving XDelegate a non-static member function hash()" ,I had a facepalm moment with that. I automatically added the std::hash specialization when I'm not using it for std::unordered_map key, heh.
              – legokangpalla
              Apr 16 at 0:48















            "Consider giving XDelegate a non-static member function hash()" ,I had a facepalm moment with that. I automatically added the std::hash specialization when I'm not using it for std::unordered_map key, heh.
            – legokangpalla
            Apr 16 at 0:48




            "Consider giving XDelegate a non-static member function hash()" ,I had a facepalm moment with that. I automatically added the std::hash specialization when I'm not using it for std::unordered_map key, heh.
            – legokangpalla
            Apr 16 at 0:48












            up vote
            1
            down vote













            The code is not very pleasant to read, its like it has been autogenerated. I wouldn't like to have to pick up from you and fix a bug in it. It may be a very clever way of doing whatever it is you are trying to do, but I don't think its good code.



            It has very little in the way of meaningful comments, nothing to describe what the purpose of the templates and functions are. If you combine this with the links to websites in my experience it implies that the person writing it doesn't really understand what the code does.



            Why do you only throw exceptions in DEBUG builds, does the code never go wrong in RELEASE builds?



            One question I can't get out of my head is if you want C# delegates and events why not program in C#, otherwise just use what is already in C++?






            share|improve this answer

























              up vote
              1
              down vote













              The code is not very pleasant to read, its like it has been autogenerated. I wouldn't like to have to pick up from you and fix a bug in it. It may be a very clever way of doing whatever it is you are trying to do, but I don't think its good code.



              It has very little in the way of meaningful comments, nothing to describe what the purpose of the templates and functions are. If you combine this with the links to websites in my experience it implies that the person writing it doesn't really understand what the code does.



              Why do you only throw exceptions in DEBUG builds, does the code never go wrong in RELEASE builds?



              One question I can't get out of my head is if you want C# delegates and events why not program in C#, otherwise just use what is already in C++?






              share|improve this answer























                up vote
                1
                down vote










                up vote
                1
                down vote









                The code is not very pleasant to read, its like it has been autogenerated. I wouldn't like to have to pick up from you and fix a bug in it. It may be a very clever way of doing whatever it is you are trying to do, but I don't think its good code.



                It has very little in the way of meaningful comments, nothing to describe what the purpose of the templates and functions are. If you combine this with the links to websites in my experience it implies that the person writing it doesn't really understand what the code does.



                Why do you only throw exceptions in DEBUG builds, does the code never go wrong in RELEASE builds?



                One question I can't get out of my head is if you want C# delegates and events why not program in C#, otherwise just use what is already in C++?






                share|improve this answer













                The code is not very pleasant to read, its like it has been autogenerated. I wouldn't like to have to pick up from you and fix a bug in it. It may be a very clever way of doing whatever it is you are trying to do, but I don't think its good code.



                It has very little in the way of meaningful comments, nothing to describe what the purpose of the templates and functions are. If you combine this with the links to websites in my experience it implies that the person writing it doesn't really understand what the code does.



                Why do you only throw exceptions in DEBUG builds, does the code never go wrong in RELEASE builds?



                One question I can't get out of my head is if you want C# delegates and events why not program in C#, otherwise just use what is already in C++?







                share|improve this answer













                share|improve this answer



                share|improve this answer











                answered Apr 13 at 11:34









                Code Gorilla

                1,08228




                1,08228






















                     

                    draft saved


                    draft discarded


























                     


                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function ()
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f191945%2fc-style-event-and-delegate-in-c%23new-answer', 'question_page');

                    );

                    Post as a guest













































































                    Popular posts from this blog

                    Greedy Best First Search implementation in Rust

                    Function to Return a JSON Like Objects Using VBA Collections and Arrays

                    C++11 CLH Lock Implementation