Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"has_trivial_destructor" defined instead of "is_trivially_destructible"

During the refinement process of the C++11 standard, it seems that is_trivially_destructible was considered a better/more-consistent name than has_trivial_destructor.

This is a relatively recent development, as my g++ 4.7.1 still uses the old name, and it's been fixed to be compliant with the standard as of 4.8:

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52702

I've been lazily using an #if which favors the compiler I'm on:

#if TRIVIAL_DESTRUCTOR_TYPE_TRAIT_MATCHES_STANDARD
template<class T>
using is_trivially_destructible = std::is_trivially_destructible<T>;
#else
template<class T>
using is_trivially_destructible = std::has_trivial_destructor<T>;
#endif

...but now I'm trying to share the source with 4.8 users and other compilers chasing the standard. Is there any better trick for making the detection of the situation more "automatic" and not require a #define?

like image 669
HostileFork says dont trust SE Avatar asked Oct 03 '12 04:10

HostileFork says dont trust SE


2 Answers

This works for me with GCC 4.7 and 4.8, correctly telling me whether the old or new trait is provided:

#include <type_traits>

namespace std
{
  template<typename> struct has_trivial_destructor;
  template<typename> struct is_trivially_destructible;
}

template<typename T>
  class have_cxx11_trait_helper
  {
    template<typename T2, bool = std::is_trivially_destructible<T2>::type::value>
      static std::true_type test(int);

    template<typename T2, bool = std::has_trivial_destructor<T2>::type::value>
      static std::false_type test(...);

  public:
    typedef decltype(test<T>(0)) type;
  };

template<typename T>
  struct have_cxx11_trait : have_cxx11_trait_helper<T>::type
  { };

int main()
{
  static_assert( have_cxx11_trait<int>::value, "new trait" );
}

N.B. I declare (but don't define) both traits because the standard library (probably) won't declare both and if the name isn't even declared then you can't refer to std::is_trivially_destructible. So I declare them both, but only the one defined by the library will be usable. Adding declarations to namespace std is technically undefined behaviour, so use it at your own risk (it's not likely to wipe your hard drive in this case though.)

Unfortunately an older compiler that doesn't provide the new trait might not be able to handle the code either -- I haven't checked if it works with GCC 4.6

Now you can define your own portable trait:

template<typename T>
  using is_trivially_destructible
    = typename std::conditional<have_cxx11_trait<T>::value,
                                std::is_trivially_destructible<T>,
                                std::has_trivial_destructor<T>>::type;

The semantics of has_trivial_destructor aren't the same as the new trait, but it's a reasonable approximation for older compilers that don't support the new trait.

Alternatively, you could use static polymoprhism to get different code depending on which type trait is available, e.g. by specializing templates or by overloading and tag dispatching, like so:

template<typename T>
  void foo_helper(const T&, std::true_type)
  {
    // code that uses std::is_trivially_destructible
  }

template<typename T>
  void foo_helper(const T&, std::false_type)
  {
    // different code using std::has_trivial_destructor
  }

template<typename T>
  void foo(const T& t)
  {
    // do common stuff

    // stuff that depends on trait
    foo_helper(t, has_cxx11_trait<T>{});

    // more common stuff
  }

No macros were harmed in the making of this answer.

like image 88
Jonathan Wakely Avatar answered Nov 15 '22 06:11

Jonathan Wakely


Here's a very hackish and officially UB snippet that can test whether the std namespace sports the has_trivial_destructor name and has a trivially_destructible trait alias pick up the right trait to check (is_trivially_destructible in the case has_trivial_destructor is not available).

#include <type_traits>

template<class>
struct has_trivial_destructor{ using test_fail = int; };

template<class>
struct is_trivially_destructible{ using test_fail = int; };

// very hackish and officially UB
namespace std{
  template<class T>
  struct inherit_htd : has_trivial_destructor<T>{};
  template<class T>
  struct inherit_itd : is_trivially_destructible<T>{};
}

namespace check_htd{
  template<class T>
  struct sfinae_false : ::std::false_type{};

  template<class T>
  auto test(int) -> sfinae_false<typename ::std::inherit_htd<T>::test_fail>;
  template<class>
  auto test(...) -> ::std::true_type;

  struct htd_available : decltype(test<int>(0)){};
}

template<class T>
using Apply = typename T::type;

template<class C, class T, class F>
using If = Apply<std::conditional<C::value,T,F>>;

template<class T>
using trivially_destructible = If<check_htd::htd_available, std::inherit_htd<T>, std::inherit_itd<T>>;

Live example.

like image 36
Xeo Avatar answered Nov 15 '22 06:11

Xeo