philipp
philipp

Reputation: 1823

indirect recursion with template functions

I wrote a very compact, limited Json Encoder with C++17 function templates:

namespace {
    using std::string;

    template<typename T>
    string toString(T&& t){
        if constexpr (std::is_arithmetic_v<std::decay_t<T>>)
            return std::to_string(t);
        else if constexpr (std::is_same<string, typename std::remove_reference<T>::type>::value)
            return "\"" + t + "\"";
        else
            return "\"" + string(t) + "\"";            
    }

    template <typename Arg1, typename Arg2>
    std::string append(Arg1&& arg1, Arg2&& arg2) {
        return toString(arg1) + ":" + toString(arg2);
    }

    template <typename Arg1, typename Arg2, typename... Args>
    std::string append(Arg1&& arg1, Arg2&& arg2, Args&&... moreArgs) {
        return toString(arg1) + ":" + toString(arg2) + "," + append(moreArgs...);
    }
}

template <typename... Args>
static std::string encodeJson(Args&&... args) {
    return "{" + append(args...) + "}";
}

It's part of a specific project, so it's fine if only cases are supported that are actually used. I can simply create JSON messages by using keys and values, as alternating parameters:

auto json = encodeJson("key1", "value1", "key2", 2);

However, now I do need nested JSON objects. I am trying to extend the code above, so I am able to do something like:

encodeJson("key", "value", "nested", std::make_tuple("subkey1","subval1","subkey2",3));

I tried by adding the following template function:

template<typename ... Args>
    std::string toString(std::tuple<Args...> t) {
        std::string s = std::apply(append, t);
        return "{" + s + "}";
    }

However, I have issues where to put it. If I put if before the definitions of append, then std::apply cannot resolve append. If I put it after the definitions of append, it is not used by append.

Apparently I am trying to accomplish indirect/mutual recursion with two template functions. Is there a solution for this? (preferrably preserving the simplicity of the code...)

General comments on the code are also appreciated, of course! I am not an expert in template programming.

Upvotes: 0

Views: 81

Answers (1)

j6t
j6t

Reputation: 13617

You have to declare the function, then define it later. That is no different from any other function, template or not.

template<typename ... Args>
    std::string toString(std::tuple<Args...> t);

// place definitions of append() here

template<typename ... Args>
    std::string toString(std::tuple<Args...> t) {
        std::string s = std::apply(append, t);
        return "{" + s + "}";
    }

Upvotes: 1

Related Questions