For some serialization application in a relatively old software, I have types that look like this:
using T = boost::tuple<
std::pair<std::integral_constant<uint32_t, 0>, std::vector<int>>,
std::pair<std::integral_constant<uint32_t, 1>, std::vector<double>>
>;
T events;
These constant numbers are static constexpr
and represent some database table, and the vector is of some storage type (details not important).
To make this "kind of" type safe, and make everything work together for the future, I need to ensure that a user who adds another type to the tuple follows the serial number. So another element should be this:
using T = boost::tuple<
std::pair<std::integral_constant<uint32_t, 0>, std::vector<int>>,
std::pair<std::integral_constant<uint32_t, 1>, std::vector<double>>,
std::pair<std::integral_constant<uint32_t, 2>, std::vector<float>>
>;
T events;
This is wrong and should not compile:
using T = boost::tuple<
std::pair<std::integral_constant<uint32_t, 0>, std::vector<int>>,
std::pair<std::integral_constant<uint32_t, 1>, std::vector<double>>,
std::pair<std::integral_constant<uint32_t, 1>, std::vector<float>>
>;
T events;
My solution has failed so far, and I'd appreciate someone's input on this:
template <typename Tuple>
struct tuple_assert;
template <typename... Ts>
struct tuple_assert<boost::tuple<Ts...>> {
static constexpr void verify() {
__verify<std::integral_constant<uint32_t, 0>, Ts...>();
}
static constexpr void __verify() {}
template <typename Count, typename Pair>
static constexpr void __verify() {
static_assert(std::is_same<typename Pair::first_type, Count>::value, "");
}
template <typename Count, typename Pair, typename... Ts2>
static constexpr void __verify() {
static_assert(std::is_same<typename Pair::first_type, Count>::value, "");
__verify<std::integral_constant<uint32_t, Pair::first_type::value + 1>,
Ts...>();
}
};
So what you see up there is that I created a state (Count
) and I'm increasing the count at every iteration. But this somehow reaches the wrong state and the static_assert()
fires when I use it with this call:
tuple_assert<T>::verify(); // `T` is the type I mentioned at the very beginning
See my solution that doesn't work online.
What am I doing wrong?
There are several things wrong with your code. First one is in this line, a typo in template argument list expansion:
__verify<std::integral_constant<uint32_t, Pair::first_type::value + 1>,
Ts...>();
It should be:
__verify<std::integral_constant<uint32_t, Pair::first_type::value + 1>,
Ts2...>();
But sadly it won't fix it. boost::tuple
has some strange typedefs inside with some nulltype_t
. Changing it to std::tuple
still won't fix it as your __verify
function call is ambiguous. So instead here are my solutions.
std::tuple
template<unsigned V, class T, class ...Args>
struct verify_types {
static constexpr bool value = V == T::first_type::value && verify_types<V+1, Args...>::value;
};
template<unsigned V, class T>
struct verify_types<V, T> {
static constexpr bool value = V == T::first_type::value;
};
template<class T>
struct verify_tuple : std::false_type {};
template<class ...Args>
struct verify_tuple<std::tuple<Args...>> : verify_types<0, Args...>{};
boost::tuple
This one is a bit more convoluted.
template<unsigned V, class T, class ...Args>
struct verify_types {
static constexpr bool value = V == T::first_type::value && verify_types<V+1, Args...>::value;
};
template<unsigned V, class ...Args>
struct verify_types<V, boost::tuples::null_type, Args...> {
static constexpr bool value = true;
};
template<unsigned V, class T>
struct verify_types<V, T> {
static constexpr bool value = V == T::first_type::value;
};
template<class T>
struct verify_tuple : std::false_type {};
template<class ...Args>
struct verify_tuple<boost::tuple<Args...>> : verify_types<0, Args...>{};
Notice handling of boost::tuples::null_type
. It is caused by the fact that boost::tuple
was created before C++11 and variadic templates.