Reputation: 2594
In json.h I have:
template <class T>
Json::Value write_json(const T& object);
In json.cpp:
template <>
Json::Value write_json(const bool& object) {
Json::Value output;
output = object;
return output;
};
template <>
Json::Value write_json(const int& object) {
Json::Value output;
output = object;
return output;
};
template <>
Json::Value write_json(const std::vector<bool>& v) {
Json::Value output;
for (auto it = v.begin(); it != v.end(); ++it) { output.append(*it); };
return output;
};
template <>
Json::Value write_json(const std::vector<int>& v) {
Json::Value output;
for (auto it = v.begin(); it != v.end(); ++it) { output.append(*it); };
return output;
};
Is there any way in C++ to specialize basic types on one "subtemplate" and containers on another?
Upvotes: 4
Views: 372
Reputation: 10315
Let's start with trait to check if you are getting container or not:
template<class ...>
using void_t = void;
template<class T, class = void>
struct is_container : std::false_type{};
template<class T>
struct is_container<T, void_t<decltype(std::begin(std::declval<T>())), decltype(std::end(std::declval<T>()))>> : std::true_type{};
template<class T>
constexpr auto is_container_v = is_containter<T>::value;
With that you can just do:
template <class T>
Json::Value write_json_primitive(const T& object) {
static_assert(!is_container_v<T>);
Json::Value output;
output = object;
return output;
};
template <class T>
Json::Value write_json_container(const T& v) {
static_assert(is_container_v<T>);
Json::Value output;
for (const auto& val : v) { output.append(val); };
return output;
};
template <class T>
Json::Value write_json(const T& object) {
if constexpr (is_container_v<T>)
return write_json_container(object);
else
return write_json_primitive(object);
}
Sidenote: this code requires C++17 support. If you need explicitly C++11 it can be done but a bit differently
A bit more metaprogramming is needed here and static_asserts
cannot be used so easily but still I did it:
template<typename... Ts> struct make_void { typedef void type;};
template<typename... Ts> using void_t = typename make_void<Ts...>::type;
template<class T, class = void>
struct is_container : std::false_type{};
template<class T>
struct is_container<T, void_t<decltype(std::begin(std::declval<T>())), decltype(std::end(std::declval<T>()))>> : std::true_type{};
template <class T>
Json::Value write_json_primitive(const T& object) {
Json::Value output;
output = object;
return output;
};
template <class T>
Json::Value write_json_container(const T& v) {
Json::Value output;
for (const auto& val : v) { output.append(val); };
return output;
};
template<class T>
using json_function = std::conditional<
is_container<T>::value,
std::integral_constant<decltype(&write_json_container<T>), &write_json_container<T>>,
std::integral_constant<decltype(&write_json_primitive<T>), &write_json_primitive<T>>>;
template <class T>
Json::Value write_json(const T& object) {
return json_function<T>::type::value(object);
}
This hopefully will work like a charm ;)
Upvotes: 6