Boost Fusion:在编译时验证适应的结构成员排序
Boost Fusion: validate adapted struct member ordering at compile time
我正在使用 BOOST_FUSION_ADAPT_STRUCT()
,我需要检查所有成员的声明顺序是否正确。所以首先我这样做了:
template <typename Sequence>
struct checker
{
static void check()
{
typedef typename mpl::accumulate<Sequence, mpl::size_t<0>,
mpl::plus<mpl::_1, mpl::sizeof_<mpl::_2>>>::type total_size;
static_assert(sizeof(Sequence) == total_size::value, "omitted field?");
}
};
这有效:
struct foo
{
int x;
float y;
double z;
};
BOOST_FUSION_ADAPT_STRUCT(foo, x, y, z);
checker<foo>::check(); // fails if any field is missing
接下来我要确保顺序是正确的,所以例如上面例子中的(x, z, y)
应该编译失败。但到目前为止我只想出了一个运行时解决方案(添加到check()
):
const Sequence* dummy = nullptr;
++dummy;
boost::fusion::for_each(*dummy, struct_offset_checker());
使用这个仿函数:
struct struct_offset_checker
{
mutable const void* _last = nullptr;
template <typename Element>
void operator()(const Element& element) const
{
if (&element <= _last)
throw std::logic_error("struct member is declared in a different order");
_last = &element;
}
};
但我更希望有一个 compile-time 解决方案。你能想到一个吗?
有趣的是,如果我有 -Wsuggest-attribute=noreturn
,GCC 实际上能够在编译时找出何时抛出异常 - 它告诉我调用 check()
的函数何时会抛出异常不是 return(由于 logic_error
)。
如果你想自己尝试,相关的headers是:
#include <stdexcept>
#include <boost/fusion/adapted.hpp>
#include <boost/mpl/accumulate.hpp>
#include <boost/mpl/plus.hpp>
#include <boost/mpl/sizeof.hpp>
#include <boost/mpl/size_t.hpp>
为了执行编译时检查,您可以使用 std::index_sequence
:
以 constexpr
方式遍历改编后的序列
#include <boost/fusion/adapted.hpp>
#include <boost/fusion/include/at.hpp>
struct foo
{
char c;
int x;
float y;
double z;
};
BOOST_FUSION_ADAPT_STRUCT(foo, x, c, y, z)
template <typename Sequence>
struct check_order
{
template <std::size_t First, std::size_t Second>
static constexpr bool internal()
{
constexpr Sequence* s = nullptr;
const void* first = &boost::fusion::at_c<First>(*s);
const void* second = &boost::fusion::at_c<Second>(*s);
if (second <= first)
{
throw std::logic_error("struct member is declared in a different order");
}
return true;
}
template <std::size_t... Is>
static constexpr bool run(std::index_sequence<Is...>)
{
int list[] = {(internal<Is,Is+1>(),0)...};
(void)list;
return true;
}
static constexpr void check()
{
constexpr std::size_t size = boost::fusion::result_of::size<Sequence>::type::value;
static_assert(run(std::make_index_sequence<size-1>{}), "");
}
};
int main()
{
check_order<foo>::check();
}
根据需要,这失败了:
main.cpp: In instantiation of 'static constexpr void check_order<Sequence>::check() [with Sequence = foo]':
main.cpp:49:23: required from here
main.cpp:42:9: error: non-constant condition for static assertion
static_assert(run(std::make_index_sequence<size-1>{}), "");
^~~~~~~~~~~~~
main.cpp:42:26: in constexpr expansion of 'check_order<Sequence>::run<{0ul, 1ul, 2ul}>((std::make_index_sequence<3ul>{}, std::make_index_sequence<3ul>()))'
main.cpp:34:41: in constexpr expansion of 'check_order<Sequence>::internal<0ul, 1ul>()'
使用 m.s. 在这里的回答作为灵感,我使用 Boost.MPL 而不是 std::make_index_sequence<>
制定了一个解决方案,部分原因是我更习惯这种风格:
typedef mpl::range_c<unsigned, 0, fusion::result_of::size<Sequence>::value - 1> IndicesWithoutLast;
mpl::for_each<IndicesWithoutLast>(struct_offset_checker<Sequence>());
使用这个仿函数:
template <typename Sequence>
struct struct_offset_checker
{
template <typename Index>
constexpr void operator()(Index)
{
typedef typename mpl::plus<Index, mpl::int_<1>>::type NextIndex;
constexpr Sequence* dummy = nullptr;
constexpr void* curr = &fusion::at<Index>(*dummy);
constexpr void* next = &fusion::at<NextIndex>(*dummy);
static_assert(curr < next, "fields are out of order");
}
};
我正在使用 BOOST_FUSION_ADAPT_STRUCT()
,我需要检查所有成员的声明顺序是否正确。所以首先我这样做了:
template <typename Sequence>
struct checker
{
static void check()
{
typedef typename mpl::accumulate<Sequence, mpl::size_t<0>,
mpl::plus<mpl::_1, mpl::sizeof_<mpl::_2>>>::type total_size;
static_assert(sizeof(Sequence) == total_size::value, "omitted field?");
}
};
这有效:
struct foo
{
int x;
float y;
double z;
};
BOOST_FUSION_ADAPT_STRUCT(foo, x, y, z);
checker<foo>::check(); // fails if any field is missing
接下来我要确保顺序是正确的,所以例如上面例子中的(x, z, y)
应该编译失败。但到目前为止我只想出了一个运行时解决方案(添加到check()
):
const Sequence* dummy = nullptr;
++dummy;
boost::fusion::for_each(*dummy, struct_offset_checker());
使用这个仿函数:
struct struct_offset_checker
{
mutable const void* _last = nullptr;
template <typename Element>
void operator()(const Element& element) const
{
if (&element <= _last)
throw std::logic_error("struct member is declared in a different order");
_last = &element;
}
};
但我更希望有一个 compile-time 解决方案。你能想到一个吗?
有趣的是,如果我有 -Wsuggest-attribute=noreturn
,GCC 实际上能够在编译时找出何时抛出异常 - 它告诉我调用 check()
的函数何时会抛出异常不是 return(由于 logic_error
)。
如果你想自己尝试,相关的headers是:
#include <stdexcept>
#include <boost/fusion/adapted.hpp>
#include <boost/mpl/accumulate.hpp>
#include <boost/mpl/plus.hpp>
#include <boost/mpl/sizeof.hpp>
#include <boost/mpl/size_t.hpp>
为了执行编译时检查,您可以使用 std::index_sequence
:
constexpr
方式遍历改编后的序列
#include <boost/fusion/adapted.hpp>
#include <boost/fusion/include/at.hpp>
struct foo
{
char c;
int x;
float y;
double z;
};
BOOST_FUSION_ADAPT_STRUCT(foo, x, c, y, z)
template <typename Sequence>
struct check_order
{
template <std::size_t First, std::size_t Second>
static constexpr bool internal()
{
constexpr Sequence* s = nullptr;
const void* first = &boost::fusion::at_c<First>(*s);
const void* second = &boost::fusion::at_c<Second>(*s);
if (second <= first)
{
throw std::logic_error("struct member is declared in a different order");
}
return true;
}
template <std::size_t... Is>
static constexpr bool run(std::index_sequence<Is...>)
{
int list[] = {(internal<Is,Is+1>(),0)...};
(void)list;
return true;
}
static constexpr void check()
{
constexpr std::size_t size = boost::fusion::result_of::size<Sequence>::type::value;
static_assert(run(std::make_index_sequence<size-1>{}), "");
}
};
int main()
{
check_order<foo>::check();
}
根据需要,这失败了:
main.cpp: In instantiation of 'static constexpr void check_order<Sequence>::check() [with Sequence = foo]':
main.cpp:49:23: required from here
main.cpp:42:9: error: non-constant condition for static assertion
static_assert(run(std::make_index_sequence<size-1>{}), "");
^~~~~~~~~~~~~
main.cpp:42:26: in constexpr expansion of 'check_order<Sequence>::run<{0ul, 1ul, 2ul}>((std::make_index_sequence<3ul>{}, std::make_index_sequence<3ul>()))'
main.cpp:34:41: in constexpr expansion of 'check_order<Sequence>::internal<0ul, 1ul>()'
使用 m.s. 在这里的回答作为灵感,我使用 Boost.MPL 而不是 std::make_index_sequence<>
制定了一个解决方案,部分原因是我更习惯这种风格:
typedef mpl::range_c<unsigned, 0, fusion::result_of::size<Sequence>::value - 1> IndicesWithoutLast;
mpl::for_each<IndicesWithoutLast>(struct_offset_checker<Sequence>());
使用这个仿函数:
template <typename Sequence>
struct struct_offset_checker
{
template <typename Index>
constexpr void operator()(Index)
{
typedef typename mpl::plus<Index, mpl::int_<1>>::type NextIndex;
constexpr Sequence* dummy = nullptr;
constexpr void* curr = &fusion::at<Index>(*dummy);
constexpr void* next = &fusion::at<NextIndex>(*dummy);
static_assert(curr < next, "fields are out of order");
}
};