Topic: Arbitrary function for parameter pack expansion by


Author: =?UTF-8?Q?Jules_P=C3=A9nuchot?= <jules@penuchot.com>
Date: Sun, 14 Oct 2018 19:33:49 -0700 (PDT)
Raw View
------=_Part_1334_1897456759.1539570829687
Content-Type: multipart/alternative;
 boundary="----=_Part_1335_1639328010.1539570829688"

------=_Part_1335_1639328010.1539570829688
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

Hello everyone,

I would like to propose an extension for parameter pack expansion.

Currently only infix operators are allowed to reduce parameter pack=20
expansions. The only ways to replace these operators by an arbitrary=20
function would be to override them directly, which is obviously not a good=
=20
option, or to create a specific decorator class to be able to replace the=
=20
operator.

I will use a function piping function as an example case to illustrate the=
=20
existing (limited) methods and the one I propose.

Direct override method :
#include <iostream>

template<typename F, typename G>
auto operator | (F f, G g)
{
  return [=3D] (auto e)
  {
    return g(f(e));
  };
};

int main()
{
  auto plus_1 =3D [](auto arg) { return arg + 1; };
  auto mult_2 =3D [](auto arg) { return arg * 2; };

  auto pipe =3D [](auto... Functions)
  {
    return ( Functions | ... );
  };

  auto test =3D pipe(mult_2, mult_2, plus_1, plus_1);

  std::cout << test(10) << std::endl;

  return 0;
}


Decorator class method :
#include <iostream>

template<typename F>
struct pipe_decorator
{
  F f;
};

template<typename Fa, typename Fb>
auto operator | (pipe_decorator<Fa> da, pipe_decorator<Fb> db)
{
  auto fun =3D [=3D](auto elmt){ return db.f(da.f(elmt)); };

  return pipe_decorator<decltype(fun)>{ fun };
}

int main()
{
  auto plus_1 =3D [](auto arg){ return arg + 1; };
  auto mult_2 =3D [](auto arg){ return arg * 2; };

  auto pipe =3D [](auto... Functions)
  {
    return ( pipe_decorator<decltype(Functions)>{ Functions } | ... ).f;
  };

  auto test =3D pipe(mult_2, mult_2, plus_1, plus_1);

  std::cout << test(10) << std::endl;

  return 0;
}


The decorator method is much better as it avoids overriding the operator=20
for all the functions, however it is not yet self-contained as it still=20
requires to create a class and an operator both external to the function we=
=20
need it in.

Being able to reduce packs with an arbitrary function would even allow us=
=20
to use lambda-capture in this context, which isn't possible in this case=20
without using std::function and therefore missing the optimizations that=20
these solutions benefit from; the code is properly unrolled and partially=
=20
evaluated at compile-time by gcc 7.1 in both cases. clang 7.0.0 wasn't able=
=20
to compile the *direct override method* but it performed the same=20
optimizations as gcc for the *decorator class method*.

The parameter pack expansion is as simple as a map-reduce, we can easily=20
alter the function mapped to each parameter but not the reduction function,=
=20
and there is no reason other than syntax for such a constraint.

Here's what it could look like :

#include <iostream>

int main()
{
  auto plus_1 =3D [](auto arg){ return arg + 1; };
  auto mult_2 =3D [](auto arg){ return arg * 2; };

  auto pipe_funs =3D [](auto F, auto G)
  {
    return [=3D](auto elmt) { return G(F(elmt)); };
  };

  auto fallthrough =3D [](auto e)
  {
    return e;
  };

  auto pipe =3D [](auto... Functions)
  {
    return std::parameter_map_reduce(fallthrough, pipe_funs, Functions...);
  };

  auto test =3D pipe(mult_2, mult_2, plus_1, plus_1);

  std::cout << test(10) << std::endl;

  return 0;
}


The simple addition of such an "std::parameter_map_reduce" function would=
=20
make it possible without even changing the language's syntax. The first=20
argument would be the function to map, the second one would be the reductor=
=20
then the rest would be the parameters to reduce. An even more minimalistic=
=20
approach would be to add an "std::parameter_reduce" function as we can=20
already map a function to the parameters, but adding both would expose more=
=20
granular options.

Any feedback or criticism regarding this proposal is welcome.

Regards,
Jules P=C3=A9nuchot

--=20
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp=
..org/d/msgid/std-proposals/8788ce93-157b-464a-8325-bfbe296c38b8%40isocpp.or=
g.

------=_Part_1335_1639328010.1539570829688
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr">Hello everyone,<div><br></div><div>I would like to propose=
 an extension for parameter pack expansion.</div><div><br></div><div>Curren=
