Fold expressions with arbitrary callable?
Asked Answered
S

2

41

Looking over the C++17 paper on folds, (and on cppreference), I'm confused as to why the choice was made to only work with operators? At first glance it seems like it would make it easier to expand (... + args) by just shoving a + token between the elements of args, but I'm unconvinced this is a great decision.

Why can't a binary lambda expression work just as well and follow the same expansion as the latter above? It's jarring to me that a fold syntax would be added to a language without support for arbitrary callables, so does the syntax allow a way to use them that I'm just not seeing?


Update: This works for a variadic min() function with clang

template <typename T>
struct MinWrapper {
    const T& obj;
};

template <typename T, typename U, typename V=std::common_type_t<T,U>>
constexpr MinWrapper<V> operator%(
        const MinWrapper<T>& lhs, const MinWrapper<U>& rhs) {
    return {lhs.obj < rhs.obj ? lhs.obj : rhs.obj};
}


template <typename... Ts>
constexpr auto min(Ts&&... args) {
    return (MinWrapper<Ts>{args} % ...).obj;
}
Subcutaneous answered 20/12, 2014 at 18:1 Comment(8)
The main reason for fold expressions is for the use in concepts. "Normal" user code can already achieve all kinds of folding with ordinary library algorithms.Effectuate
@KerrekSB One could still have a callable that uses concepts and does something other than apply an operator thoughSubcutaneous
@KerrekSB: Could you elaborate on that, or link to someplace that does? I'm interested to see where this goes.Hautesavoie
@Novelocrat: You'll find references in the intro of the Concepts TS, as well as in the cited N4191.Effectuate
Good point. Write a paper.Thermometry
Bah. Now I need to figure out a way to get this to work with named operators. Hmm. May not be possible. meh.Wilding
I think it is limited to operators to keep the proposal small. Also look at the limited list of ops that support an empty pack - I guess if you allow for arbitrary bin-fns, that whole thing would have to be expanded to handle that cleanly. That said, and in tune with your Q, I find the proposal rather terse, not to say half baked.Princessprinceton
@Columbo open-std.org isSubcutaneous
G
5

That's a great paper and a glorious language feature. If we bypass the standardese talk, which I'm not particularly fond of, I'd like to propose a workaround. Since I don't have a c++17 compiler (or a time machine) my answer will only be outlining, what I believe could be, a solution to providing fold expressions with arbitrary functions with the language status as is.

1. Define a type wrapper (a lightweight one)

template<typename T>
struct wp {
    T const& val; 
    // yes there should be constructors
};

2. Machinery to transform packs to wrapped packs

template<typename Op, typename Ts...>
using wrapped_pack = make_wrapped<Op, Ts..>

3. Overload a built in operator for wp<T>

template<typename T, typename U>
ret_val operator+(wp<T> const& lhs, wp<U> const& rhs) {...}

4. Use the wrapped pack in the fold expression

This would require an extra layer where the args of the fold are transformed to wrapped arguments


An obvious shortcoming of the above is that it doesn't guarantee uniqueness (or scalability) : every fold with a custom callable would consume a built in operator overloading.

There should be hacks to vary the types based on the expression they're encountered in but I don't want to dive that deep into a thought experiment (eg using the type of Op in the type of the wrapper already gives much more space to scale into).

Genagenappe answered 2/1, 2015 at 20:11 Comment(1)
clang current head from its svn repo has support for folds with -std=c++1zSubcutaneous
G
5

First of all I'm happy that what I wrote works in clang (I see your update implements 3 out of the 4 steps I mention). I have to give credit to Nick Athanasiou for this technique that discussed this with me way before writing this.

The reason I mention this now is because I was informed he released a library (in the boost library incubator) that implements this stuff; you can find related documentation here. It seems the initial idea (that we both use here) and allowed code like this:

(Op<Max>(args) + ...); // Op is a function producing the custom fold type

was left out in favor of lazy evaluation and stateful operators (or not included yet, can't know for sure).

Genagenappe answered 7/4, 2016 at 16:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.