Sandor
Sandor

Reputation: 25

A compilation issue regarding constexpr expressions in Visual Studio 2017

I'm working on a rendering engine using Vulkan and Visual Studio 2017, and I bumped into the following type of problem recently.

I have a template struct template<uint32_t id> struct A;. This struct is defined (in separate header files) for id=0, ... , N-1. All of the definitions have a static constexpr std::array<B, M(id)> member for some struct B and number M depending on id. I have a constexpr function (and a helper function) which for a given value b of type B counts how many elements of all of these arrays equal to b. It looks something like this:

Helper function:

template<size_t Size>
constexpr void count_in_array(B b, const std::array<B, Size>& a, uint32_t& count)
{
    for(auto& e : a)
    {
        if(e==b)
            ++count;
    }
}

Main function:

template<uint32_t... ids>
constexpr uint32_t count_in_arrays(B b, std::index_sequence<ids...>)
{
   uint32_t count=0;
   auto l ={ (count_in_array(b, A<ids>::member, count), 0)... };
   return count;
}

When I compile, I get a C1001 internal compiler error. The strange thing is that my funcions work, because if I use them to define a constexpr variable

constexpr uint32_t var=count_in_arrays(b, std::make_index_sequence<N>());

(for a constexpr B b), and I hoover the mouse over that variable, I see the computed (and correct) number in the appearing rectangle.

I am not familiar with compiler switches, I only tried to use #pragma optimize("", on/off) around the above functions, but that didn't help. Does somebody have an idea how to make Visual Studio to compile my code?

Remark: I am pretty sure that the struct B is not important here, in my case, it is a simple data struct containing some built-in variables.

Upvotes: 1

Views: 599

Answers (1)

Barry
Barry

Reputation: 302718

First, an internal compiler error is always a compiler bug. Please report this to MSVC.

Second, this implementation is a bit odd. When you write constexpr functions you want to think in a more functionally-oriented way - input-only arguments, output-only results. count_in_array should surely just return a number:

template <size_t Size>
constexpr uint32_t count_in_array(B b, const std::array<B, Size>& a)
{
    uint32_t count = 0;
    for(auto& e : a)
    {
        if(e==b)
            ++count;
    }
    return count;
}

This is a more reasonable implementation - count returns a count. Not only that, but it composes really nicely. How do you get all the counts? You sum them:

template <size_t... Ids>
constexpr uint32_t count_in_arrays(B b, std::index_sequence<Ids...>)
{
   return (count_in_array(b, A<Ids>::member) + ...);
}

Much clearer.


Note that, while I think fold-expressions don't quite work in MSVC yet (though might soon?), that in of itself is not a reason to implement this differently. It just means that you need to manually sum - not that count_in_array() shouldn't return a count.

Upvotes: 1

Related Questions