2

Я использую BOOST_FUSION_ADAPT_STRUCT(), и мне нужно проверить, что все участники объявлены и в правильном порядке. Итак, сначала я сделал это:Boost Fusion: проверять адаптированный порядок элементов структуры во время компиляции

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; 
    } 
}; 

Но я предпочел бы иметь время компиляции решения. Можете ли вы подумать об одном?

Самое смешное, что GCC на самом деле в состоянии выяснить, во время компиляции, когда будет сгенерировано исключение, если у меня есть -Wsuggest-attribute=noreturn - он говорит мне, когда функция, которая вызывает check() не вернется (из-за logic_error).

Если вы хотите попробовать это самостоятельно, соответствующие заголовки:

#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> 
+0

Ваш чек упущение не принимает во внимание отступы - http://coliru.stacked-crooked.com/a/88ff885041f95349 – Praetorian

+0

, какой стандарт C++ вы используете? можете ли вы использовать C++ 11 или C++ 14? –

+0

@ м.с .: Я использую C++ 14. –

ответ

1

Используя ответ МС здесь, как вдохновение, я разработал решение с использованием 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"); 
    } 
}; 
2

Для того, чтобы выполнить время компиляции проверки вы можете перебирать себя в constexpr способом по адаптированному последовательности с использованием std::index_sequence:

#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>()' 

live example

+1

Очень хороший ответ, я думаю, [это] (http://coliru.stacked-crooked.com/a/808f1d16fe3a29c6) также может работать. – llonesmiz

+0

@jv_: Спасибо, а также m.s. Я обратился к вашим решениям, чтобы сделать свое, что я написал в качестве ответа здесь. Немного короче, но в целом он делает то, что вы оба сделали. –