Reputation: 1823
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
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