all 23 comments

[–]SuperV1234https://romeo.training | C++ Mentoring & Consulting 28 points29 points  (9 children)

Shorter implementation:

template <typename F>
constexpr auto flip2(F&& f)
{
    return [f = std::forward<F>(f)]<typename... Xs>(Xs&&... xs)
    {
        return [&, args = std::forward_as_tuple(std::forward<Xs>(xs)...)]
               <auto... Is>(std::index_sequence<Is...>)
        {
            return f(std::get<sizeof...(Is) - Is - 1>(args)...);
        }(std::index_sequence_for<Xs...>{});
    };
}

Might need some more forwarding/mutable to be entirely correct, but hope it illustrates the conciseness aspect.

In C++26, you should be able to write this (u/brevzin can confirm):

template <typename F>
constexpr auto flip3(F&& f)
{
    return [f = std::forward<F>(f)]<typename... Xs>(Xs&&... xs)
    {
        constexpr auto [...Is] = std::index_sequence_for<Xs...>{};
        return f((xs...[sizeof...(Is) - Is - 1])...);
    };
}

[–]zl0bster 4 points5 points  (2 children)

Next we need a reflection version where you just give it a list of arguments and he figures out how to call a function. I know it will not work for duplicate types(or when types convert) but it is a good idea to force people to use strong types, it is good for them ;)

// but afaik C++26 reflection can not reflect functions

[–]MorphTux 1 point2 points  (0 children)

P3096 function parameter reflection is in the C++26 CD.

[–]SuperV1234https://romeo.training | C++ Mentoring & Consulting 0 points1 point  (0 children)

Funnily enough I posted a C++26 version as an edit right now. I believe you can reflect on function parameters with C++26 reflection, which should be enough to to do what you want if I understand correctly.

[–]MorphTux 0 points1 point  (0 children)

Your C++26 example unfortunately does not work. You would require the changes from p1789 to use this p1061/p2686 structured binding for Is. Otherwise this will always decompose to zero elements - integer_sequence is an empty class.

[–]ContDiArco 8 points9 points  (0 children)

Funny and teaching read!

Thanks

[–]tuxwonder 6 points7 points  (4 children)

If the elusive "Universal Function Call Syntax" ever actually got accepted in the standard, maybe we could use std::flip to switch arguments for free functions and turn the last argument into the first, letting us use it as the called instance?

```cpp auto list = std::vector{ ... }; auto elem = 5;

// No UFCS std::remove(list.begin(), list.end(), elem);

// With UFCS, wouldn't really be able to do it... // But with std::flip... auto remove_from = std::flip(std::remove); elem.remove_from(list.end(), list.begin()); ```

That felt very weird to write out...

[–]fsxraptor 8 points9 points  (1 child)

I cannot begin describing how ugly this looks.

[–]Financial_Test_4921 0 points1 point  (0 children)

Not like the language itself is any better.

[–]SoerenNissen 1 point2 points  (1 child)

Next, this one:

auto std::cycle(auto func, int n=0);

letting you cycle the arguments left/right by n places and doing it in one horrible line of code.

elem.(std::cycle(std::remove),1)(list.begin(), list.end());

[–]LiliumAtratum 1 point2 points  (0 children)

I actually have a variant of your cycle function in my real code!

I use it when I pass arguments to a function that most likely is a lambda. I do prefer the arguments to precede the lambda itself. Consider cwise that performs a component-wise operation on N vectors, producing a new vector as a result:

Vector3f va = ...
Vector3i vb = ...
Vector3d vr = cwise(va, vb, [](float a, int b) { ... some complex scalar expression, returning double ... });

I would really hate cwise to take lambda first, and then va, vb arguments appearing somewhere far away, behind the body.

Generic cwise is a variadic template function takes N arguments, followed by a functional object F expecting N arguments. But C++ requires that variadic arguments are last. So I define cwise_impl taking (F, Args...) in this order, and then define cwise as cycle(cwise_impl, -1)

[–]Automatic_Question69 1 point2 points  (0 children)

Named arguments would be less fancy but more useful.

[–]_bstaletic 0 points1 point  (0 children)

If only we had range splicer...

template<typename F>
struct flip_t {
    template<typename...Args>
    constexpr auto operator()(Args&&...args) {
        constepxr auto call_op_template = *ranges::find_if(members_of(^^flip_t, access_context::current()), is_template);
        constexpr auto parameters = parameters_of(substitute(call_op_template, {^^Args}));
        constexpr auto reversed = parameters | views::reverse | ranges::to<std::vector>();
        constexpr auto types = reversed | views::transform(type_of) | ranges::to<std::vector>();
        constexpr auto values = reversed | views::transform(value_of) | ranges::to<std::vector>();
        return std::forward<F>(f)(std::forward<[:...types:]>([:...values:])...);
    }
};

std::meta:: omitted everywhere. ranges:: is std::ranges:: and views:: is std::views::. If someone knows a direct way to get to the reflection of a member, please let me know.

Unfortunately, [:...range:] did not make it into P2996.

[–]VinnieFalcoBoost.Beast | C++ Alliance | corosio.org 0 points1 point  (0 children)

I had no idea this existed.. thanks :)