Home > Software engineering >  Compiling a function with templates fails in variadic template function
Compiling a function with templates fails in variadic template function

Time:03-02

I have come across a compiler error involving variadic templates. The following code is a strongly simplified version which reproduces the error in my original code:

#include <iostream>
#include <sstream>
#include <string>
#include <vector>

typedef std::vector<std::string> stringvec;

// dummy function: return string version of first vectorelemenr
template<typename T>
std::string vecDummy(const std::string sFormat, const T t) {
    std::stringstream ss("");
    if (t.size() > 0) {
        ss << t[0];
    }
    return  ss.str();    
}

// recursion termination
std::string recursiveY(stringvec &vFlags,  uint i) {
    return "";
}  

// walk through arguments
template<typename T, typename... Args>
std::string recursiveY(stringvec &vFlags,  uint i, T value, Args... args) {

    std::string sRes = "";
    if (vFlags[i] == "%v") {
        sRes  = vecDummy(vFlags[i], value);
    }    
    sRes  = " " recursiveY(vFlags, i 1, args...);

    return sRes;
}

int main(void) {
    stringvec vPasta   = {"spagis", "nudle", "penne", "tortellini"};
    stringvec vFormats = {"%v", "%s"};

    std::string st = "";
    st  = recursiveY(vFormats, 0, vPasta, "test12");
    std::cout << ">>" << st  << "<<" << std::endl;

    return 0;
} 

This simple code should walk through the arguments passed to recursiveY() and, if the current format string is "%v" it would pass the corresponding argument to vecDummy() which would return a string version of the vector's first element (if there is one).

The error message from the compiler is

sptest2.cpp: In instantiation of ‘std::string vecDummy(std::string, T) [with T = const char*; std::string = std::__cxx11::basic_string<char>]’:
sptest2.cpp:30:25:   required from ‘std::string recursiveY(stringvec&, uint, T, Args ...) [with T = const char*; Args = {}; std::string = std::__cxx11::basic_string<char>; stringvec = std::vector<std::__cxx11::basic_string<char> >; uint = unsigned int]’
sptest2.cpp:32:27:   required from ‘std::string recursiveY(stringvec&, uint, T, Args ...) [with T = std::vector<std::__cxx11::basic_string<char> >; Args = {const char*}; std::string = std::__cxx11::basic_string<char>; stringvec = std::vector<std::__cxx11::basic_string<char> >; uint = unsigned int]’
sptest2.cpp:43:21:   required from here
sptest2.cpp:12:11: error: request for member ‘size’ in ‘t’, which is of non-class type ‘const char* const’
   12 |     if (t.size() > 0) {
      |         ~~^~~~

It seems as if the compiler uses all types i pass to recursiveY() in main, but vecDummy() is designed to only work with vectors of some kind (and not with const char*, for example).

Is there a possibility to modify this code so that it will work as intended?

Is there perhaps a way of assuring the compiler that i will only pass vectors to vecDummy() (even at the risk of a runtime error or unexpected behaviour - similar to passing an integer to printf() when it expects a string)?

CodePudding user response:

You can add an overload of vecDummy that handles the std::vector case and 'dumb down' the more general one to (say) just return an empty string:

// dummy function: return string version of first vectorelement (catch-all)
template<typename T>
std::string vecDummy(const std::string, const T) {
    return "";
}

// dummy function: return string version of first vectorelement (vectors only
template<typename T>
std::string c(const std::string, const std::vector <T> t) {
    std::stringstream ss("");
    if (t.size() > 0) {
        ss << t[0];
    }
    return  ss.str();    
}

Live demo

CodePudding user response:

I think if constexpr can help here. I'm posting two solutions.

First solution (only the function vecDummy changes). Here, vecDummy receives parameters of all types, doing its intended work only for vectors and doing nothing when the parameter is not a vector.

template<typename T>
std::string vecDummy(
    [[maybe_unused]] const std::string sFormat,
    [[maybe_unused]] const T t
)
noexcept
{
    std::stringstream ss("");
    if constexpr (std::is_same_v<T, stringvec>) {
        if (t.size() > 0) {
            ss << t[0];
        }
    }
    else {
        // nothing, since this is intended to work only for vectors
    }
    return ss.str();
}

Another solution is to move if constexpr inside recursiveY (and leave vecDummy unchanged). In this solution, vecDummy is only called for parameters of vector-type and when the format is "%v".

template<typename T, typename... Args>
std::string recursiveY(
    const stringvec& vFormats,
    std::size_t i,
    T value, Args... args
)
noexcept
{
    std::cout << "(1) i= " << i << '\n';

    std::string res = "";

    if (vFormats[i] == "%v") {
        if constexpr (std::is_same_v<T, stringvec>) {
            res  = vecDummy(vFormats[i], value);
        }
    }
    else {
        if constexpr (not std::is_same_v<T, stringvec>) {
            res  = std::string(value);
        }
    }
    res  = " "   recursiveY(vFormats, i 1, args...);

    return res;
}
  • Related