Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Forcing non-throwing arguments

48 views
Skip to first unread message

Dmitry Potapov

unread,
Feb 18, 2012, 4:55:45 PM2/18/12
to
Disclaimer: I suspect the trick described below is obvious, but I failed
to find any posts in this group which is referring to it, so I just
leave it here.

There was a big problem in C++03, when accepting template argument, you
know nothing about exceptions which can occur while you're using this
argument. Sometimes, this can lead to additional logic and performance
penalty.

As for me, I don't care about exceptions type, it is the caller who
should handle them. My job is to prevent resource leaks and I need to
know if execution sequence can be interrupted by exception.

In C++11 there is noexcept operator, which allows to determine if some
expression can throw an exception. From this, it is become possible to
provide two function implementations, one for exception throwing
argument, and second one with noexcept exception specification.

For example, consider class C with template c'tor which allocates some
resources and then calls member function f() of the argument. It is a
good practice to wrap resources with unique_ptr or something similar in
order to avoid resource leak, but this introduces slight overhead for
non-throwing arguments, which can be unacceptable in
performance-critical applications. So, public c'tor accepting argument
can delegate its work to one of the overloaded c'tors which accepts
std::true_type and std::false_type accordingly:

#include <type_traits>
#include <iostream>

struct A {
void f() {}
};

struct B {
void f() noexcept {}
};

class C {
template <class T>
C(T t, std::true_type) noexcept
{
t.f();
std::cout << "here we can use straight and simple logic here as no"
" exceptions are possible"<< std::endl;
}

template <class T>
C(T t, std::false_type)
try
{
// resources allocation here
// ...
t.f();
std::cout << "here we should use a bit more complicated logic
to avoid"
" resource and memory leaks" << std::endl;
}
catch (...)
{
std::cerr << "here we must free resources acquired" << std::endl;
}

public:
template <class T>
C(T t) noexcept(noexcept(t.f()))
: C(t, std::integral_constant<bool, noexcept(t.f())>())
{
}
};

int main()
{
A a;
B b;
C c(a);
C c2(b);
}

In the example above, we can delete c'tor overloading with false_type in
order to forbid throwing arguments.

Fly in the ointment:
Thoughts about such trick came to me when I worked on class which
accepts functor, which can be just a std::plus. Unfortunately helper
functors declared as:

T operator()(const T& x, const T& y) const;

Not as:

T operator()(const T& x, const T& y) const noexcept(x+y);

So, this reduces the area where this trick is applicable.


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Daniel Krügler

unread,
Feb 22, 2012, 11:06:37 AM2/22/12
to
Am 18.02.2012 22:55, schrieb Dmitry Potapov:
> Disclaimer: I suspect the trick described below is obvious, but I failed
> to find any posts in this group which is referring to it, so I just
> leave it here.
>
> There was a big problem in C++03, when accepting template argument, you
> know nothing about exceptions which can occur while you're using this
> argument. Sometimes, this can lead to additional logic and performance
> penalty.
>
> As for me, I don't care about exceptions type, it is the caller who
> should handle them. My job is to prevent resource leaks and I need to
> know if execution sequence can be interrupted by exception.
>
> In C++11 there is noexcept operator, which allows to determine if some
> expression can throw an exception. From this, it is become possible to
> provide two function implementations, one for exception throwing
> argument, and second one with noexcept exception specification.
>
> For example, consider class C with template c'tor which allocates some
> resources and then calls member function f() of the argument. It is a
> good practice to wrap resources with unique_ptr or something similar in
> order to avoid resource leak, but this introduces slight overhead for
> non-throwing arguments, which can be unacceptable in
> performance-critical applications. So, public c'tor accepting argument
> can delegate its work to one of the overloaded c'tors which accepts
> std::true_type and std::false_type accordingly:

I agree that this is a nice idiom. A very minimalistic form of this kind
of exception-based flow control is part of the standard library:
move_if_noexcept (In this case it does not perform different actions,
but it has been added to take advantage of such "exception branching").

