Search code examples
c++eigenassertions

Eigen Library: Assertions failed in a template


I have a templated function, which should generate a compile-time known fixed-size Vector, dependent on the template parameter:

template <int nSize> 
Eigen::Matrix<double, nSize, 1> myFunc()
{
    switch(nSize){
    case 2: return ExternalLibrary::generate_Vector_2d();
    case 3: return ExternalLibrary::generate_Vector_3d();
    }
}

However, when i compile this code, i get a failed assertion, because the Vector_2d size doesn't fit the 3d size:

Eigen::Vector3d testVector3d = myFunc<3>(); // failed, because 3d doesn't fit the theoreticelly possible 2d 

Basically, the failed assertion is understandable, but of course this will never be an issue at runtime. How can i bypass this failed assertion? Or is there a more elegant way than the switch condition?

Thank you!


Solution

  • Just declare the template function and don't define it, then define two specializations:

    template <int nSize>
    Eigen::Matrix<double, nSize, 1> myFunc();
    
    template <>
    Eigen::Matrix<double, 2, 1> myFunc<2>()
    {
        return ExternalLibrary::generate_Vector_2d();
    }
    
    template <>
    Eigen::Matrix<double, 3, 1> myFunc<3>()
    {
        return ExternalLibrary::generate_Vector_3d();
    }
    

    The compiler, while it will eliminate dead code as an optimization, must verify the validity of the dead code anyway, which means it must ensure that the return type of ExternalLibrary::generate_Vector_3d() can be converted to Eigen::Matrix<double, 2, 1>, even though it can be proven that that return statement can't be executed. Because this conversion is not allowed, you get a compile-time error.

    By separating out the two return statements into separate specializations, you eliminate this problem as it removes the return ExternalLibrary::generate_Vector_3d(); statement from a function that could return something other than an Eigen::Matrix<double, 3, 1>.


    Note that using myFunc() with an nSize other than 2 or 3 will cause a link-time error. We can move this error to compile-time by providing a body that will fail to instantiate, but that (ostensibly) depends on nSize so that the compiler won't try to instantiate it before it is used:

    template <int nSize>
    Eigen::Matrix<double, nSize, 1> myFunc()
    {
        // With C++11, the following line provides a more useful diagnostic. If
        // you don't have C++11 support, remove it and the bad return statement
        // will do the job, albeit with a more cryptic error message.
        static_assert((static_cast<void>(nSize), false), "Invalid template parameter nSize");
    
        return static_cast<class INVALID_NSIZE_PARAMETER**********>(nSize);
    }
    

    This will provide a diagnostic pointing to the incorrect use of myFunc(). For example, without C++11 support you would see something like:

    main.cpp:12:12: error: cannot cast from type 'int' to pointer type 'class INVALID_NSIZE_PARAMETER **********'
        return static_cast<class INVALID_NSIZE_PARAMETER**********>(nSize);
               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    main.cpp:29:5: note: in instantiation of function template specialization 'myFunc<4>' requested here
        myFunc<4>();
        ^