Templated Function Pointer Arrays in Visual Studio
Asked Answered
D

1

3

Guillaume Racicot gave an excellent answer to this question on how I could specialize template variables. But I'm having trouble in with creating a templated array of function pointers. This code for example:

struct vec
{
    double x;
    double y;
    double z;
};

namespace details
{
template <typename T>
constexpr double X(const T& param) { return param.x; }

template <typename T>
constexpr double Y(const T& param) { return param.y; }

template <typename T>
constexpr double Z(const T& param) { return param.z; }
}

template <typename T, typename = void>
constexpr double (*my_temp[])(const vec&) = { &details::X<T>, &details::Y<T> };

template <typename T>
constexpr double (*my_temp<T, enable_if_t<is_floating_point_v<decltype(details::X(T()))>>>[])(const vec&) = { &details::X<T>, &details::Y<T>, &details::Z<T> };


int main() {
    vec foo = { 1.0, 2.0, 3.0 };

    for(const auto i : my_temp<decltype(foo)>) {
        cout << (*i)(foo) << endl;
    }
}

In gcc outputs:

1
2
3

But in only outputs:

1
2

Is there something I can do to work around this?

Dauphin answered 18/6, 2019 at 12:44 Comment(13)
Hmmm... this code seem to ICE clang 8, but not trunk.Trevethick
I can't compile this in Visual Studio 2017 with /std:c++17, did you maybe mean Visual Studio 2019?Interlocutory
@RudolfsBundulis Honestly I wasn't using: /std:c++17 Maybe that's the issue?Dauphin
@JonathanMee but in Visual Studio 2017 it does not work neither with /std:c++14 nor /std:c++17. So, are you really using Visual Studio 2017?Interlocutory
FWIW, VS2019 fails to compile the code so you can't even upgrade to fix it.Liquidation
With std::array, it works on all three compilers: godbolt.org/z/Ro-WHZTrevethick
Yeah, VS2019 fails as well - godbolt.org/z/eUor-4Interlocutory
Wonder if it is a syntax issue. Can't parse the array declaration.Liquidation
@RudolfsBundulis Can confirm this is indeed Visual Studio 2017: i.sstatic.net/iYkuY.pngDauphin
@Liquidation As in I'm using bad syntax? I'm assuming my syntax is OK, since gcc can parse it?Dauphin
@GuillaumeRacicot Maybe array is the workaround then? I'll try locally...Dauphin
@JonathanMee I'm saying MSVS can't handle the syntax, not that it is bad. It handles the trivial case from your previous Q but boorks on this.Liquidation
@GuillaumeRacicot Indeed array works locally. Seems like the most viable workaround available to us at this point if you care to post an answer?Dauphin
T
1

Welcome to the world of compiler bugs! Your syntax is completely valid, yet only GCC can compile it.

So far, I tested with multiple clang, gcc and msvc versions.

Your variation with the function pointer raw array, only GCC parses it correctly. Clang 8.0.0 will crash, and MSCV will not compile it.

I tried two other variations: with a template alias and std::array

Function pointer alias template:

template<typename T>
using fptr = auto(*)(T const&) -> double;

template <typename T, typename = void>
constexpr fptr<T> my_temp[] = {
    &details::X<T>, &details::Y<T>
};

template <typename T>
constexpr fptr<T> my_temp<T, enable_if_t<is_floating_point_v<decltype(details::X(T()))>>>[] = {
    &details::X<T>, &details::Y<T>, &details::Z<T>
};

std::array + CTAD:

template <typename T, typename = void>
constexpr std::array my_temp = {
    &details::X<T>, &details::Y<T>
};

template <typename T>
constexpr std::array my_temp<T, enable_if_t<is_floating_point<decltype(details::X(T()))>::value>> = {
    &details::X<T>, &details::Y<T>, &details::Z<T>
};

To remove CTAD, simply use std::array<auto(*)(const vec&) -> double, 3>.

Here's the results:

+------------+-------+-------+-------+
| Compiling? |  GCC  | Clang | MSVC  |
+------------+-------+-------+-------+
| raw array  |  Yes  |  ICE  |  No   |
+------------+-------+-------+-------+
| fptr alias |  Yes  |  ICE  |  Yes  |
+------------+-------+-------+-------+
| std::array |  Yes  |  Yes  |  Yes  |
+------------+-------+-------+-------+

Note that on the upcoming clang 9, it will be on par with GCC. All versions needs at least MSVC 2017. With workaround I'm sure it's possible to make it work with msvc 2015 too.

In the end, as long as it work on the platform you need right now, that would be okay. std::array have a small compile time cost, but the raw array is surprisingly less portable as of right now.

Trevethick answered 18/6, 2019 at 13:23 Comment(5)
clang 8 appears to workChorography
No? Haha, I don't know which to believe.Chorography
Me neither at this point. I don't have clang installed on the computer I use right now, only AppleClang.Trevethick
@Chorography and Guillaume Racicot OK guys one more: I understand what we're doing here, and I can use the workarounds, but I'm not following why the specialized argument must always be void for this to work. Can you guys help clarify for me: https://mcmap.net/q/1781190/-why-must-specializing-argument-be-void/2642059Dauphin
@JonathanMee: Max's answer there is spot on. You might be able to write yet another level of indirection if you choose not to provide voidChorography

© 2022 - 2024 — McMap. All rights reserved.