I'm using BOOST_FUSION_ADAPT_STRUCT()
, and I need to check that all the members are declared and in the correct order. So first I did this:
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?");
}
};
And this works:
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
Next I want to make sure the order is correct, so for example (x, z, y)
in the above example should fail to compile. But so far I've only figured out a runtime solution (added to check()
):
const Sequence* dummy = nullptr;
++dummy;
boost::fusion::for_each(*dummy, struct_offset_checker());
Using this functor:
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;
}
};
But I'd rather have a compile-time solution. Can you think of one?
The funny thing is that GCC is actually able to figure out at compile time when an exception will be thrown, if I have -Wsuggest-attribute=noreturn
- it tells me when a function which calls check()
would not return (due to the logic_error
).
If you want to try it yourself, the relevant headers are:
#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>
Using m.s.'s answer here as inspiration, I drew up a solution using Boost.MPL instead of std::make_index_sequence<>
, partly because I'm more accustomed to this style:
typedef mpl::range_c<unsigned, 0, fusion::result_of::size<Sequence>::value - 1> IndicesWithoutLast;
mpl::for_each<IndicesWithoutLast>(struct_offset_checker<Sequence>());
Using this functor:
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");
}
};