[webkit-dev] How we enable template functions
Keith Miller
keith_miller at apple.com
Wed Aug 23 12:27:35 PDT 2017
> On Aug 23, 2017, at 12:21 PM, Konstantin Tokarev <annulen at yandex.ru> wrote:
>
>
>
> 23.08.2017, 21:15, "Keith Miller" <keith_miller at apple.com <mailto:keith_miller at apple.com>>:
>> You can totally have the enable_if in the template list:
>>
>> #define ENABLE_TEMPLATE_IF(condition) typename = typename std::enable_if<condition>::type
>>
>> template<typename T, ENABLE_TEMPLATE_IF((std::is_same<T, int>::value))>
>> int foo() { return 0; }
>>
>> template<typename T, ENABLE_TEMPLATE_IF((std::is_same<T, double>::value))>
>> double foo() { return 0; }
>>
>> int myFunction()
>> {
>> return foo<int>();
>> }
>>
>> Compiles fine for me.
>>
>> There is another downside to the macros though. Since they will probably have a comma in them C++ thinks that comma is meant to distinguish arguments to the macro... The only work around I know of is to wrap the argument in parens as I did above.
>
> Actually, you need no parens:
>
> #define ENABLE_TEMPLATE_IF(...) typename = typename std::enable_if<__VA_ARGS__>::type
>
> template<typename T, ENABLE_TEMPLATE_IF(std::is_same<T, int>::value)>
> int foo() { return 0; }
>
> template<typename T, ENABLE_TEMPLATE_IF(std::is_same<T, double>::value)>
> double foo() { return 0; }
>
> int myFunction()
> {
> return foo<int>();
> }
Oh, clever! I didn’t think of that.
>
>
>>
>> I think mark’s case doesn’t work with my proposal so that’s convinced me that the template argument is the way to go. Although, I still think we should use the macro.
>>
>> Any objections?
>>
>> Cheers,
>> Keith
>>
>>> On Aug 23, 2017, at 7:28 AM, Mark Lam <mark.lam at apple.com> wrote:
>>>
>>> One application of enable_if I’ve needed in the past is where I want specialization of a template function with the same argument signatures, but returning a different type. The only way I know to make that happen is to use enable_if in the return type, e.g.
>>>
>>> std::enable_if<std::is_integral<T>, T>::type doStuff() { }
>>> std::enable_if<std::is_double<T>, T>::type doStuff() { }
>>>
>>> This works around the problem of “duplicate function definitions” which arises if the enable_if is not in the function signature itself. So, I’m not sure your ENABLE_TEMPLATE_IF macro will give me a solution for this.
>>>
>>> Mark
>>>
>>>> On Aug 22, 2017, at 11:14 PM, Keith Miller <keith_miller at apple.com> wrote:
>>>>
>>>>> On Aug 22, 2017, at 9:17 PM, JF Bastien <jfb at chromium.org> wrote:
>>>>>
>>>>> I'd suggest considering what it'll look like when we're migrating to concepts in C++20.
>>>>>
>>>>> Here's an example for our bitwise_cast:
>>>>> https://github.com/jfbastien/bit_cast/blob/master/bit_cast.h#L10
>>>>>
>>>>> Notice the 3 ways to enable. There's also the option of using enable_if on the return value, or as a defaulted function parameter, but I'm not a huge fan of either.
>>>>
>>>> I think the concepts approach is the cleanest. I’d avoid the macro if we go that way.
>>>>
>>>> But C++20 is a long way away and I only expect this problem to get worse over time. So I’d rather find a nearer term solution.
>>>>
>>>>> On Aug 22, 2017, at 9:13 PM, Chris Dumez <cdumez at apple.com> wrote:
>>>>>
>>>>> I personally prefer std::enable_if<>. For e.g.
>>>>>
>>>>> template<typename T, class = typename std::enable_if<std::is_same<T, int>::value>
>>>>> Class Foo { }
>>>>
>>>> I just find this much harder to parse since I now have to:
>>>>
>>>> 1) recognize that the last class is not a actually polymorphic parameter
>>>> 2) figure out exactly what the condition is given that it’s hidden inside an enable_if*
>>>>
>>>> The plus side of using a static_assert based approach is that it doesn’t impact the readability of function/class signature at a high level since it’s nested inside the body. It’s also not hidden particularly hidden since I would expect it to be the first line of the body
>>>>
>>>> Another downside of enable_if as a default template parameter is that someone could make a mistake and pass an extra template value, e.g. Foo<float, int>, and it might pick the wrong template parameter. This isn’t super likely but it’s still a hazard.
>>>>
>>>> Admittedly, we could make a macro like (totes not stolen from JF’s GitHub):
>>>>
>>>> #define ENABLE_TEMPLATE_IF(condition) typename = typename std::enable_if<condition>::type
>>>>
>>>> and implement Foo as:
>>>>
>>>> template<typename T, ENABLE_TEMPLATE_IF(std::is_same<T, int>::value)>
>>>> class Foo { };
>>>>
>>>> I think this approach is pretty good, although, I think I care about the enable_if condition rarely enough that I’d rather not see it in the signature. Most of the time the code will look like:
>>>>
>>>> template<typename T, ENABLE_TEMPLATE_IF(std::is_same<T, int>::value)>
>>>> class Foo {...};
>>>>
>>>> template<typename T, ENABLE_TEMPLATE_IF(std::is_same<T, float>::value)>
>>>> class Foo {...};
>>>>
>>>> template<typename T, ENABLE_TEMPLATE_IF(std::is_same<T, double>::value)>
>>>> class Foo {...};
>>>>
>>>> So when I know I want to use a Foo but I forgot the signature I now need to look mentally skip the enable_if macro, which I’d rather avoid.
>>>>
>>>>> I don’t like that something inside the body of a class / function would cause a template to be enabled or not.
>>>>
>>>> I believe there are cases where this already basically already happens e.g. bitwise_cast. Although, I think those cases could be fixed with a more standard approach.
>>>>
>>>> Cheers,
>>>> Keith
>>>>
>>>>> --
>>>>> Chris Dumez
>>>>>
>>>>>> On Aug 22, 2017, at 8:34 PM, Keith Miller <keith_miller at apple.com> wrote:
>>>>>>
>>>>>> Hello fellow WebKittens,
>>>>>>
>>>>>> I’ve noticed over time that we don’t have standard way that we enable versions of template functions/classes (flasses?). For the most part it seems that people use std::enable_if, although, it seems like it is attached to every possible place in the function/class.
>>>>>>
>>>>>> I propose that we choose a standard way to conditionally enable a template.
>>>>>>
>>>>>> There are a ton of options; my personal favorite is to add the following macro:
>>>>>>
>>>>>> #define ENABLE_TEMPLATE_IF(condition) static_assert(condition, “template disabled”)
>>>>>>
>>>>>> Then have every function do:
>>>>>>
>>>>>> template<typename T>
>>>>>> void foo(…)
>>>>>> {
>>>>>> ENABLE_TEMPLATE_IF(std::is_same<T, int>::value);
>>>>>> …
>>>>>> }
>>>>>>
>>>>>> And classes:
>>>>>>
>>>>>> template<typename T>
>>>>>> class Foo {
>>>>>> ENABLE_TEMPLATE_IF(std::is_same<T, int>::value);
>>>>>> };
>>>>>>
>>>>>> I like this proposal because it doesn’t obstruct the signature/declaration of the function/class but it’s still obvious when the class is enabled. Obviously, I think we should require that this macro is the first line of the function or class for visibility. Does anyone else have thoughts or ideas?
>>>>>>
>>>>>> Cheers,
>>>>>> Keith
>>>>>>
>>>>>> P.S. in case you are wondering why this macro works (ugh C++), it’s because if there is any compile time error in a template it cannot be selected as the final candidate. In my examples, if you provided a type other than int foo/Foo could not be selected because the static_assert condition would be false, which is a compile error.
>>>>>> _______________________________________________
>>>>>> webkit-dev mailing list
>>>>>> webkit-dev at lists.webkit.org
>>>>>> https://lists.webkit.org/mailman/listinfo/webkit-dev
>>>>
>>>> _______________________________________________
>>>> webkit-dev mailing list
>>>> webkit-dev at lists.webkit.org
>>>> https://lists.webkit.org/mailman/listinfo/webkit-dev
>>
>> _______________________________________________
>> webkit-dev mailing list
>> webkit-dev at lists.webkit.org
>> https://lists.webkit.org/mailman/listinfo/webkit-dev
>
> --
> Regards,
> Konstantin
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.webkit.org/pipermail/webkit-dev/attachments/20170823/e0bf0229/attachment.html>
More information about the webkit-dev
mailing list