mardi 28 mars 2017

Array with const pointers, works in GCC but not with Intel C++

I have a numerical library that has been designed with one “flavor” in mind. Now I want to generalize this. The basic data structure is a “spinor” which itself is a multi dimensional matrix. There are lots of functions which take arrays of these spinors. The generalized functions need to take one such spinor array for each flavor.

Say there is a function which, minimally, does the following:

void legacy(Spinor *out, const Spinor *in) {}

My generalization now is this:

void legacy(Spinor *out[num_flav], const Spinor *in[num_flav]) {}

As far as I understand, one has to read this as const Spinor *(in[num_flav]), so in is a pointer to an array of probably num_flav elements (or another quantity because foo[] is just *foo in a function parameter) of type pointer-to-const-spinor.

The problem is that it does not compile when using a Spinor *non_const[2] (without the const), see my earlier question. From the answer there I have learned that this must not compile because within the function legacy, the pointer non_const[0] could be made to point to some const array of Spinor *. Then non_const would point to const data. Therefore that does not work.

My conclusion was that adding another const will make it correct:

void legacy(Spinor *out[num_flav], const Spinor *const in[num_flav]) {}

When I now pass my non_const as the second parameter, the function cannot change in[0] to anything, because that pointer is now immutable. This has served me well with GCC 6.3. Now going into production with Intel C++ 17, it does not work any more.

The minimal working example is the following:

#include <cstdint>

typedef float Spinor[3][4][2][8];

template <uint8_t num_flav>
class Solver {
  public:
    void legacy(Spinor *out, const Spinor *in) {}
    void legacy(Spinor *out[num_flav], const Spinor *const in[num_flav]) {}
};

int main(int argc, char **argv) {
    Spinor *s1 = new Spinor[10];
    Spinor *s2 = new Spinor[10];

    Spinor *s1_a[1] = {s1};
    Spinor *s2_a[1] = {s2};

    Solver<1> s;

    s.legacy(s2_a, s1_a);
}

On GCC, it apparently resolves to the second legacy overload. The variable s1_a which takes the role of the previous non_const, is allowed as an argument.

Intel C++ 17 however, does not accept that:

$ icpc -Wall -pedantic const-spinor-const.cpp  --std=c++11
const-spinor-const.cpp(23): error: no instance of overloaded function "Solver<num_flav>::legacy [with num_flav=(uint8_t={unsigned char})'\001']" matches the argument list
            argument types are: (Spinor *[1], Spinor *[1])
            object type is: Solver<(uint8_t)'\001'>
      s.legacy(s2_a, s1_a);
        ^
const-spinor-const.cpp(11): note: this candidate was rejected because arguments do not match
      void legacy(Spinor *out[num_flav], const Spinor *const in[num_flav]) {}
           ^
const-spinor-const.cpp(10): note: this candidate was rejected because arguments do not match
      void legacy(Spinor *out, const Spinor *in) {}
           ^

The error message is not particularly helpful because it does not say what conversion was not allowed. It just seems that the const is the problem.

When I change the definition of s1_a in the main function to have the two const, it compiles:

const Spinor *const s1_a[1] = {s1};

In order to run the code in production, I see the following options:

  1. Add a const wrapper for every array that I pass to my generalized functions. That will look pretty bad and is not really easy to maintain.

  2. Remove the left-most const from the function argument parameters. That compiles cleanly on both compilers. However, I do want to document that I am not changing anything in that array, therefore its values should be const.

Is there perhaps something I miss with Intel C++? Is it lacking a feature or did I make use of an unofficial GCC extension? Is that a bug in Intel C++ or GCC?

What would be a good way to go forward? I can alter my new functions all I want, but I would like to avoid touching caller code as much as possible.

Aucun commentaire:

Enregistrer un commentaire