Topic: Exception safety of std::function chaining composition


Author: David Krauss <potswa@gmail.com>
Date: Thu, 14 Jan 2016 16:24:14 +0800
Raw View
--Apple-Mail=_FCE1B772-62FD-4D2C-BAF8-2DFE1E9E0BDB
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain; charset=UTF-8

There=E2=80=99s a convenient idiom for composing std::function objects in a=
 chain:

std::function< ret( arg ) > f;

template< typename G >
void compose( G && g ) {
    f =3D [next =3D std::move( f ),
         g =3D std::forward< G >( g )]
        ( arg a ) { return g( next( a ) ); };
    // (Body may be something else involving f and g.)
}

This is nice, but what about exception safety? If the copy/move constructor=
 of G throws, or if std::function fails to allocate memory, then f is left =
in the moved-from state. If the program doesn=E2=80=99t anticipate this con=
dition, there could be an unexpected bad_function_call or other broken inva=
riant. It would be better to leave f unmodified.

The simplest solution, that I see, is to delay modifying f until the last m=
inute.

template< typename G >
void compose( G && g ) {
    struct composer {
        G g;
        std::function< ret( arg ) > next;

        ret operator () ( arg a )
            { return g( next( a ) ); }
    };
    std::function< ret( arg ) > cf
        =3D composer{ std::forward< G >( g ) };
    cf.target< composer >()->next =3D std::move( f );
    f =3D std::move( cf );
}

That=E2=80=99s a bit tricky=E2=80=A6 Perhaps there should be something like=
 this in the standard library? It could look generically like,

template< typename ret, typename ... arg, typename comp >
void compose( std::function< ret( arg ... ) > & f, comp && c );

usage like,

typedef std::function< ret( arg ) > fn;
fn f =3D []( arg a ) { return a; };
compose( f, []( fn & next, arg a ) { return 1 + next( 1 + a ); } );

The modifiable reference argument is a little odd. Maybe it should be named=
 operator+=3D or operator*=3D instead?

Once I noticed the problem, I dug up another instance of it in my code base=
.. It=E2=80=99s too bad the idiom doesn=E2=80=99t work perfectly, given its =
elegance.

--=20

---=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.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-propos=
als/.

--Apple-Mail=_FCE1B772-62FD-4D2C-BAF8-2DFE1E9E0BDB
Content-Transfer-Encoding: quoted-printable
Content-Type: text/html; charset=UTF-8