> #include<type_traits>
> #include<iostream>
>
> struct A {
> void f() {}
> };
>
> struct B {
> void f() noexcept {}
> };
>
> class C {
> template<class T>
> C(T t, std::true_type) noexcept
> {
> t.f();
> std::cout<< "here we can use straight and simple logic here as
no"
> " exceptions are possible"<< std::endl;
> }

IO in noexcept code can be problematic, but except from that the idiom
is nice.

> template<class T>
> C(T t, std::false_type)
> try
> {
> // resources allocation here
> // ...
> t.f();
> std::cout<< "here we should use a bit more complicated logic
> to avoid"
> " resource and memory leaks"<< std::endl;
> }
> catch (...)
> {
> std::cerr<< "here we must free resources acquired"<<
std::endl;
> }
>
> public:
> template<class T>
> C(T t) noexcept(noexcept(t.f()))
> : C(t, std::integral_constant<bool, noexcept(t.f())>())
> {
> }
> };

Let me just remark that this code misses to take into account that the
copy-constructor (or more precisely: The constructor that would be used
to make a copy) of T might throw an exception: T is transferred by value
to the delegating constructor here.

HTH & Greetings from Bremen,

Daniel Krügler

Marc

unread,
Feb 22, 2012, 11:09:47 AM2/22/12
to
Dmitry Potapov wrote:

> There was a big problem in C++03, when accepting template argument, you
> know nothing about exceptions which can occur while you're using this
> argument. Sometimes, this can lead to additional logic and performance
> penalty.
>
> As for me, I don't care about exceptions type, it is the caller who
> should handle them. My job is to prevent resource leaks and I need to
> know if execution sequence can be interrupted by exception.
>
> In C++11 there is noexcept operator, which allows to determine if some
> expression can throw an exception. From this, it is become possible to
> provide two function implementations, one for exception throwing
> argument, and second one with noexcept exception specification.

Yes, that's part of the goal. std::vector is one typical user.

> For example, consider class C with template c'tor which allocates some
> resources and then calls member function f() of the argument. It is a
> good practice to wrap resources with unique_ptr or something similar in
> order to avoid resource leak, but this introduces slight overhead for
> non-throwing arguments, which can be unacceptable in
> performance-critical applications.

Does it? In most cases it makes no difference at all.
Except for the fact that iostreams may throw, a good compiler can transform:
try { call_nothrow_function(); } catch...
into just:
call_nothrow_function();

> In the example above, we can delete c'tor overloading with false_type in
> order to forbid throwing arguments.

Yes, although I think I'd rather use sfinae to remove the constructor:

template <class T,class=typename
std::enable_if<noexcept(std::declval<T&>().f())>::type>
C(T t) noexcept;

> Fly in the ointment:
> Thoughts about such trick came to me when I worked on class which
> accepts functor, which can be just a std::plus. Unfortunately helper
> functors declared as:
>
> T operator()(const T& x, const T& y) const;
>
> Not as:
>
> T operator()(const T& x, const T& y) const noexcept(x+y);

you want noexcept(noexcept(x+y)) here.

> So, this reduces the area where this trick is applicable.

Yes, the C++11 standard was quite conservative in marking things
constexpr and/or noexcept. Your library vendor is allowed to add those
noexcept, but you won't be able to rely on it.

Nevin :-] Liber

unread,
Feb 22, 2012, 11:10:39 AM2/22/12
to
In article
<15615443.57.1329560139436.JavaMail.geo-discussion-forums@ynnk21>,
Dmitry Potapov <pota...@gmail.com> wrote:

> It is a
> good practice to wrap resources with unique_ptr or something similar in
> order to avoid resource leak, but this introduces slight overhead for
> non-throwing arguments, which can be unacceptable in
> performance-critical applications.

What have you measured this penalty to be, on what compilers and what
optimization settings? I'd be very weary of using such a compiler for
performance-critical applications (as this isn't a difficult case to
optimize).

--
Nevin ":-)" Liber <mailto:ne...@eviloverlord.com> 773 961-1620
0 new messages