Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How is std::is_empty<T> implemented in VS2015 (or any compiler)?

My current question has been inspired by attempting to understand how std::unique_ptr<T, D> utilizes template mechanics to instantiate a template class the size of a T* when D (the deleter type) is a lambda function type, but a larger size when D is a function pointer type (since space needs to be allocated in the unique_ptr instance to store the function pointer).

Looking through the VS2015 source code, I find that std::unique_ptr derives from std::_Unique_ptr_base, which in turn declares a data member of type _Compressed_pair<class _Ty1, class _Ty2, bool = is_empty<_Ty1>::value && !is_final<_Ty1>::value>. The type _Ty1 in this latter context is the type of the deleter, D, that is the second unique_ptr template parameter noted in the previous paragraph; i.e., the motivation behind this question is that I am contrasting _Ty1 being a lambda type, vs. _Ty1 being a function pointer type. (In fact, the default value of the bool is being utilized.)

I recognize that is_empty<_Ty1>::value is true when _Ty1 is an instance of a lambda type (when the lambda has no capture variables and therefore has a 0 size); but that it is false when _Ty1 is a function pointer type.

This led me to pursue how std::is_empty is defined.

Ugh!

What follows is the complete implementation of std::is_empty that I can find in the VS2015 C++ library source code.

In the file type_traits is this:

// TEMPLATE CLASS is_empty
template<class _Ty>
struct is_empty _IS_EMPTY(_Ty)
{   // determine whether _Ty is an empty class
};

... and the macro _IS_EMPTY is defined in the same file:

#define _IS_EMPTY(_Ty)  \
: _Cat_base<__is_empty(_Ty)>

... and at this point my luck runs dry, because I cannot find the definition of __is_empty anywhere. I have GREPped through the entire VS2015 installation directory (which includes, I think, all of the C++ library source code, in - though perhaps I'm mistaken).

I like to understand C++ internals when I want to. But ... I'm stuck on this one, and plenty of googling did not reveal the answer (though I have seen reference to intrinsics), and my digging has not ... discovered any source code.

Can someone enlighten this situation? How is std::is_empty<T> actually implemented in VS2015, or, for that matter, any other compiler?

like image 557
Dan Nissenbaum Avatar asked Feb 21 '16 01:02

Dan Nissenbaum


1 Answers

Looks as if MSVC++ provides an intrinsic __isempty(T) rather than dealing with a library-level implementation. Since the argument type T passed to std::is_empty<T> can be final, I don't think there can be a safe library implementation and compiler help may be needed.

The only way to determine if a type T is empty in a library I can think of is this (the specialization deals with non-class types for which std::is_empty<T> is not true):

template <bool, typename T>
struct is_empty_aux: T { unsigned long long dummy; };
template <typename T>
struct is_empty_aux<false, T> { unsigned long long dummy[2]; };

template <typename T>
struct is_empty:
    std::integral_constant<bool,
                           sizeof(is_empty_aux<std::is_class<T>::value, T>)
                           == sizeof(unsigned long long)> {
};

However, if T is final the inheritance in is_empty_aux is illegal. While the case of a final class can be detected using std::is_final<T> I don't see a way to determine whether its objects are empty. Thus, using a compiler intrinsic may be necessary. Compiler intrinsics are certainly necessary for some of the other type traits anyway. Compiler intrinsics are declarations/definitions somehow magically provided by the compiler: they are normally neither explicitly declared nor defined. The compiler has the necessary knowledge about types and exposing this knowledge via intrinsics is a reasonable approach.

like image 89
Dietmar Kühl Avatar answered Sep 27 '22 21:09

Dietmar Kühl