Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

boost::mp11::mp_transform is not SFINAE friendly on transform error #101

Open
wanghan02 opened this issue Nov 27, 2024 · 3 comments
Open

Comments

@wanghan02
Copy link

Example could be found below or on godbolt. boost::mp11::mp_transform is not SFINAE friendly on transform error. It is SFINAE friendly on size mismatch though. It would be very helpful in constraints if it is SFINAE friendly.

template<template<typename...> typename F, typename... List>
concept has_boost_mp_transform = requires { typename boost::mp11::mp_transform<F, List...>; };

static_assert(!has_boost_mp_transform<std::remove_cvref_t, boost::mp11::mp_list<int&, double const>, std::tuple<short, int const&>>); // invalid transform. Does not compile.
static_assert(!has_boost_mp_transform<std::common_type_t, boost::mp11::mp_list<char*, float>, boost::mp11::mp_list<int, double>>); // invalid transform result. Does not compile.
static_assert(!has_boost_mp_transform<std::common_type_t, boost::mp11::mp_list<short, float>, boost::mp11::mp_list<int, double, char>>); // size mismatch. Compiles.

An example implementation for c++20 could be found below or on godbolt.

namespace no_adl {
    template<template<typename...> typename F, typename List0, typename... List>
    struct mp_transform_impl {};

    template<template<typename...> typename F, template<typename...> typename TList0, typename... List>
    struct mp_transform_impl<F, TList0<>, List...> {
        using type = TList0<>;
    };

    template<template<typename...> typename F, template<typename...> typename TList0, typename T0, typename... T, typename... List> requires
        requires { typename F<T0, boost::mp11::mp_front<List>...>; } &&
        requires { typename mp_transform_impl<F, TList0<T...>, boost::mp11::mp_pop_front<List>...>::type; }
    struct mp_transform_impl<F, TList0<T0, T...>, List...> {
        using type = boost::mp11::mp_append<
            TList0<F<T0, boost::mp11::mp_front<List>...>>,
            typename mp_transform_impl<F, TList0<T...>, boost::mp11::mp_pop_front<List>...>::type
        >;
    };

    template<template<typename...> typename F, typename... List>
        requires boost::mp11::mp_same<boost::mp11::mp_size<List>...>::value && (0<sizeof...(List))
    struct mp_transform: mp_transform_impl<F, List...> {};
}

template<template<typename...> typename F, typename... List>
using mp_transform = typename no_adl::mp_transform<F, List...>::type;
@pdimov
Copy link
Member

pdimov commented Nov 27, 2024

C++20 implementations aren't of much help to me; the library supports C++11 and up.

@wanghan02
Copy link
Author

C++20 implementations aren't of much help to me; the library supports C++11 and up.

c++11 version could be found below or on godbolt.

namespace no_adl {
    template<template<typename...> class F, typename ListList, typename=void>
    struct mp_transform_impl {};

    template<template<typename...> class F, template<typename...> class TList0, typename... List>
    struct mp_transform_impl<
        F,
        boost::mp11::mp_list<TList0<>, List...>
    > {
        using type = TList0<>;
    };

    template<template<typename...> class F, template<typename...> class TList0, typename T0, typename... T, typename... List>
    struct mp_transform_impl<
        F,
        boost::mp11::mp_list<TList0<T0, T...>, List...>,
        typename std::enable_if<!std::is_same<
            boost::mp11::mp_list<
                F<T0, boost::mp11::mp_front<List>...>,
                typename mp_transform_impl<F, boost::mp11::mp_list<TList0<T...>, boost::mp11::mp_pop_front<List>...>>::type
            >,
            void
        >::value>::type
    > {
        using type = boost::mp11::mp_append<
            TList0<F<T0, boost::mp11::mp_front<List>...>>,
            typename mp_transform_impl<F, boost::mp11::mp_list<TList0<T...>, boost::mp11::mp_pop_front<List>...>>::type
        >;
    };

    template<template<typename...> class F, typename ListList, typename=void>
    struct mp_transform {};

    template<template<typename...> class F, typename... List>
    struct mp_transform<
        F,
        boost::mp11::mp_list<List...>,
        typename std::enable_if<
            boost::mp11::mp_same<boost::mp11::mp_size<List>...>::value &&
            0<sizeof...(List)
        >::type
    >: mp_transform_impl<F, boost::mp11::mp_list<List...>> {};
}

template<template<typename...> class F, typename... List>
using mp_transform = typename no_adl::mp_transform<F, boost::mp11::mp_list<List...>>::type;

@pdimov
Copy link
Member

pdimov commented Nov 27, 2024

The problem with using the generic implementation is that it's much, much slower than the specialized variants for 1/2/3 lists. I'll see what can be done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants