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< =
ret( arg ) > 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< typename G ></font></div><div class=3D""><font fac=
e=3D"Courier" class=3D"">void compose( G && g ) {</font></div><div =
class=3D""><font face=3D"Courier" class=3D""> f =3D [next =3D =
std::move( f ),</font></div><div class=3D""><font face=3D"Courier" class=3D=
""> g =3D std::forward< G >( g )]</f=
ont></div><div class=3D""><font face=3D"Courier" class=3D""> &=
nbsp; ( arg a ) { return g( next( a ) ); };</font></div><div class=
=3D""><font face=3D"Courier" class=3D""> // (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 <font face=3D"Courier" class=3D"">G</font> throws, o=
r if <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> 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< typename G &g=
t;</font></div><div class=3D""><font face=3D"Courier" class=3D"">void compo=
se( G && g ) {</font></div><div class=3D""><font face=3D"Courier" c=
lass=3D""> struct composer {</font></div><div class=3D"">=
<span style=3D"font-family: Courier;" class=3D"">  =
; G g;</span></div><div class=3D""><div class=3D""><font face=3D"Courier" c=
lass=3D""> std::function< ret( arg ) > 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; ret operator () ( arg a )</font></div><div class=
=3D""><font face=3D"Courier" class=3D""> =
{ return g( next( a ) ); }</font></div><div class=3D""><font face=3D=
"Courier" class=3D""> };</font></div><div class=3D""><font fac=
e=3D"Courier" class=3D""> </font><span style=3D"font-fami=
ly: Courier;" class=3D"">std::function< ret( arg ) > cf</span></div><=
div class=3D""><font face=3D"Courier" class=3D"">  =
; =3D </font><span style=3D"font-family: Courier;" class=3D"">composer=
</span><font face=3D"Courier" class=3D"">{ </font><span style=3D"font-=
family: Courier;" class=3D"">std::forward< G >( g )</span><font face=
=3D"Courier" class=3D""> };</font></div><div class=3D""><span style=3D=
"font-family: Courier;" class=3D""> cf.target< composer >=
;()->next =3D std::move( f );</span></div><div class=3D""><span style=3D=
"font-family: Courier;" class=3D""> 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< typename ret, typename ... arg, typename comp ></font></d=
iv><div class=3D""><font face=3D"Courier" class=3D"">void compose( std::fun=
ction< ret( arg ... ) > & f, comp && 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< ret( arg ) > 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 & 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> 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 the idiom 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" 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--
.