0

I work on an existing C++11 project where I have a problem with creating an array of pointers. Unfortunately, the error does not persist in the minimal example.

The member function of a templated class I want to call takes a two by two array of FullCloverBlock pointers, like this:

void two_flav_achimbdpsi(FourSpinorBlock *res[2],
                         const FourSpinorBlock *chi[2],
                         const FourSpinorBlock *psi[2],
                         const SU3MatrixBlock *u,
                         FullCloverBlock *clov[2][2],
                         const double beta,
                         const double epsilon,
                         const int isign,
                         const int cb);

In the test for this function, a bunch of fields are declared for use in all tests:

FullClover *A_cb0_plus = (FullClover *)geom.allocCBFullClov();
FullClover *A_cb0_minus = (FullClover *)geom.allocCBFullClov();
FullClover *A_cb1_plus = (FullClover *)geom.allocCBFullClov();
FullClover *A_cb1_minus = (FullClover *)geom.allocCBFullClov();

FullClover *clov_packed[2][2];

clov_packed[0][0] = A_cb0_plus;
clov_packed[0][1] = A_cb0_minus;
clov_packed[1][0] = A_cb1_plus;
clov_packed[1][1] = A_cb1_minus;

The type FullClover and FullCloverBlock are the same, there are just different typedefs in the different files.

Then later in that function I build a container to hold the quantity of interest. I want to use the same thing twice, this is needed for the application.

FullClover **two_flav_clov[2] = {clov_packed[target_cb],
                                 clov_packed[target_cb]};

Then I try to call the function using that new variable:

tmdslash.two_flav_achimbdpsi(two_flav_res,
                             two_flav_chi,
                             two_flav_psi,
                             u_packed[target_cb],
                             two_flav_clov,
                             beta,
                             epsilon,
                             isign,
                             target_cb);

I would expect the type of two_flav_clov to be FullClover *** or equivalent. This should match the type needed in the function two_flav_achimbdpsi that I want to call. However, I get the following error:

tests/testNDTMvsTMCloverFull.cc: In instantiation of 'void testNDTMvsTMCloverFull::runTest() [with FT = float; int V = 8; int S = 4; bool compress = true; U = QDP::OLattice<QDP::PScalar<QDP::PColorMatrix<QDP::RComplex<float>, 3> > >; Phi = QDP::OLattice<QDP::PSpinVector<QDP::PColorVector<QDP::RComplex<float>, 3>, 4> >]':
tests/testNDTMvsTMCloverFull.cc:838:58:   required from here
tests/testNDTMvsTMCloverFull.cc:421:17: error: no matching function for call to 'QPhiX::TMClovDslash<float, 8, 4, true>::two_flav_achimbdpsi(float (* [2])[3][4][2][4], float (* [2])[3][4][2][4], float (* [2])[3][4][2][4], float (*&)[8][2][3][2][8], FullClover** [2], double&, const double&, int&, int&)'
                 tmdslash.two_flav_achimbdpsi(two_flav_res,
                 ^~~~~~~~
In file included from include/qphix/tm_clov_dslash_def.h:376:0,
                 from include/qphix/qdp_packer_parscalar.h:21,
                 from include/qphix/qdp_packer.h:15,
                 from tests/testNDTMvsTMCloverFull.cc:24:
include/qphix/tm_clov_dslash_body.h:1817:10: note: candidate: void QPhiX::TMClovDslash<FT, veclen, soalen, compress12>::two_flav_achimbdpsi(QPhiX::TMClovDslash<FT, veclen, soalen, compress12>::FourSpinorBlock**, const FourSpinorBlock**, const FourSpinorBlock**, const SU3MatrixBlock*, QPhiX::TMClovDslash<FT, veclen, soalen, compress12>::FullCloverBlock* (*)[2], double, double, int, int) [with FT = float; int veclen = 8; int soalen = 4; bool compress12 = true; QPhiX::TMClovDslash<FT, veclen, soalen, compress12>::FourSpinorBlock = float [3][4][2][4]; QPhiX::TMClovDslash<FT, veclen, soalen, compress12>::SU3MatrixBlock = float [8][2][3][2][8]; QPhiX::TMClovDslash<FT, veclen, soalen, compress12>::FullCloverBlock = QPhiX::Geometry<float, 8, 4, true>::FullCloverBlock]
     void TMClovDslash<FT, veclen, soalen, compress12>::two_flav_achimbdpsi(
          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/qphix/tm_clov_dslash_body.h:1817:10: note:   no known conversion for argument 5 from 'FullClover** [2] {aka QPhiX::Geometry<float, 8, 4, true>::FullCloverBlock** [2]}' to 'QPhiX::TMClovDslash<float, 8, 4, true>::FullCloverBlock* (*)[2] {aka QPhiX::Geometry<float, 8, 4, true>::FullCloverBlock* (*)[2]}'

The core seems to be that it cannot convert FullClover** [2] to FullCloverBlock* (*)[2]. This syntax with the parentheses is new to me. What does it mean?

The following is a minimal example that I think should do the same thing. However, it does not fail to compile.

struct FullClover {
    int x;
};

void achimdpsi(FullClover *clov[2][2]) {}

int main(int argc, char **argv) {
    FullClover *A;
    FullClover *clov_packed[2][2];
    clov_packed[0][0] = A;
    clov_packed[0][1] = A;
    clov_packed[1][0] = A;
    clov_packed[1][1] = A;

    int cb;
    FullClover **two_flav_clover[2] = {clov_packed[cb], clov_packed[cb]};

    achimdpsi(clov_packed);
}

Therefore I am not sure what happens here, the minimal example should show the same errors, if the problem was indeed the type.

What is going on here? How can I fix this?

Martin Ueding
  • 8,245
  • 6
  • 46
  • 92
  • Why not simply `std::array`? – πάντα ῥεῖ Feb 26 '17 at 20:01
  • I would have to change everything to `std::array` from the normal C arrays, right? Also shouldn't it be `std::array`, with two asterisks? – Martin Ueding Feb 26 '17 at 20:05
  • _"with two asterisks?"_ No. Other question why are you using raw pointers at all? – πάντα ῥεῖ Feb 26 '17 at 20:06
  • The variable `two_flav_clover` shall store two arrays (for *flavor*) of two arrays (for *plus/minus*) of an array (lots of items) of `FullClover`. `std::array` would just give me the two *flavor* components of long arrays, but I would miss out on the *plus/minus* index in the data structure. – Martin Ueding Feb 26 '17 at 20:09
  • Why I am using raw pointers: The allocation is done with special Intel aligned allocation routines that seem to have a C rather than a C++ API. Also the whole codebase uses raw pointers and offsets instead of `std::vector` with a custom allocator. – Martin Ueding Feb 26 '17 at 20:11
  • 3
    `FullCloverBlock* (*)[2]` is a pointer to an array of pointers. Unlike `FullClover** [2]` which is an array of pointers to pointers. See the difference? Generally, a multidimensional array is *not at all* the same as pointers to pointers (to pointers). See this [further explained in this answer](http://stackoverflow.com/a/4810676/597607) – Bo Persson Feb 26 '17 at 20:21
  • @BoPersson: I did not realize that `int a[10][10]` would create `int a[100]` and would do flattening. That is great. Also this explains why I cannot cast to `int **`, there is nothing of that anywhere. So I actually need `const FullClover *two_flav_clov[2][2] = { {clov_packed[target_cb][0], clov_packed[target_cb][1]}, {clov_packed[target_cb][0], clov_packed[target_cb][1]} };` to get it to work. Now it seems to work, except that I have to think about `const` as a next step. — Do you want to turn your comment into an answer such that I can accept it? – Martin Ueding Feb 27 '17 at 09:42

0 Answers0