<html><head><meta http-equiv=3D"Content-Type" content=3D"text/html charset=
=3Dutf-8"></head><body style=3D"word-wrap: break-word; -webkit-nbsp-mode: s=
pace; -webkit-line-break: after-white-space;" class=3D"">There=E2=80=99s a =
convenient idiom for composing <font face=3D"Courier" class=3D"">std::funct=
ion</font> objects in a chain:<div class=3D""><br class=3D""></div><div cla=
ss=3D""><span style=3D"font-family: Courier;" class=3D"">std::function&lt; =
ret( arg ) &gt; f;</span></div><div class=3D""><font face=3D"Courier" class=
=3D""><br class=3D""></font></div><div class=3D""><font face=3D"Courier" cl=
ass=3D"">template&lt; typename G &gt;</font></div><div class=3D""><font fac=
e=3D"Courier" class=3D"">void compose( G &amp;&amp; g ) {</font></div><div =
class=3D""><font face=3D"Courier" class=3D"">&nbsp; &nbsp; f =3D [next =3D =
std::move( f ),</font></div><div class=3D""><font face=3D"Courier" class=3D=
"">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;g =3D std::forward&lt; G &gt;( g )]</f=
ont></div><div class=3D""><font face=3D"Courier" class=3D"">&nbsp; &nbsp; &=
nbsp; &nbsp; ( arg a ) { return g( next( a ) ); };</font></div><div class=
=3D""><font face=3D"Courier" class=3D"">&nbsp; &nbsp; // (Body may be somet=
hing else involving f and g.)</font></div><div class=3D""><font face=3D"Cou=
rier" class=3D"">}</font></div><div class=3D""><br class=3D""></div><div cl=
ass=3D"">This is nice, but what about exception safety? If the copy/move co=
nstructor of&nbsp;<font face=3D"Courier" class=3D"">G</font>&nbsp;throws, o=
r if&nbsp;<font face=3D"Courier" class=3D"">std::function</font> fails to a=
llocate memory, then <font face=3D"Courier" class=3D"">f</font> is left in =
the moved-from state. If the program doesn=E2=80=99t anticipate this condit=
ion, there could be an unexpected <font face=3D"Courier" class=3D"">bad_fun=
ction_call</font>&nbsp;or other broken invariant. It would be better to lea=
ve <font face=3D"Courier" class=3D"">f</font> unmodified.</div><div class=
=3D""><br class=3D""></div><div class=3D"">The simplest solution, that I se=
e, is to delay modifying <font face=3D"Courier" class=3D"">f</font> until t=
he last minute.</div><div class=3D""><br class=3D""></div><div class=3D""><=
div class=3D""><font face=3D"Courier" class=3D"">template&lt; typename G &g=
t;</font></div><div class=3D""><font face=3D"Courier" class=3D"">void compo=
se( G &amp;&amp; g ) {</font></div><div class=3D""><font face=3D"Courier" c=
lass=3D"">&nbsp; &nbsp; struct&nbsp;composer {</font></div><div class=3D"">=
<span style=3D"font-family: Courier;" class=3D"">&nbsp; &nbsp; &nbsp; &nbsp=
; G g;</span></div><div class=3D""><div class=3D""><font face=3D"Courier" c=
lass=3D"">&nbsp; &nbsp; &nbsp; &nbsp; std::function&lt; ret( arg ) &gt; nex=
t;</font></div></div><div class=3D""><font face=3D"Courier" class=3D""><br =
class=3D""></font></div><div class=3D""><font face=3D"Courier" class=3D"">&=
nbsp; &nbsp; &nbsp; &nbsp; ret operator () ( arg a )</font></div><div class=
=3D""><font face=3D"Courier" class=3D"">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =
&nbsp; { return g( next( a ) ); }</font></div><div class=3D""><font face=3D=
"Courier" class=3D"">&nbsp; &nbsp; };</font></div><div class=3D""><font fac=
e=3D"Courier" class=3D"">&nbsp; &nbsp;&nbsp;</font><span style=3D"font-fami=
ly: Courier;" class=3D"">std::function&lt; ret( arg ) &gt; cf</span></div><=
div class=3D""><font face=3D"Courier" class=3D"">&nbsp; &nbsp; &nbsp; &nbsp=
; =3D&nbsp;</font><span style=3D"font-family: Courier;" class=3D"">composer=
</span><font face=3D"Courier" class=3D"">{&nbsp;</font><span style=3D"font-=
family: Courier;" class=3D"">std::forward&lt; G &gt;( g )</span><font face=
=3D"Courier" class=3D"">&nbsp;};</font></div><div class=3D""><span style=3D=
"font-family: Courier;" class=3D"">&nbsp; &nbsp; cf.target&lt; composer &gt=
;()-&gt;next =3D std::move( f );</span></div><div class=3D""><span style=3D=
"font-family: Courier;" class=3D"">&nbsp; &nbsp; f =3D std::move( cf );</sp=
an></div><div class=3D""><span style=3D"font-family: Courier;" class=3D"">}=
</span></div></div><div class=3D""><br class=3D""></div><div class=3D"">Tha=
t=E2=80=99s a bit tricky=E2=80=A6 Perhaps there should be something like th=
is in the standard library? It could look generically like,</div><div class=
=3D""><br class=3D""></div><div class=3D""><font face=3D"Courier" class=3D"=
">template&lt; typename ret, typename ... arg, typename comp &gt;</font></d=
iv><div class=3D""><font face=3D"Courier" class=3D"">void compose( std::fun=
ction&lt; ret( arg ... ) &gt; &amp; f, comp &amp;&amp; c );</font></div><di=
v class=3D""><br class=3D""></div><div class=3D"">usage like,</div><div cla=
ss=3D""><br class=3D""></div><div class=3D""><font face=3D"Courier" class=
=3D"">typedef std::function&lt; ret( arg ) &gt; fn;</font></div><div class=
=3D""><font face=3D"Courier" class=3D"">fn f =3D []( arg a ) { return a; };=
</font></div><div class=3D""><font face=3D"Courier" class=3D"">compose( f, =
[]( fn &amp; next, arg a ) { return 1 + next( 1 + a ); } );</font></div><di=
v class=3D""><br class=3D""></div><div class=3D"">The modifiable reference =
argument is a little odd. Maybe it should be named <font face=3D"Courier" c=
lass=3D"">operator+=3D</font>&nbsp;or <font face=3D"Courier" class=3D"">ope=
rator*=3D</font> instead?</div><div class=3D""><br class=3D""></div><div cl=
ass=3D"">Once I noticed the problem, I dug up another instance of it in my =
code base. It=E2=80=99s too bad&nbsp;the idiom&nbsp;doesn=E2=80=99t work pe=
rfectly, given its elegance.</div><div class=3D""><br class=3D""></div></bo=
dy></html>

<p></p>

-- <br />
<br />
--- <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 />
Visit this group at <a href=3D"https://groups.google.com/a/isocpp.org/group=
/std-proposals/">https://groups.google.com/a/isocpp.org/group/std-proposals=
/</a>.<br />

--Apple-Mail=_FCE1B772-62FD-4D2C-BAF8-2DFE1E9E0BDB--

.