RFR (M): 8183927: Hotspot needs C++ type_traits metaprogramming utilities

Erik Österlund erik.osterlund at oracle.com
Thu Jul 6 13:08:23 UTC 2017


Hi,

I'm sure we have all had moments when we wished we could use the C++11 
type_traits header and the nice metaprogramming tools in there, but then 
suddenly remembered that we can not, because we are building libjvm.so 
with C++03 and are still arguing whether we can use namespaces.
Well.. welcome to the future. I am delighted to announce that now at 
least some of those tools will become available.

A bunch of C++11 compliant metaprogramming tools that can be used 
already in C++03 have been developed.
They have been developed with the most conservative and portable C++03 
code possible so that even exotic compilers can support it.
Special thanks go to Kim Barret whom I have had many long conversations 
about many paragraphs of the C++ standard throughout this work. It has 
helped a lot and improved the code.

While this change introduces the traits without using it in the code 
(more than from gtest), it will become used in already prepared patches 
for refactoring Atomic and OrderAccess to use templates rather than hand 
picked types like the JNI-specific jint etc types. That serves as the 
bottom layer of the upcoming GC barrier interface that also requires 
these utilities. Since they are rather large changes, I hope it is okay 
that I separate out the metaprogramming utilities. I argue that they are 
of value in general in hotspot and will make our lives easier.

All metaprogramming tools have been placed in a new 
hotspot/src/share/vm/metaprogramming directory.
Tests have been written and placed in a new test/native/metaprogramming 
directory.

A bug ID can be found here:
https://bugs.openjdk.java.net/browse/JDK-8183927

A webrev with code can be found here:
http://cr.openjdk.java.net/~eosterlund/8183927/webrev.00/

Here is a list of metaprogramming tools that have been introduced with a 
corresponding short explanation:

============================

template<typename T, T v>
struct IntegralConstant

This metafunction takes as parameters a type and a value.
The type and value can later be queried from the value_type and value 
members respectively.

Here is an example usage:
typedef IntegralConstant<bool, true> TrueType;

Now TrueType::value_type is the bool type, and TrueType::value is the 
true boolean value.

Many other metafunctions will later inherit from either TrueType or 
FalseType to e.g. denote the metafunction returning true or false based 
on some condition.

The following traits that start with Is* belong to this category. They 
manifest a constant evaluated at compile time that checks some condition 
of types.

============================

template <typename X, typename Y>
struct IsSame

This metafunction takes as parameters two types, X and Y, and returns 
true iff they are the same type. Otherwise it returns false.

============================

template <typename T>
struct IsConst

This metafunction takes as parameter a type T, and returns true iff T 
has a const qualifier in it.

============================

template <typename T>
struct IsVolatile

This metafunction takes as parameter a type T, and returns true iff T 
has a volatile qualifier in it.

============================

template<typename T>
struct IsSigned

This metafunction takes as parameter a type T, and checks if it is 
signed. This is equivalent to asking std::numeric_limits<>::is_signed, 
but additionally disregards CV qualifiers.

============================

template<typename T>
struct IsIntegral

This metafunction takes as parameter a type T, and returns true if it is 
an integral type. This is similar to asking 
std::numeric_limits<>::is_integer, but additionally disregards CV 
qualifiers. Note that this returns false for enums.

============================

template<typename T>
struct IsSignedIntegral

template<typename T>
struct IsUnsignedIntegral

These two metafunctions are helpers that return true for integral types 
that have corresponding signedness.

============================

template <typename T>
struct IsFloatingPoint

This metafunction takes as parameter a type T, and returns true iff T is 
a floating point type (irrespective of CV qualifiers).

============================

template <typename T>
class IsPointer

This metafunction takes as parameter a type T, and returns true iff T is 
a pointer type (irrespective of CV qualifiers).

============================

That is it as far as metafunctions returning booleans go in these 
changes. So now we move on to metafunctions that make use of these 
boolean conditions.

template <bool condition, typename TrueType, typename FalseType>
struct Conditional

This metafunction constitutes a conditional type, controlled by the 
condition passed in. Its type evaluates to TrueType iff the condition is 
true, and otherwise evaluates to FalseType.

Here is a contrived toy example:

template<T>
void example(T base) {
   typedef Conditional<IsPointer<T>::value, ptrdiff_t, T> ScaleType;
   ScaleType offset = offset();
   do_something_clever(base + offset);
}

============================

In this example, the type of the offset is ptrdiff_t iff T is a pointer 
and otherwise it is T. This allows passing in pointers and integers to 
the function, and adding offsets into it.

template <bool B, typename T = void>
struct EnableIf

This metafunction is the key to SFINAE (Substitution Failure Is Not An 
Error), a C++ metaprogramming idiom that allows explicitly allowing or 
disallowing function overloads based on precise conditions for types 
passed in as parameter.
The EnableIf overload guard should replace the return value of a 
function. The type T is the type that is intended to be returned if the 
overload is allowed based on the passed in condition. By default that is 
void.

Here is a contrived toy example usage:

template <typename T>
EnableIf<IsIntegral<T>::value> print(T x) {}

template <typename T>
EnableIf<!IsIntegral<T>::value> print(T x) {}

...

print(1); // calls the first overload enabled for integral types
print("hello SFINAE world"); // call the second overload enabled for 
non-integral types

============================

Now we move on to metafunctions that remove properties like e.g. CV 
qualifiers, pointers and references from types, resulting in a new type.

template <typename T>
struct RemovePointer

This metafunction takes as parameter a type T, and returns the type 
under the pointer iff T is a pointer, and otherwise returns the same type T.

============================

template <typename T>
struct RemoveReference

This metafunction takes as parameter a type T, and returns the type 
under the reference iff T is a pointer, and otherwise returns the same 
type T.

============================

template <typename T>
struct RemoveCV

This metafunction takes as parameter a type T, and returns the type T 
without CV qualifiers.

============================

template <typename T>
struct Decay

This metafunction takes as parameter a type T, and returns the CV 
unqualified type under the reference iff T is a reference type, and 
otherwise returns the CV unqualified type T.

============================

Testing: A bunch of tests have been added to the 
test/native/metaprogramming folder for all traits (except 
IntegralConstant, because there is not a whole lot to test there, and it 
is used by all condition metafunctions in their corresponding tests). 
They have gone through JPRT, and work well on all oracle supported 
platforms, and is also known to have recently built with the xlC 
compiler on AIX. I would like to ask external maintainers though to 
please verify that this works on your compilers. I have done my best to 
use the most conservative tricks in the book to make it as portable as 
possible, and I hope it will work well for us all.

Thanks,
/Erik


More information about the hotspot-dev mailing list