PATCH: using mixed types in MIN2/MAX2 functions

Erik Österlund erik.osterlund at lnu.se
Mon Jul 28 22:43:34 UTC 2014


Hi guys,

Thought I'd put my general opinion on templates vs macros in here.
I personally do not get the fear of "non-trivial" templates to complicate things, while instead embracing macros as friends.

I would on the contrary argue that the macros many times complicate things a lot more than templates would.
Apart from providing type safety to such small macro functions, they can also get rid of the immensely ugly macros, e.g. for doing full GC, and specialized oop closures.

The  specialized closures span multiple files, multiple macros with different mechanics for compiler compatibility taking into account having too large macros.
I do not know in which universe having such macros for closure specialization and logic for doing full GC (hundreds of LOC per macro) is less painful nor complicated compared to templates. As a matter of fact, for the sake of it, I made a similar solution for closure specialization using templates instead of macros to remove virtual calls, and it is a lot cleaner.

I strongly believe that by considering templates friends, people will become a lot happier. Rather than complicating the code, I am convinced it will both simplify it and make it safer.

+1 vote for non-trivial templates!

/Erik

On 28 Jul 2014, at 23:56, Jesper Wilhelmsson <jesper.wilhelmsson at oracle.com> wrote:

> Hi Dan, Kim, and others,
> 
> Trying to get this discussion going again.
> 
> There isn't too much of non-trivial template usage in HotSpot today and I'm not sure I think it's worth complicating the code to avoid a few type casts.
> 
> How do other people feel about the non-trivial template usage?
> /Jesper
> 
> 
> Kim Barrett skrev 20/6/14 20:03:
>> On Jun 19, 2014, at 9:06 AM, Dan Horák <dan at danny.cz> wrote:
>>> 
>>> On Thu, 19 Jun 2014 14:55:52 +0200
>>> Bengt Rutisson <bengt.rutisson at oracle.com> wrote:
>>>> Can you explain more in detail why it is not possible to specialize
>>>> the MIN2 and MAX2 functions? You are probably correct, because when I
>>>> read the comment in the code it says:
>>>> 
>>>> 
>>>> // It is necessary to use templates here. Having normal overloaded
>>>> // functions does not work because it is necessary to provide both 32-
>>>> // and 64-bit overloaded functions, which does not work, and having
>>>> // explicitly-typed versions of these routines (i.e., MAX2I, MAX2L)
>>>> // will be even more error-prone than macros.
>>>> template<class T> inline T MAX2(T a, T b)           { return (a >
>>>> b) ? a : b; }
>>>> 
>>>> 
>>>> This kind of says what you also said. That it is not possible, but it
>>>> does not really explain why.
>>>> 
>>>> Can you explain why we can have definitions like:
>>>> 
>>>> inline uint MAX2(uint a, size_t b)
>>>> inline uint MAX2(size_t a, uint b)
>>> 
>>> if I remember correctly from my previous experience, these 2 definition
>>> conflict with the existing
>>> inline uint MAX2(uint a, uint b)
>>> on platforms where size_t == uint. But I might be wrong, the easiest we
>>> can do, is to try add them. Or we could add the new definitions only
>>> for s390 with #if defined(__s390__) && ! defined(__s390x__). Or maybe
>>> there is another way to add them only when size_t != uint. A C++ expert
>>> is required :-)
>> 
>> This isn’t too difficult.  The tests at the end should of course be turned into real test cases.
>> 
>> Requires
>> 
>> -  <limits>
>>   - std::numeric_limits<T>::is_specialized
>>   - for specialized types
>>     - std::numeric_limits<T>::is_integer
>>     - std::numeric_limits<T>::is_signed
>> - partial template specialization
>> - SFINAE for function return types
>> 
>> I have no idea whether all of our toolchains support all that.  I’ve heard of some
>> strange defects around numeric_limits with some (older) toolchains.  For example,
>> boost provides
>>   BOOST_NO_LIMIT - does not provide <limits>
>>   BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
>>     std::numeric_limits<T>::is_signed and similar are not available at compile-time.
>> Neither of those seem to be applicable to any toolchain relevant to jdk code though.
>> 
>> I also don't know how folks feel about non-trivial template usage.
>> 
>> ———————
>> 
>> #include <limits>
>> 
>> // MIN2/MAX2(a, b) compare a and b and return the lesser/greater.
>> //
>> // a and b must either
>> //
>> // - be of the same type, in which case the result is of that type, or
>> //
>> // - both be of integer types with the same signed-ness, in which case the
>> // result is the larger of those two types.
>> 
>> template<typename T, typename U, bool T_Smaller = (sizeof(T) < sizeof(U))>
>> struct MINMAX2_result_differ_choose { typedef U type; };
>> 
>> template<typename T, typename U>
>> struct MINMAX2_result_differ_choose<T, U, false> { typedef T type; };
>> 
>> template<typename T, typename U,
>>          bool T_Integer = std::numeric_limits<T>::is_specialized,
>>          bool U_Integer = std::numeric_limits<U>::is_specialized>
>> struct MINMAX2_result_differ { };
>> 
>> template<typename T, typename U,
>>          bool T_Integer = std::numeric_limits<T>::is_integer,
>>          bool U_Integer = std::numeric_limits<U>::is_integer,
>>          bool SameSigned = (std::numeric_limits<T>::is_signed
>>                             == std::numeric_limits<U>::is_signed)>
>> struct MINMAX2_result_differ_aux { };
>> 
>> template<typename T, typename U>
>> struct MINMAX2_result_differ_aux<T, U, true, true, true>
>>   : public MINMAX2_result_differ_choose<T, U>
>> { };
>> 
>> template<typename T, typename U>
>> struct MINMAX2_result_differ<T, U, true, true>
>>   : public MINMAX2_result_differ_aux<T, U>
>> { };
>> 
>> template<typename T, typename U>
>> struct MINMAX2_result_type
>>   : public MINMAX2_result_differ<T, U>
>> { };
>> 
>> template<typename T>
>> struct MINMAX2_result_type<T, T> {
>>   typedef T type;
>> };
>> 
>> template<typename T, typename U>
>> inline typename MINMAX2_result_type<T, U>::type MAX2(T a, U b) {
>>   // note: if T & U are different integral types, terniary operator will
>>   // perform implicit promotion of the smaller to the larger.
>>   return a > b ? a : b;
>> }
>> 
>> template<typename T, typename U>
>> inline typename MINMAX2_result_type<T, U>::type MIN2(T a, U b) {
>>   // note: if T & U are different integral types, terniary operator will
>>   // perform implicit promotion of the smaller to the larger.
>>   return a < b ? a : b;
>> }
>> 
>> // TESTS
>> 
>> typedef unsigned int uint;
>> typedef unsigned int uint_size_t;
>> typedef unsigned long ulong_size_t;
>> 
>> uint max_same1(uint a, uint_size_t b) { return MAX2(a, b); }
>> uint max_same2(uint_size_t a, uint b) { return MAX2(a, b); }
>> 
>> uint min_same1(uint a, uint_size_t b) { return MIN2(a, b); }
>> uint min_same2(uint_size_t a, uint b) { return MIN2(a, b); }
>> 
>> ulong_size_t max_diff1(uint a, ulong_size_t b) { return MAX2(a, b); }
>> ulong_size_t max_diff2(ulong_size_t a, uint b) { return MAX2(a, b); }
>> 
>> ulong_size_t min_diff1(uint a, ulong_size_t b) { return MIN2(a, b); }
>> ulong_size_t min_diff2(ulong_size_t a, uint b) { return MIN2(a, b); }
>> 
>> ulong_size_t max_ulong_size_t(ulong_size_t a, ulong_size_t b) {
>>   return MAX2(a, b);
>> }
>> 
>> uint_size_t max_uing_size_t(uint_size_t a, uint_size_t b) {
>>   return MAX2(a, b);
>> }
>> 
>> ulong_size_t min_ulong_size_t(ulong_size_t a, ulong_size_t b) {
>>   return MIN2(a, b);
>> }
>> 
>> uint_size_t min_uing_size_t(uint_size_t a, uint_size_t b) {
>>   return MIN2(a, b);
>> }
>> 
>> // these aren't supposed to compile
>> 
>> // float max_float1(uint a, float b) { return MAX2(a, b); }
>> // float max_float2(float a, uint b) { return MAX2(a, b); }
>> 
>> // float min_float1(uint a, float b) { return MIN2(a, b); }
>> // float min_float2(float a, uint b) { return MIN2(a, b); }
>> 
>> // uint max_int1(uint a, int b) { return MAX2(a, b); }
>> // uint max_int2(int a, uint b) { return MAX2(a, b); }
>> 
>> // uint min_int1(uint a, int b) { return MIN2(a, b); }
>> // uint min_int2(int a, uint b) { return MIN2(a, b); }
>> 
>> // uint max_long1(uint a, long b) { return MAX2(a, b); }
>> // uint max_long2(long a, uint b) { return MAX2(a, b); }
>> 
>> // uint min_long1(uint a, long b) { return MIN2(a, b); }
>> // uint min_long2(long a, uint b) { return MIN2(a, b); }
>> 



More information about the hotspot-dev mailing list