Throwing destructor compilable by C++98 and C++1x. Is there a better way?

221 views Asked by At

Is there a clean way or workaround to achieve a throwing destructor compilable by both c++98 and c++1x compilers which is cross-platform and does not produce warnings?

Given

There is a legacy class representing an error code, which throws if it is not handled. To achieve this it uses a throwing destructor.

Goal

A cross-platform (g++, VC++) implementation that compiles and works with both older c++98 compilers (thank you MS for the WindowsCE) as well as with compilers supporting c++1x. Ideally, it should not produce any warnings.

Problem

The problem is that since c++11 all destructors are non-throwing by default (will terminate at run-time if an exception is thrown). Workaround is noexcept(false), but it is not available in c++98. On the other hand c++98 has exception specifications, which are generally considered bad and are deprecated since c++11 E.g., see: Why are exception specifications bad?.

Current workaround

class ThrowingErrorCode
{
public:
    ...
// silence the VC exception specification warning
// we need to use exception specification for a throwing destructor, so that it would compile with both c++98 and c++11
#ifdef _MSC_VER
#pragma warning( push )
#pragma warning( disable : 4290 )
#endif

   ~ThrowingErrorCode() throw(ErrorCodeException)
   {
      ...
      throw ErrorCodeException("Must handle error code");
   }

// enable the warning again
#ifdef _MSC_VER
#pragma warning( pop )
#endif   
};

I don't quite like it, as it needs to suppress warnings and uses deprecated exception specification. Could you please tell me if there a better/cleaner way of doing this?


This is my first StackOverflow question. If you have any advice on improving the question, please let me know :-)

2

There are 2 answers

3
Kerrek SB On BEST ANSWER

You could define a simple macro to effect a throwing exception specification:

#if __cplusplus >= 201103L
#  define MAY_THROW noexcept(false)
#else
#  define MAY_THROW
#endif

Usage:

struct X
{
    ~X() MAY_THROW { /* ... */ }
    //   ^^^^^^^^^
};

Non-conforming compilers may not set the __cplusplus macro correctly, in which case you may need to compare against additional vendor macros. For example, some versions of MSVC++ can use this code:

#if __cplusplus >= 201103L
#  define MAY_THROW noexcept(false)
#elif defined(_MSC_VER) && _MSC_VER >= 1800
#  define MAY_THROW noexcept(false)
#else
#  define MAY_THROW
#endif
1
AMA On

I ended up using the approach suggested in this answer. It will still produce a warning when c++03 is used with gcc (see here). Since I don't use this configuration, it will do.

I used boost's BOOST_NO_NOEXCEPT macro for the cross-platform check of noexcept support. Here's how it looks:

#include <boost/detail/workaround.hpp>

#if defined BOOST_NO_NOEXCEPT
#  define MAY_THROW
#else
#  define MAY_THROW noexcept(false)
#endif