nikobarli
Topic Author
Posts: 178
Joined: 26 Apr 2017, 06:23

Cannot unregister routed event handler (v2.0.1f1, C++)

12 May 2017, 02:41

Just for testing, I registered a MouseMove event handler to an element, then soon unregistered it as follows. However, it seems that unregistering the handler doesn't work and my handler was still being called by the system.
       static auto handler = [](BaseComponent* sender, const MouseEventArgs& args) {
         ...
       };
       
       parent->MouseMove() += handler;
       parent->MouseMove() -= handler;
 
User avatar
jsantos
Site Admin
Posts: 2905
Joined: 20 Jan 2012, 17:18
Contact:

Re: Cannot unregister routed event handler (v2.0.1f1, C++)

12 May 2017, 07:52

Unfortunately when a delegate is built from a lambda, the equality operator always returns false. I am not sure if we can implement this properly (in fact C++ std::function is not comparable at all). You need to use normal functions (free function or member functions) for this to work properly.
 
nikobarli
Topic Author
Posts: 178
Joined: 26 Apr 2017, 06:23

Re: Cannot unregister routed event handler (v2.0.1f1, C++)

12 May 2017, 09:41

I see.

I think it's better if you provide another API to subscribe to the event because lambda is so much powerful that it will be pain if it is not supported.

