Search code examples
c++templatesvariadic-functionsvariadic

Acces violation on va_arg


I am trying to create a function that takes a variable number of matrix in parameters and multiply theses to a first one. I can read a first one using va_arg, but the next call of va_arg will cause an access violation.

Here is how i declare my method :

template<class MType>
static CMatrice<MType> COperationsComplexesMatricesPassages::OCPChangementDeBase
   (CMatrice<MType> & MATVecteur, unsigned int uiNbMatricesPassages,
    CMatricePassage<MType> MAPMatrices...)

And here is how i call it :

COperationsComplexesMatricesPassages::OCPChangementDeBase(mat1, 2, matP1, matP2)

The exception appears on the first va_arg of the for(...) in the body of my method. This is the code of my method:

unsigned int uiIndice;
unsigned int uiBaseArriveePrecedente;

va_list args;
va_start(args, MAPMatrices);

CMatrice<MType> MATResult(MATVecteur);
CMatricePassage<MType> MAPMatricePass = va_arg(args, CMatricePassage<MType>);

MATResult = MATResult * MAPMatricePass;

uiBaseArriveePrecedente = MAPMatricePass.MAPGetBaseArrivee();
for (uiIndice = 1; uiIndice < uiNbMatricesPassages; uiIndice++) {
    CMatricePassage<MType> MAPMatricePass2 = va_arg(args, CMatricePassage<MType>);
    if (uiBaseArriveePrecedente != MAPMatricePass2.MAPGetBaseDepart()) {
        CException EXCError(EXC_ChangementImpossible);
        throw EXCError;
    }
    uiBaseArriveePrecedente = MAPMatricePass2.MAPGetBaseArrivee();
    MATResult = MATResult * MAPMatricePass2;
}

return MATResult;

Solution

  • I don't understand what you exactly want obtain from your OCPChangementDeBase() method, anyway... maybe I'm wrong... but it seems to me that there are a couple of important points, regarding variadic functions, that you don't know.

    (1) the old C variad syntax

    void foo (int a, int b...)
    

    doesn't mean that b is a variadic list of integer.

    That declaration is equivalent (the last comma is optional) to

    void foo (int a, int b, ...)
    

    so, with both declarations, you have a b integer (a single b integer) and an unnamed list of variadic arguments.

    So given you method

    template<class MType>
    static CMatrice<MType>
    COperationsComplexesMatricesPassages::OCPChangementDeBase
       (CMatrice<MType> & MATVecteur, unsigned int uiNbMatricesPassages,
        CMatricePassage<MType> MAPMatrices...)
    

    and calling it

    COperationsComplexesMatricesPassages::OCPChangementDeBase(mat1, 2, matP1, matP2)
    

    you have that

    • MATVecteur become mat1
    • uiNbMatricesPassages become 2
    • MAPMatrices become matP1
    • the unnamed variadic list ... become matP2

    So, if you're expecting two arguments in the unnamed varidic list, you have only one and I'm not surprised that "the next call of va_arg will cause an access violation."

    (2) the old C variadic syntax (va_list, va_arg, va_start based) remains available in C++ but, as far I know, works only for PODs (Plain Old Data) types.

    So, as far I know, your code is UB (undefined bahaviour) because matP2 (I suppose) isn't POD.

    Fortunately C++ (starting from C++11) introduce variadic templates that are compatible also with not PODs types.

    So, I suppose, you count write your method as follows or something similar

    template <typename MType, typename ... MTs>
    static auto COperationsComplexesMatricesPassages::OCPChangementDeBase
       (CMatrice<MType> & MATVecteur, MTs ... MAPMatrices)
     {
       auto MatResult { MatVectour };
    
       ( MatResult *= MapMatrices, ... ); // template folding; only from C++17
    
       return MatResult;
     }
    

    You can also add some constraints (look for SFINAE) to impose that MTs... types are exactly (or, maybe better, convertible to) CMatricePassage<MType> (or another type, if you want).