xtensor-stack / xtl

The x template library
BSD 3-Clause "New" or "Revised" License
207 stars 96 forks source link

Implement partial dispatch #239

Closed davidbrochart closed 3 years ago

davidbrochart commented 3 years ago

Checklist

Description

davidbrochart commented 3 years ago

The first commit implemented a partial dispatch which supported only 1 argument on which to dispatch and 1 argument on which not to dispatch ("ignored argument"). This second commit supports an arbitrary number of arguments on which to dispatch and 1 ignored argument.

davidbrochart commented 3 years ago

I'm wondering if it's possible to have the partial dispatcher not separate from the normal dispatcher, with an optional ignored argument.

JohanMabille commented 3 years ago

It's actually possible and I think it's the way to go.Let's illustrate this with the basic_fast_dispatcher, the reasoning is similar with the basic_dispatcher. The idea is to add a template parameter for the non dispatched arguments after the return_type parameter:

template
    <
        class type_list,
        class return_type,
        class undispatched_type_list
        class callback_type
    >
    class basic_fast_dispatcher;

and define the specialization for mpl::vector only:

    template
    <
        class return_type,
        class callback_type,
        class... B,
        class... T,
    >
    class basic_fast_dispatcher<mpl::vector<B...>, return_type, mpl::vector<T...>, callback_type>
    {
        // Implementation very similar to the existing one except that you forward the non dispatched arguments
        inline return_type dispatch(B&... args, T&... udargs) const
        {
            index_type index = {{args.get_class_index()...}};
            return dispatch_impl<0>(m_callbacks, index, args..., udargs...);
        }
    };

The template parameter for non dispatched type is also added to the functor_dispatcher, and is defaulted to the empty mpl::vector:

    template
    <
        class type_list,
        class return_type,
        class undispatched_type = mpl::vector<>,
        template <class, class> class casting_policy = dynamic_caster,
        template <class, class, class> class dispatcher = basic_dispatcher
    >
    class functor_dispatcher;

        template
    <
        class return_type, 
        template <class, class> class casting_policy,
        template <class, class, class> class dispatcher,
        class... B,
        class... T
    >
    class functor_dispatcher<mpl::vector<B...>, return_type, mpl::vector<T...>, casting_policy, dispatcher>
    {
    // Similar implementation except that you forward the additional arguments without casting them:
        template <class... D, class Fun>
        void insert(const Fun& fun)
        {
            functor_type f([fun](B&... args, T&... targs) -> return_type
            {
                return fun(casting_policy<D&, B&>::cast(args)..., targs...);
            });
            m_backend.template insert<D...>(std::move(f));
        }

        inline return_type dispatch(B&... args, T&... targs) const
        {
            return m_backend.dispatch(args..., targs);
        }
    };
davidbrochart commented 3 years ago

Thanks @JohanMabille. It's still annoying to have to set a default parameter (for which you want the default value) when you want to set a parameter that is further in the list. Maybe @serge-sans-paille could use his C++ fu to do something similar to params14 for template parameters :smile:

JohanMabille commented 3 years ago

Awesome!