RFR: 8251274: Provide utility types for function SFINAE using extra template parameters
Kim Barrett
kim.barrett at oracle.com
Fri Aug 28 19:46:44 UTC 2020
> On Aug 28, 2020, at 3:44 AM, Erik Österlund <erik.osterlund at oracle.com> wrote:
>
> Hi Kim,
>
> On 2020-08-27 22:47, Kim Barrett wrote:
>> That would work, as would a macro that takes a type. That is, both of
>> these work.
>>
>> #define ENABLE_IF_T(...) EnableIfT<__VA_ARGS__> = 0
>> #define ENABLE_IF_X(...) EnableIfX<__VA_ARGS__> = 0
>>
>> I didn't propose either of those because adding a macro just to eliminate
>> the trailing " = 0" doesn't seem worthwhile.
>
> I see. Yeah I can see how that might not feel worthwhile. Although in a way,
> I guess the macro would hide the implementation details of the enable if mechanism,
> that otherwise leak out to the enable if user.
>
>> I proposed both EnableIfT and EnableIfX because I don't think either alone
>> is worthwhile, though for different reasons. (Assume that if we only had one
>> that the T/X suffix would eventually be eliminated.) Both of them are
>> basically about reducing syntactic noise.
>>
>> EnableIfT alone doesn't deal well with expressions, […]
>>
>> EnableIfX alone doesn't, in my opinion, carry its weight; just use
>> std::enable_if_t directly. […]
>>
>> I considered not proposing them at all. While I think together they provide
>> enough to be worth having, I also think that's not blatantly clear and
>> obvious. A single macro supporting both is much more compelling. If both
>> were removed, I'm not sure what to do with EnableIfAnd/Or; retain in the
>> proposal or retract the proposal entirely.
>
> I once again understand that having just an alias type for std::enable_if_t
> that takes an int might not feel worth while.
>
> I can't believe I'm about to say this though, but what if you combine the
> two not worthwhile things into that macro... it just might become worthwhile
> combined (yes I think I am in favour of the macro while you are not - I don't
> know what is going on either!).
What have you done with the real Erik?
> So if we don't do anything, the template parameter SFINAE syntax would seemingly be:
>
> template<typename T, std::enable_if_t<std::is_integral<T>::value, int> = 0>
> void foo (T t) { ... }
>
> And if we have an ENABLE_IF macro that takes values only (keeping to the convention
> we have to work with values only as discussed previously), it would be:
>
> template<typename T, ENABLE_IF(std::is_integral<T>::value)>
> void foo (T t) { ... }
>
> Here, I think I would actually prefer using the macro that hides the implementation
> details how it works from the user, and in the API just focuses on only passing in
> the right condition. Also, not importantly, the identifier ENABLE_IF is not taken
> (while EnableIf is). That macro can also have some comments describing the trick
> at one point, which I think is quite useful on its own.
>
> I guess the macro itself would look like this:
> #define ENABLE_IF(...) std::enable_if_t<__VA_ARGS__ , int> = 0
The macro needs to be
#define ENABLE_IF(...) std::enable_if_t<(__VA_ARGS__), int> = 0
Otherwise one gets a mess from `ENABLE_IF(sizeof(T) > 4)` for example; the
angle bracket closes the template parameter list instead of being a greater
than operator.
> So this value SFINAE only macro for doing SFINAE using a template parameter seems like
> something that we can actually do without relying on any compiler bugs being fixed or
> refactoring the world in HotSpot (yet). And I think it is more readable than the return
> type SFINAE we have today (that requires extra lines for the return type, and you have
> to scratch your head to figure out what the actual return type is). I would say that
> such a value SFINAE macro is worthwhile doing, IMO.
This is the main point of all this, of course. As you know, I really dislike
the C++98 function template SFINAE syntax. So much so that I'd rather
delegate to class templates, as was done with the Atomic::*Impl classes. The
new style enabled by this C++11 feature changes everything. So I'm highly
motivated to reach some consensus on how we're going to use this new
feature.
I think that macro is okay. I would be even happier with it if we had
variable templates for the type traits, a la C++17, so
std::is_integral<T>::value => std::is_integral_v<T>
We could add a suite of IsIntegralV &etc variable templates. Alternatively,
I wonder how much trouble we might be taking on if we conditionally defined
them with something like
#if __cplusplus == 201402L
namespace std {
template<typename T> constexpr bool is_integral_v = std::is_integral<T>::value;
...
}
#endif // __cplusplus
(Note that we can't get *exactly* the behavior of C++17 for these, since
inline variables are a C++17 language feature. The difference is linkage,
but I don't expect anyone to care whether `&IsIntegralV<int>` in different
translation units result in the same address.)
> I tried it in my usecase I mentioned, and I think it looks pretty good. Feels like
> having a nice comfortable sofa in the sewers. I think I want this nice comfortable sofa.
>
> What do you think?
I had code that used the combined type/value macro and was ready to move forward
with that until I tried testing with Visual Studio (sigh!). I’d prefer being able to use the
variable template forms of various traits, but could live without if need be.
If we adopt this macro, I'm inclined to just drop the proposed
EnableIfAnd<...> and EnableIfOr<…>. I’ve not encountered that many uses for the
Or case. The And case can be done with separate ENABLE_IF’s, and indeed there
is a little bit of benefit to doing so, as the compiler may give more helpful error messages
when trying to figure out why one of the cases failed. (Similar to why it’s better to split
conjunctive asserts.)
More information about the hotspot-dev
mailing list