tly only infix operators are allowed to reduce parameter pack expansions. T=
he only ways to replace these operators by an arbitrary function would be t=
o override them directly, which is obviously not a good option, or to creat=
e a specific decorator class to be able to replace the operator.</div><div>=
<br></div><div>I will use a function piping function as an example case to =
illustrate the existing (limited) methods and the one I propose.</div><div>=
<br></div><div>Direct override method :</div><div><div class=3D"prettyprint=
" style=3D"background-color: rgb(250, 250, 250); border-color: rgb(187, 187=
, 187); border-style: solid; border-width: 1px; overflow-wrap: break-word;"=
><code class=3D"prettyprint"><div class=3D"subprettyprint"><div class=3D"su=
bprettyprint"><div class=3D"subprettyprint"><font color=3D"#660066">#includ=
e &lt;iostream&gt;</font></div><div class=3D"subprettyprint"><font color=3D=
"#660066"><br></font></div><div class=3D"subprettyprint"><font color=3D"#66=
0066">template&lt;typename F, typename G&gt;</font></div><div class=3D"subp=
rettyprint"><font color=3D"#660066">auto operator | (F f, G g)</font></div>=
<div class=3D"subprettyprint"><font color=3D"#660066">{</font></div><div cl=
ass=3D"subprettyprint"><font color=3D"#660066">=C2=A0 return [=3D] (auto e)=
</font></div><div class=3D"subprettyprint"><font color=3D"#660066">=C2=A0 {=
</font></div><div class=3D"subprettyprint"><font color=3D"#660066">=C2=A0 =
=C2=A0 return g(f(e));</font></div><div class=3D"subprettyprint"><font colo=
r=3D"#660066">=C2=A0 };</font></div><div class=3D"subprettyprint"><font col=
or=3D"#660066">};</font></div><div class=3D"subprettyprint"><font color=3D"=
#660066"><br></font></div><div class=3D"subprettyprint"><font color=3D"#660=
066">int main()</font></div><div class=3D"subprettyprint"><font color=3D"#6=
60066">{</font></div><div class=3D"subprettyprint"><font color=3D"#660066">=
=C2=A0 auto plus_1 =3D [](auto arg) { return arg + 1; };</font></div><div c=
lass=3D"subprettyprint"><font color=3D"#660066">=C2=A0 auto mult_2 =3D [](a=
uto arg) { return arg * 2; };</font></div><div class=3D"subprettyprint"><fo=
nt color=3D"#660066"><br></font></div><div class=3D"subprettyprint"><font c=
olor=3D"#660066">=C2=A0 auto pipe =3D [](auto... Functions)</font></div><di=
v class=3D"subprettyprint"><font color=3D"#660066">=C2=A0 {</font></div><di=
v class=3D"subprettyprint"><font color=3D"#660066">=C2=A0 =C2=A0 return ( F=
unctions | ... );</font></div><div class=3D"subprettyprint"><font color=3D"=
#660066">=C2=A0 };</font></div><div class=3D"subprettyprint"><font color=3D=
"#660066"><br></font></div><div class=3D"subprettyprint"><font color=3D"#66=
0066">=C2=A0 auto test =3D pipe(mult_2, mult_2, plus_1, plus_1);</font></di=
v><div class=3D"subprettyprint"><font color=3D"#660066"><br></font></div><d=
iv class=3D"subprettyprint"><font color=3D"#660066">=C2=A0 std::cout &lt;&l=
t; test(10) &lt;&lt; std::endl;</font></div><div class=3D"subprettyprint"><=
font color=3D"#660066"><br></font></div><div class=3D"subprettyprint"><font=
 color=3D"#660066">=C2=A0 return 0;</font></div><div class=3D"subprettyprin=
t"><font color=3D"#660066">}</font></div></div><div><br></div></div></code>=
</div><br>Decorator class method :</div><div><div class=3D"prettyprint" sty=
le=3D"background-color: rgb(250, 250, 250); border-color: rgb(187, 187, 187=
); border-style: solid; border-width: 1px; overflow-wrap: break-word;"><cod=
e class=3D"prettyprint"><div class=3D"subprettyprint"><div class=3D"subpret=
typrint"><div class=3D"subprettyprint"><div class=3D"subprettyprint"><font =
color=3D"#660066">#include &lt;iostream&gt;</font></div><div class=3D"subpr=
ettyprint"><font color=3D"#660066"><br></font></div><div class=3D"subpretty=
print"><font color=3D"#660066">template&lt;typename F&gt;</font></div><div =
class=3D"subprettyprint"><font color=3D"#660066">struct pipe_decorator</fon=
t></div><div class=3D"subprettyprint"><font color=3D"#660066">{</font></div=
><div class=3D"subprettyprint"><font color=3D"#660066">=C2=A0 F f;</font></=
div><div class=3D"subprettyprint"><font color=3D"#660066">};</font></div><d=
iv class=3D"subprettyprint"><font color=3D"#660066"><br></font></div><div c=
lass=3D"subprettyprint"><font color=3D"#660066">template&lt;typename Fa, ty=
pename Fb&gt;</font></div><div class=3D"subprettyprint"><font color=3D"#660=
066">auto operator | (pipe_decorator&lt;Fa&gt; da, pipe_decorator&lt;Fb&gt;=
 db)</font></div><div class=3D"subprettyprint"><font color=3D"#660066">{</f=
ont></div><div class=3D"subprettyprint"><font color=3D"#660066">=C2=A0 auto=
 fun =3D [=3D](auto elmt){ return db.f(da.f(elmt)); };</font></div><div cla=
ss=3D"subprettyprint"><font color=3D"#660066"><br></font></div><div class=
=3D"subprettyprint"><font color=3D"#660066">=C2=A0 return pipe_decorator&lt=
;decltype(fun)&gt;{ fun };</font></div><div class=3D"subprettyprint"><font =
color=3D"#660066">}</font></div><div class=3D"subprettyprint"><font color=
=3D"#660066"><br></font></div><div class=3D"subprettyprint"><font color=3D"=
#660066">int main()</font></div><div class=3D"subprettyprint"><font color=
=3D"#660066">{</font></div><div class=3D"subprettyprint"><font color=3D"#66=
0066">=C2=A0 auto plus_1 =3D [](auto arg){ return arg + 1; };</font></div><=
div class=3D"subprettyprint"><font color=3D"#660066">=C2=A0 auto mult_2 =3D=
 [](auto arg){ return arg * 2; };</font></div><div class=3D"subprettyprint"=
><font color=3D"#660066"><br></font></div><div class=3D"subprettyprint"><fo=
nt color=3D"#660066">=C2=A0 auto pipe =3D [](auto... Functions)</font></div=
><div class=3D"subprettyprint"><font color=3D"#660066">=C2=A0 {</font></div=
><div class=3D"subprettyprint"><font color=3D"#660066">=C2=A0 =C2=A0 return=
 ( pipe_decorator&lt;decltype(Functions)&gt;{ Functions } | ... ).f;</font>=
</div><div class=3D"subprettyprint"><font color=3D"#660066">=C2=A0 };</font=
></div><div class=3D"subprettyprint"><font color=3D"#660066"><br></font></d=
iv><div class=3D"subprettyprint"><font color=3D"#660066">=C2=A0 auto test =
=3D pipe(mult_2, mult_2, plus_1, plus_1);</font></div><div class=3D"subpret=
typrint"><font color=3D"#660066"><br></font></div><div class=3D"subprettypr=
int"><font color=3D"#660066">=C2=A0 std::cout &lt;&lt; test(10) &lt;&lt; st=
d::endl;</font></div><div class=3D"subprettyprint"><font color=3D"#660066">=
<br></font></div><div class=3D"subprettyprint"><font color=3D"#660066">=C2=
=A0 return 0;</font></div><div class=3D"subprettyprint"><font color=3D"#660=
066">}</font></div><div><br></div></div></div></div></code></div><div><br><=
/div>The decorator method is much better as it avoids overriding the operat=
or for all the functions, however it is not yet self-contained as it still =
requires to create a class and an operator both external to the function we=
 need it in.<br></div><div><br></div><div>Being able to reduce packs with a=
n arbitrary function would even allow us to use lambda-capture in this cont=
ext, which isn&#39;t possible in this case without using std::function and =
therefore missing the optimizations that these solutions benefit from; the =
code is properly unrolled and partially evaluated at compile-time by gcc 7.=
1 in both cases. clang 7.0.0 wasn&#39;t able to compile the <i>direct overr=
ide method</i> but it performed the same optimizations as gcc for the <i>de=
corator class method</i>.</div><div><br></div><div>The parameter pack expan=
sion is as simple as a map-reduce, we can easily alter the function mapped =
to each parameter but not the reduction function, and there is no reason ot=
her than syntax for such a constraint.</div><div><br></div><div>Here&#39;s =
what it could look like :</div><div><br></div><div><div class=3D"prettyprin=
t" style=3D"background-color: rgb(250, 250, 250); border-color: rgb(187, 18=
7, 187); border-style: solid; border-width: 1px; overflow-wrap: break-word;=
"><code class=3D"prettyprint"><div class=3D"subprettyprint"><div class=3D"s=
ubprettyprint"><font color=3D"#660066">#include &lt;iostream&gt;</font></di=
v><div class=3D"subprettyprint"><font color=3D"#660066"><br></font></div><d=
iv class=3D"subprettyprint"><font color=3D"#660066">int main()</font></div>=
<div class=3D"subprettyprint"><font color=3D"#660066">{</font></div><div cl=
ass=3D"subprettyprint"><font color=3D"#660066">=C2=A0 auto plus_1 =3D [](au=
to arg){ return arg + 1; };</font></div><div class=3D"subprettyprint"><font=
 color=3D"#660066">=C2=A0 auto mult_2 =3D [](auto arg){ return arg * 2; };<=
/font></div><div class=3D"subprettyprint"><font color=3D"#660066"><br></fon=
t></div><div class=3D"subprettyprint"><font color=3D"#660066">=C2=A0 auto p=
ipe_funs =3D [](auto F, auto G)</font></div><div class=3D"subprettyprint"><=
font color=3D"#660066">=C2=A0 {</font></div><div class=3D"subprettyprint"><=
font color=3D"#660066">=C2=A0 =C2=A0 return [=3D](auto elmt) { return G(F(e=
lmt)); };</font></div><div class=3D"subprettyprint"><font color=3D"#660066"=
>=C2=A0 };</font></div><div class=3D"subprettyprint"><font color=3D"#660066=
"><br></font></div><div class=3D"subprettyprint"><font color=3D"#660066">=
=C2=A0 auto fallthrough =3D [](auto e)</font></div><div class=3D"subprettyp=
rint"><font color=3D"#660066">=C2=A0 {</font></div><div class=3D"subprettyp=
rint"><font color=3D"#660066">=C2=A0 =C2=A0 return e;</font></div><div clas=
s=3D"subprettyprint"><font color=3D"#660066">=C2=A0 };</font></div><div cla=
ss=3D"subprettyprint"><font color=3D"#660066"><br></font></div><div class=
=3D"subprettyprint"><font color=3D"#660066">=C2=A0 auto pipe =3D [](auto...=
 Functions)</font></div><div class=3D"subprettyprint"><font color=3D"#66006=
6">=C2=A0 {</font></div><div class=3D"subprettyprint"><font color=3D"#66006=
6">=C2=A0 =C2=A0=C2=A0return std::parameter_map_reduce(fallthrough, pipe_fu=
ns, Functions...);</font></div><div class=3D"subprettyprint"><font color=3D=
"#660066">=C2=A0 };</font></div><div class=3D"subprettyprint"><font color=
=3D"#660066"><br></font></div><div class=3D"subprettyprint"><font color=3D"=
#660066">=C2=A0 auto test =3D pipe(mult_2, mult_2, plus_1, plus_1);</font><=
/div><div class=3D"subprettyprint"><font color=3D"#660066"><br></font></div=
><div class=3D"subprettyprint"><font color=3D"#660066">=C2=A0 std::cout &lt=
;&lt; test(10) &lt;&lt; std::endl;</font></div><div class=3D"subprettyprint=
"><font color=3D"#660066"><br></font></div><div class=3D"subprettyprint"><f=
ont color=3D"#660066">=C2=A0 return 0;</font></div><div class=3D"subprettyp=
rint"><font color=3D"#660066">}</font></div><div><br></div></div></code></d=
iv><br>The simple addition of such an &quot;std::parameter_map_reduce&quot;=
 function would make it possible without even changing the language&#39;s s=
yntax. The first argument would be the function to map, the second one woul=
d be the reductor then the rest would be the parameters to reduce. An even =
more minimalistic approach would be to add an &quot;std::parameter_reduce&q=
uot; function as we can already map a function to the parameters, but addin=
g both would expose more granular options.</div><div><br></div><div>Any fee=
dback or criticism regarding this proposal is welcome.</div><div><br></div>=
<div>Regards,</div><div>Jules P=C3=A9nuchot</div></div>

<p></p>

-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals&quot; group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/8788ce93-157b-464a-8325-bfbe296c38b8%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/8788ce93-157b-464a-8325-bfbe296c38b8=
%40isocpp.org</a>.<br />

------=_Part_1335_1639328010.1539570829688--

------=_Part_1334_1897456759.1539570829687--

.