验证(使用 static_assert)元组类型是否遵循某种顺序(状态编译时检查)
Verify (with static_assert) that tuple types follow some order (stateful compile-time check)
对于一些比较老的软件中的序列化应用,我有这样的类型:
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;
这些常数是static constexpr
,代表某种数据库table,向量是某种存储类型(细节不重要)。
为了使这个 "kind of" 类型安全,并让所有东西在未来协同工作,我需要确保向元组添加另一种类型的用户遵循序列号。所以另一个元素应该是这个:
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;
这是错误的,不应编译:
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;
到目前为止,我的解决方案 失败了,非常感谢有人对此提出意见:
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...>();
}
};
所以你在上面看到的是我创建了一个状态 (Count
) 并且我在每次迭代时都增加了计数。但这不知何故达到了错误的状态,当我在这个调用中使用它时 static_assert()
会触发:
tuple_assert<T>::verify(); // `T` is the type I mentioned at the very beginning
See my solution that doesn't work online.
我做错了什么?
您的代码有几处错误。第一个在这一行中,模板参数列表扩展中的错字:
__verify<std::integral_constant<uint32_t, Pair::first_type::value + 1>,
Ts...>();
应该是:
__verify<std::integral_constant<uint32_t, Pair::first_type::value + 1>,
Ts2...>();
但遗憾的是它无法修复它。 boost::tuple
里面有一些奇怪的类型定义和一些 nulltype_t
。将其更改为 std::tuple
仍然无法修复它,因为您的 __verify
函数调用不明确。所以这里是我的解决方案。
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
这个有点复杂。
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...>{};
通知处理boost::tuples::null_type
。这是由于 boost::tuple
是在 C++11 和可变参数模板之前创建的。
来不及玩了?
抱歉,但是...您为什么不简单地使用模板推导?
类似于
template <typename>
struct check_tuple;
template <template <typename...> class Tpl, std::uint32_t ... Is,
typename ... Ts>
struct check_tuple<Tpl<
std::pair<std::integral_constant<std::uint32_t, Is>, Ts>...>>
: public std::is_same<
std::integer_sequence<std::uint32_t, Is...>,
std::make_integer_sequence<std::uint32_t, sizeof...(Is)>>
{ };
观察模板模板参数的使用允许将其与 std::tuple
以及 boost::tuple
. 一起使用 [错误:请参阅以下编辑]
下面是一个完整的编译C++14的例子
#include <tuple>
#include <vector>
using T1 = std::tuple<
std::pair<std::integral_constant<std::uint32_t, 0>, std::vector<int>>,
std::pair<std::integral_constant<std::uint32_t, 1>, std::vector<double>>,
std::pair<std::integral_constant<std::uint32_t, 2>, std::vector<float>>
>;
using T2 = std::tuple<
std::pair<std::integral_constant<std::uint32_t, 0>, std::vector<int>>,
std::pair<std::integral_constant<std::uint32_t, 1>, std::vector<double>>,
std::pair<std::integral_constant<std::uint32_t, 1>, std::vector<float>>
>;
template <typename>
struct check_tuple;
template <template <typename...> class Tpl, std::uint32_t ... Is,
typename ... Ts>
struct check_tuple<Tpl<
std::pair<std::integral_constant<std::uint32_t, Is>, Ts>...>>
: public std::is_same<
std::integer_sequence<std::uint32_t, Is...>,
std::make_integer_sequence<std::uint32_t, sizeof...(Is)>>
{ };
int main()
{
static_assert( check_tuple<T1>::value == true, "!" );
static_assert( check_tuple<T2>::value == false, "!" );
}
-- 编辑 --
我错了:boost::tuple
没有被定义为 std::tuple
,接收到一个可变的模板类型列表,但是有一个固定数量(10,但应该可以修改它)的类型模板参数,默认为boost::tuples::null_type
.
因此,举例来说,
using T1 = boost::tuple<
std::pair<std::integral_constant<std::uint32_t, 0>, std::vector<int>>,
std::pair<std::integral_constant<std::uint32_t, 1>, std::vector<double>>,
std::pair<std::integral_constant<std::uint32_t, 2>, std::vector<float>>
>;
有三个std::pair
和七个boost::tuples::null_type
。
换句话说,T1是
using T1 = boost::tuple<
std::pair<std::integral_constant<std::uint32_t, 0>, std::vector<int>>,
std::pair<std::integral_constant<std::uint32_t, 1>, std::vector<double>>,
std::pair<std::integral_constant<std::uint32_t, 2>, std::vector<float>>,
boost::tuples::null_type,
boost::tuples::null_type,
boost::tuples::null_type,
boost::tuples::null_type,
boost::tuples::null_type,
boost::tuples::null_type,
boost::tuples::null_type
>;
这打破了我之前的解决方案,因为
template <template <typename...> class Tpl, std::uint32_t ... Is,
typename ... Ts>
struct check_tuple<Tpl<
std::pair<std::integral_constant<std::uint32_t, Is>, Ts>...>>
不拦截七强boost::tuples::null_type
.
我能想到的解决此问题的最佳方法是 boost::tuple
与 std::tuple
类型转换,删除 boost::tuples::null_type
类型。
我是说
template <typename T>
struct get_tuple
{ using type = std::tuple<T>; } ;
template <>
struct get_tuple<boost::tuples::null_type>
{ using type = std::tuple<>; };
template <typename ... Ts>
auto convert_tuple (boost::tuple<Ts...>)
-> decltype( std::tuple_cat(std::declval<typename get_tuple<Ts>::type>()...) );
现在check_tuple
可以改写如下
template <typename T,
typename = decltype(convert_tuple(std::declval<T>()))>
struct check_tuple;
template <typename BT, std::uint32_t ... Is, typename ... Ts>
struct check_tuple<BT, std::tuple<
std::pair<std::integral_constant<std::uint32_t, Is>, Ts>...>>
: public std::is_same<
std::integer_sequence<std::uint32_t, Is...>,
std::make_integer_sequence<std::uint32_t, sizeof...(Is)>>
{ };
以下是使用 boost::tuple
的完整编译 C++14 示例
#include <tuple>
#include <vector>
#include "boost/tuple/tuple.hpp"
using T1 = boost::tuple<
std::pair<std::integral_constant<std::uint32_t, 0>, std::vector<int>>,
std::pair<std::integral_constant<std::uint32_t, 1>, std::vector<double>>,
std::pair<std::integral_constant<std::uint32_t, 2>, std::vector<float>>
>;
using T2 = boost::tuple<
std::pair<std::integral_constant<std::uint32_t, 0>, std::vector<int>>,
std::pair<std::integral_constant<std::uint32_t, 1>, std::vector<double>>,
std::pair<std::integral_constant<std::uint32_t, 1>, std::vector<float>>
>;
template <typename T>
struct get_tuple
{ using type = std::tuple<T>; } ;
template <>
struct get_tuple<boost::tuples::null_type>
{ using type = std::tuple<>; };
template <typename ... Ts>
auto convert_tuple (boost::tuple<Ts...>)
-> decltype( std::tuple_cat(std::declval<typename get_tuple<Ts>::type>()...) );
template <typename T,
typename = decltype(convert_tuple(std::declval<T>()))>
struct check_tuple;
template <typename BT, std::uint32_t ... Is, typename ... Ts>
struct check_tuple<BT, std::tuple<
std::pair<std::integral_constant<std::uint32_t, Is>, Ts>...>>
: public std::is_same<
std::integer_sequence<std::uint32_t, Is...>,
std::make_integer_sequence<std::uint32_t, sizeof...(Is)>>
{ };
int main()
{
static_assert( check_tuple<T1>::value == true, "!" );
static_assert( check_tuple<T2>::value == false, "!" );
}
对于一些比较老的软件中的序列化应用,我有这样的类型:
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;
这些常数是static constexpr
,代表某种数据库table,向量是某种存储类型(细节不重要)。
为了使这个 "kind of" 类型安全,并让所有东西在未来协同工作,我需要确保向元组添加另一种类型的用户遵循序列号。所以另一个元素应该是这个:
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;
这是错误的,不应编译:
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;
到目前为止,我的解决方案 失败了,非常感谢有人对此提出意见:
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...>();
}
};
所以你在上面看到的是我创建了一个状态 (Count
) 并且我在每次迭代时都增加了计数。但这不知何故达到了错误的状态,当我在这个调用中使用它时 static_assert()
会触发:
tuple_assert<T>::verify(); // `T` is the type I mentioned at the very beginning
See my solution that doesn't work online.
我做错了什么?
您的代码有几处错误。第一个在这一行中,模板参数列表扩展中的错字:
__verify<std::integral_constant<uint32_t, Pair::first_type::value + 1>,
Ts...>();
应该是:
__verify<std::integral_constant<uint32_t, Pair::first_type::value + 1>,
Ts2...>();
但遗憾的是它无法修复它。 boost::tuple
里面有一些奇怪的类型定义和一些 nulltype_t
。将其更改为 std::tuple
仍然无法修复它,因为您的 __verify
函数调用不明确。所以这里是我的解决方案。
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
这个有点复杂。
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...>{};
通知处理boost::tuples::null_type
。这是由于 boost::tuple
是在 C++11 和可变参数模板之前创建的。
来不及玩了?
抱歉,但是...您为什么不简单地使用模板推导?
类似于
template <typename>
struct check_tuple;
template <template <typename...> class Tpl, std::uint32_t ... Is,
typename ... Ts>
struct check_tuple<Tpl<
std::pair<std::integral_constant<std::uint32_t, Is>, Ts>...>>
: public std::is_same<
std::integer_sequence<std::uint32_t, Is...>,
std::make_integer_sequence<std::uint32_t, sizeof...(Is)>>
{ };
观察模板模板参数的使用允许将其与 一起使用 [错误:请参阅以下编辑] std::tuple
以及 boost::tuple
.
下面是一个完整的编译C++14的例子
#include <tuple>
#include <vector>
using T1 = std::tuple<
std::pair<std::integral_constant<std::uint32_t, 0>, std::vector<int>>,
std::pair<std::integral_constant<std::uint32_t, 1>, std::vector<double>>,
std::pair<std::integral_constant<std::uint32_t, 2>, std::vector<float>>
>;
using T2 = std::tuple<
std::pair<std::integral_constant<std::uint32_t, 0>, std::vector<int>>,
std::pair<std::integral_constant<std::uint32_t, 1>, std::vector<double>>,
std::pair<std::integral_constant<std::uint32_t, 1>, std::vector<float>>
>;
template <typename>
struct check_tuple;
template <template <typename...> class Tpl, std::uint32_t ... Is,
typename ... Ts>
struct check_tuple<Tpl<
std::pair<std::integral_constant<std::uint32_t, Is>, Ts>...>>
: public std::is_same<
std::integer_sequence<std::uint32_t, Is...>,
std::make_integer_sequence<std::uint32_t, sizeof...(Is)>>
{ };
int main()
{
static_assert( check_tuple<T1>::value == true, "!" );
static_assert( check_tuple<T2>::value == false, "!" );
}
-- 编辑 --
我错了:boost::tuple
没有被定义为 std::tuple
,接收到一个可变的模板类型列表,但是有一个固定数量(10,但应该可以修改它)的类型模板参数,默认为boost::tuples::null_type
.
因此,举例来说,
using T1 = boost::tuple<
std::pair<std::integral_constant<std::uint32_t, 0>, std::vector<int>>,
std::pair<std::integral_constant<std::uint32_t, 1>, std::vector<double>>,
std::pair<std::integral_constant<std::uint32_t, 2>, std::vector<float>>
>;
有三个std::pair
和七个boost::tuples::null_type
。
换句话说,T1是
using T1 = boost::tuple<
std::pair<std::integral_constant<std::uint32_t, 0>, std::vector<int>>,
std::pair<std::integral_constant<std::uint32_t, 1>, std::vector<double>>,
std::pair<std::integral_constant<std::uint32_t, 2>, std::vector<float>>,
boost::tuples::null_type,
boost::tuples::null_type,
boost::tuples::null_type,
boost::tuples::null_type,
boost::tuples::null_type,
boost::tuples::null_type,
boost::tuples::null_type
>;
这打破了我之前的解决方案,因为
template <template <typename...> class Tpl, std::uint32_t ... Is,
typename ... Ts>
struct check_tuple<Tpl<
std::pair<std::integral_constant<std::uint32_t, Is>, Ts>...>>
不拦截七强boost::tuples::null_type
.
我能想到的解决此问题的最佳方法是 boost::tuple
与 std::tuple
类型转换,删除 boost::tuples::null_type
类型。
我是说
template <typename T>
struct get_tuple
{ using type = std::tuple<T>; } ;
template <>
struct get_tuple<boost::tuples::null_type>
{ using type = std::tuple<>; };
template <typename ... Ts>
auto convert_tuple (boost::tuple<Ts...>)
-> decltype( std::tuple_cat(std::declval<typename get_tuple<Ts>::type>()...) );
现在check_tuple
可以改写如下
template <typename T,
typename = decltype(convert_tuple(std::declval<T>()))>
struct check_tuple;
template <typename BT, std::uint32_t ... Is, typename ... Ts>
struct check_tuple<BT, std::tuple<
std::pair<std::integral_constant<std::uint32_t, Is>, Ts>...>>
: public std::is_same<
std::integer_sequence<std::uint32_t, Is...>,
std::make_integer_sequence<std::uint32_t, sizeof...(Is)>>
{ };
以下是使用 boost::tuple
#include <tuple>
#include <vector>
#include "boost/tuple/tuple.hpp"
using T1 = boost::tuple<
std::pair<std::integral_constant<std::uint32_t, 0>, std::vector<int>>,
std::pair<std::integral_constant<std::uint32_t, 1>, std::vector<double>>,
std::pair<std::integral_constant<std::uint32_t, 2>, std::vector<float>>
>;
using T2 = boost::tuple<
std::pair<std::integral_constant<std::uint32_t, 0>, std::vector<int>>,
std::pair<std::integral_constant<std::uint32_t, 1>, std::vector<double>>,
std::pair<std::integral_constant<std::uint32_t, 1>, std::vector<float>>
>;
template <typename T>
struct get_tuple
{ using type = std::tuple<T>; } ;
template <>
struct get_tuple<boost::tuples::null_type>
{ using type = std::tuple<>; };
template <typename ... Ts>
auto convert_tuple (boost::tuple<Ts...>)
-> decltype( std::tuple_cat(std::declval<typename get_tuple<Ts>::type>()...) );
template <typename T,
typename = decltype(convert_tuple(std::declval<T>()))>
struct check_tuple;
template <typename BT, std::uint32_t ... Is, typename ... Ts>
struct check_tuple<BT, std::tuple<
std::pair<std::integral_constant<std::uint32_t, Is>, Ts>...>>
: public std::is_same<
std::integer_sequence<std::uint32_t, Is...>,
std::make_integer_sequence<std::uint32_t, sizeof...(Is)>>
{ };
int main()
{
static_assert( check_tuple<T1>::value == true, "!" );
static_assert( check_tuple<T2>::value == false, "!" );
}