Is the proposed fold syntax for c++17 too limited?

After reading this paper, and this cppreference page, and then posting a stack overflow question about it. I went ahead and built the latest clang from source to start messing around with fold expressions. Writing a sum() function is straight forward.

template <typename... Ts>
auto sum(Ts&&... args) {
  return (args + ...);
}

The syntax is nice, but imo a bit limited. The next thing I wanted was a variadic min() function. This is where the proposed syntax starts to seem weak. Obviously the outline of the function should be easy:

template <typename... Ts>
auto min(Ts&&... args) {
  return // something
}

but there’s no builtin operator in C++ that takes two things and returns the lesser of the two. It’s a pretty easy function to write, but it doesn’t work with the fold syntax. It surprises me that folds would be proposed without a way to have them work with functions. They’re functional in origin, after all.

Here is my workaround

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

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

template <typename... Ts>
auto min(Ts&&... args) {
  return (MinWrapper<Ts>{args} % ...).obj;
}

Okay, the MinWrapper is just a struct that binds a reference. This just gives me a type to define an operator on. After that I define operator% just because modulus seems to be the thing people use when they’re doing something terrible. The mod operator compares the items in each wrapper, and returns a wrapper with their common type. This actually does work in clang’s current head believe it or not.

My question is: if I thought of (nearly) this monstrosity before I even had a compiler in front of me to run it with, is this going to be a pattern that emerges once folds make it into the language for real? Probably.

The solution would be to extend the syntax to allow not only operators, but binary functions as well, this would let me write

template <typename... Ts>
auto min(Ts&&... args) {
  auto sel_min =
    [](auto&& lhs, auto&& rhs) -> auto&& 
      {return lhs < rhs ? lhs : rhs;};
  return (args sel_min ...);
}

not the prettiest lambda either, but imo, much better than what I’m doing with the current syntax.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s