For example in rxcpp (https://github.com/Reactive-Extensions/RxCpp), a 'subscription' is returned when you subscribe to an observable. Then you can call 'unsubscribe' method of the 'subscription' to remove the subscription.

Snippet from: http://reactive-extensions.github.io/Rx ... d208cedfda
    auto values = rxcpp::observable<>::range(1, 3).
        concat(rxcpp::observable<>::never<int>()).
        finally([](){printf("The final action\n");});
    auto subscription = values.subscribe(
        [](int v){printf("OnNext: %d\n", v);},
        [](){printf("OnCompleted\n");});
    subscription.unsubscribe();
 
 
User avatar
jsantos
Site Admin
Posts: 2905
Joined: 20 Jan 2012, 17:18
Contact:

Re: Cannot unregister routed event handler (v2.0.1f1, C++)

13 May 2017, 00:20

Could you please file a ticket about it? This is something we need to solve. Thanks!
 
nikobarli
Topic Author
Posts: 178
Joined: 26 Apr 2017, 06:23

Re: Cannot unregister routed event handler (v2.0.1f1, C++)

13 May 2017, 13:19

OK, filed an issue here: https://bugs.noesisengine.com/view.php?id=1081

Looking forward for the feature in the next release !
 
nikobarli
Topic Author
Posts: 178
Joined: 26 Apr 2017, 06:23

Re: Cannot unregister routed event handler (v2.0.1f1, C++)

22 Aug 2017, 09:28

Hi, I think I found another issue when using lambda as event handler.

I cannot capture complex object (e.g. NsString, std::string, std::vector).

For example:
            std::vector<int> tmp;
            element->AddHandler(event, [tmp](BaseComponent* sender, const RoutedEventArgs& args) {
            });
emits compiler errors:
1>D:\NNG2\NNG2\Framework\NoesisGUI\Include\NsCore/Delegate.inl(189): error C2338: Insufficient buffer size
1> D:\NNG2\NNG2\Framework\NoesisGUI\Include\NsCore/Delegate.inl(35): note: see reference to function template instantiation 'void Noesis::Core::Delegate<void (Noesis::Core::BaseComponent *,const Noesis::Gui::RoutedEventArgs &)>::FromFunctor<F>(const F &,Ret (__cdecl NoesisTutorial::EventBindingExtension::ProvideValue::<lambda_b5f86aaeec448541f8a10f7abd4dead0>::* )(Noesis::Core::BaseComponent *,const Noesis::Gui::RoutedEventArgs &) const)' being compiled
1> with
1> [
1> F=NoesisTutorial::EventBindingExtension::ProvideValue::<lambda_b5f86aaeec448541f8a10f7abd4dead0>,
1> Ret=void
1> ]
1> D:\NNG2\NNG2\Framework\NoesisGUI\Include\NsCore/Delegate.inl(35): note: see reference to function template instantiation 'void Noesis::Core::Delegate<void (Noesis::Core::BaseComponent *,const Noesis::Gui::RoutedEventArgs &)>::FromFunctor<F>(const F &,Ret (__cdecl NoesisTutorial::EventBindingExtension::ProvideValue::<lambda_b5f86aaeec448541f8a10f7abd4dead0>::* )(Noesis::Core::BaseComponent *,const Noesis::Gui::RoutedEventArgs &) const)' being compiled
1> with
1> [
1> F=NoesisTutorial::EventBindingExtension::ProvideValue::<lambda_b5f86aaeec448541f8a10f7abd4dead0>,
1> Ret=void
1> ]
1> MarkupExtension.cpp(127): note: see reference to function template instantiation 'Noesis::Core::Delegate<void (Noesis::Core::BaseComponent *,const Noesis::Gui::RoutedEventArgs &)>::Delegate<NoesisTutorial::EventBindingExtension::ProvideValue::<lambda_b5f86aaeec448541f8a10f7abd4dead0>,void>(const F &)' being compiled
1> with
1> [
1> F=NoesisTutorial::EventBindingExtension::ProvideValue::<lambda_b5f86aaeec448541f8a10f7abd4dead0>
1> ]
1> MarkupExtension.cpp(126): note: see reference to function template instantiation 'Noesis::Core::Delegate<void (Noesis::Core::BaseComponent *,const Noesis::Gui::RoutedEventArgs &)>::Delegate<NoesisTutorial::EventBindingExtension::ProvideValue::<lambda_b5f86aaeec448541f8a10f7abd4dead0>,void>(const F &)' being compiled
1> with
1> [
1> F=NoesisTutorial::EventBindingExtension::ProvideValue::<lambda_b5f86aaeec448541f8a10f7abd4dead0>
1> ]
 
User avatar
jsantos
Site Admin
Posts: 2905
Joined: 20 Jan 2012, 17:18
Contact:

Re: Cannot unregister routed event handler (v2.0.1f1, C++)

24 Aug 2017, 20:07

We implemented Noesis::Delegate because standard alternatives (std::function and similars) were too inefficient (at least not efficient in all our platforms) but it seems there are a few limitations with our implementation we should fix or evaluate again the current state of std::function (at first we don't like using C++11 stdlib features because implementations vary from platform to platform).

Could you please create a ticket about it?

Thanks!
 
nikobarli
Topic Author
Posts: 178
Joined: 26 Apr 2017, 06:23

Re: Cannot unregister routed event handler (v2.0.1f1, C++)

28 Aug 2017, 10:46

Thanks. I filed the issue here: https://bugs.noesisengine.com/view.php?id=1141

Really appreciate if you can fix this along with https://bugs.noesisengine.com/view.php?id=1081. The ability to pass lambda/std::function as delegate is critical to our team productivity.
 
User avatar
jsantos
Site Admin
Posts: 2905
Joined: 20 Jan 2012, 17:18
Contact:

Re: Cannot unregister routed event handler (v2.0.1f1, C++)

30 Aug 2017, 17:03

Thanks!
 
nikobarli
Topic Author
Posts: 178
Joined: 26 Apr 2017, 06:23

Re: Cannot unregister routed event handler (v2.0.1f1, C++)

31 Aug 2017, 04:51

Somehow able to workaround the limitations (using FunctorToDelegate wrapper in the below codes) and created a utility to convert NoesisGUI events to rxcpp's observables.
    // Utility to get the N-th type from tuple
    // Ref: https://stackoverflow.com/questions/16928669/how-to-get-n-th-type-from-a-tuple
    template <int N, typename... Ts>
    struct get;

    template <int N, typename T, typename... Ts>
    struct get<N, std::tuple<T, Ts...>>
    {
        using type = typename get<N - 1, std::tuple<Ts...>>::type;
    };

    template <typename T, typename... Ts>
    struct get<0, std::tuple<T, Ts...>>
    {
        using type = T;
    };

    // Utility to strip const/ref/pointer from a type
    // Ref https://stackoverflow.com/questions/14522496/remove-reference-with-const-references
    template<class T> struct remove_all { typedef T type; };
    template<class T> struct remove_all<T*> : remove_all<T> {};
    template<class T> struct remove_all<T&> : remove_all<T> {};
    template<class T> struct remove_all<T&&> : remove_all<T> {};
    template<class T> struct remove_all<T const> : remove_all<T> {};
    template<class T> struct remove_all<T volatile> : remove_all<T> {};
    template<class T> struct remove_all<T const volatile> : remove_all<T> {};

    // Wrap a functor and provide a method that can be used for NoesisGUI Delegate
    template<typename Ret, typename ...Args>
    class FunctorToDelegate{
    public:
        FunctorToDelegate(const std::function<Ret(Args...)> & callback) : m_callback(callback) { }
        Ret Invoke(Args... args) { return m_callback(args...); }
    private:
        std::function<Ret(Args...)> m_callback;
    };

    // FromEvent: Convert NoesisGUI events to rxcpp observables
    // When the observable is subscribed   -> add a callback to the NoesisGUI event
    // When the observable is unsubscribed -> remove the callback from the NoesisGUI event
    template<typename Ret, typename ...Args, template<typename Ret, typename ...Args> typename C>
    decltype(auto) FromEvent(C<Delegate<Ret(Args...)>> & _ev)
    {
        // Remove const & from the second Args type
        // e.g. const MouseEventArgs & -> MouseEventArgs
        // this type will be passed as parameter of the returned observable
        using var = std::tuple<Args...> ;
        using type = remove_all<get<1, var>::type>::type;

        return rxcpp::observable<>::create<type>([_ev](auto && subscriber) {
            auto ev = const_cast<C<Delegate<Ret(Args...)>> &>(_ev);
            // NoesisGUI currently cannot handle lambda with object captures
            // Use FunctorToDelegate to workaround it
            auto wrapper = std::make_shared<FunctorToDelegate<Ret, Args...>>([subscriber](Args... args) -> Ret {
                auto t = std::make_tuple(args...);
                auto param = std::get<1>(t);
                subscriber.on_next(param);
            });

            // Register a callback to NoesisGUI event
            ev += MakeDelegate(wrapper.get(), &FunctorToDelegate<Ret, Args...>::Invoke); 

            subscriber.add([_ev, wrapper]() {
                // In the event of unsubscribe, remove the callback from the NoesisGUI event
                auto ev = const_cast<C<Delegate<Ret(Args...)>> &>(_ev);
                ev -= MakeDelegate(wrapper.get(), &FunctorToDelegate<Ret, Args...>::Invoke);
            });
        });
    }

I can now use it as follows:
            auto subscription = FromEvent(element->MouseDown()).subscribe([](auto && param) {
                ...
            });
            ...
            subscription.unsubscribe();

Who is online

Users browsing this forum: No registered users and 1 guest