Search code examples
c++c++14

error: implicit instantiation of undefined template 'std::tuple_element<0, std::tuple<std::string, int> &>'


I have checked out the related posts to this error and wasn't able to use those solutions to solve my problem. So i have this code that attempts to dynamically create a tuple by converting stringified values to their corresponding types in the array for that I have a generic function that aims to produce that value and add it to the tuple but it seems to be throwing an error whenever I try to generically set its return type.

This the generic function that converts the stringified argument into its actual type (its just a minimal exmaple btw not the whole function i have written in my codebase).

#include <string>
#include <iostream>
#include <tuple>
#include <vector>
#include <type_traits>

template <typename T>
T convertArg(string type, string arg)
{   
    if (type == "Integer") {
        int temp = stoi(arg);
        return temp;
    } else if (type == "Boolean") {
        return arg == "true" ? true : false;
    } else {
        return arg;
    }
};

I have a runtime loop that creates the tuple by using the function above.

namespace detail
{
    template <int... Is>
    struct seq
    {
    };

    template <int N, int... Is>
    struct gen_seq : gen_seq<N - 1, N - 1, Is...>
    {
    };

    template <int... Is>
    struct gen_seq<0, Is...> : seq<Is...>
    {
    };
    template <typename... Ts, int... Is>
    tuple<Ts...> for_each(tuple<Ts...> &t, std::vector<string> types, std::vector<string> args, seq<Is...>)
    {
// this line throws the error where i try set the return type of the function on the basis of the element at that index in the tuple.
        return make_tuple(convertArg<tuple_element<Is, decltype(t)>::type>(types.at(Is), args.at(Is))...);
    }
}

template <typename... Ts>
tuple<Ts...> for_each_in_tuple(std::tuple<Ts...> &t, std::vector<string> types, std::vector<string> args)
{
    return detail::for_each(t, types, args, detail::gen_seq<sizeof...(Ts)>());
}


int main()
{
    std::vector<std::string> types = {"String", "Integer"};
    std::vector<std::string> args = {"rehan", "12"};
    std::tuple<std::string, int> t;
    t = for_each_in_tuple(t, types, args);
    cout << "Tuple values" << endl;
    cout << get<0>(t) << endl;
    cout << get<1>(t) << endl;
}

The complete error as well:

main.cpp:41:38: error: implicit instantiation of undefined template 'std::tuple_element<0, std::tuple<std::string, int> &>'
        return make_tuple(convertArg<tuple_element<Is, decltype(t)>::type>(types.at(Is), args.at(Is))...);

I've already looked at the other solution and it didnt work for me and thats why i have asked this question to get a solution related to my particular scenario and btw im on c++ 14


Solution

  • Instead of decltype(t) it should be std::remove_reference_t<decltype(t)> but that just leads to the next problem, your convertArg function will not work. All branches must be valid and if T is std::string returning int isn't valid for example.

    I suggest that you drop the vector with types and just use the information in the tuple:

    template <class T> // primary, just let the string through
    T convertArg(const std::string& arg) {
        return arg;
    }
    
    template <> // specialization for int
    int convertArg<int>(const std::string& arg) {
        return stoi(arg);
    }
    template <> // specialization for bool
    bool convertArg<bool>(const std::string& arg) {
        return arg == "true";
    }
    
    namespace detail {
    template <typename... Ts, std::size_t... Is>
    std::tuple<Ts...> for_each(std::tuple<Ts...>& t,
                               const std::vector<std::string>& args,
                               std::index_sequence<Is...>) {
        return make_tuple(
            convertArg<
                std::tuple_element_t<Is, std::remove_reference_t<decltype(t)>>>(
                args.at(Is))...);
    }
    }  // namespace detail
    
    template <typename... Ts>
    std::tuple<Ts...> for_each_in_tuple(std::tuple<Ts...>& t,
                                        const std::vector<std::string>& args) {
        return detail::for_each(t, args, std::index_sequence_for<Ts...>());
    }
    

    std::remove_reference_t<decltype(t)> could also be replaced with std::tuple<Ts...>.

    Demo