diff --git a/SConstruct b/SConstruct index bda005d02..7f88918e8 100644 --- a/SConstruct +++ b/SConstruct @@ -431,6 +431,19 @@ int f_%(N)s() ''', ''' f_%(N)s(); +'''), + Cxx17RequiredFeature('fold expressions', ''' +static inline void g_%(N)s(int) {} +template +void f_%(N)s() +{ + (g_%(N)s(i), ...); + (g_%(N)s(i), ..., (void)0); + ((void)0, ..., g_%(N)s(i)); +} +''', +''' + f_%(N)s<0, 1, 2, 3>(); '''), Cxx17RequiredFeature('structured binding declarations', '', ''' diff --git a/common/include/serial.h b/common/include/serial.h index 771d39697..701b1e139 100644 --- a/common/include/serial.h +++ b/common/include/serial.h @@ -23,7 +23,7 @@ namespace serial { -template +template class message; template @@ -35,8 +35,8 @@ class is_message : public std::false_type { }; -template -class is_message> : public std::true_type +template +class is_message> : public std::true_type { }; @@ -90,8 +90,8 @@ static inline typename std::enable_if::value, void>::type template static typename std::enable_if::value, void>::type process_buffer(Accessor &&, A1 &); -template -static void process_buffer(Accessor &, const message &); +template +static void process_buffer(Accessor &, const message &); class endian_access { @@ -121,39 +121,10 @@ public: /* Implementation details - avoid namespace pollution */ namespace detail { - /* - * gcc before 4.8 chokes on tuple> due to ambiguous - * base class after applying EBO - */ -template -class wrapped_empty_value : T -{ -public: - wrapped_empty_value() = default; - wrapped_empty_value(T &&t) : T(std::forward(t)) {} - T &get() { return *this; } - const T &get() const { return *this; } -}; - -template -static inline T &extract_value(wrapped_empty_value &t) -{ - return t.get(); -} - -template -static inline const T &extract_value(const wrapped_empty_value &t) -{ - return t.get(); -} - template ::type> using capture_type = typename std::conditional::value, std::reference_wrapper, - typename std::conditional::value, - wrapped_empty_value, - std::tuple - >::type + std::tuple >; template ::type> @@ -163,13 +134,7 @@ static inline auto capture_value(Trr &t) -> decltype(std::ref(t)) } template ::type> -static inline typename std::enable_if::value, detail::wrapped_empty_value>::type capture_value(Trr &&t) -{ - return std::forward(t); -} - -template ::type> -static inline typename std::enable_if::value && std::is_rvalue_reference::value, std::tuple>::type capture_value(Trr &&t) +static inline typename std::enable_if::value, std::tuple>::type capture_value(Trr &&t) { return std::tuple{std::forward(t)}; } @@ -235,7 +200,7 @@ public: using pointer = T *; using reference = T &; // Default bytebuffer_t usage to little endian - static uint16_t endian() { return little_endian; } + static constexpr endian_access::little_endian_type endian{}; base_bytebuffer_t(pointer u) : p(u) {} operator pointer() const { return p; } D &operator++() @@ -307,8 +272,6 @@ static inline void process_udt(Accessor &&accessor, const pad_type) {} - template static inline T &extract_value(std::reference_wrapper t) { @@ -369,10 +332,10 @@ static inline detail::sign_extend_type sign_ #define ASSERT_SERIAL_UDT_MESSAGE_SIZE(T, SIZE) \ assert_equal(serial::class_type::maximum_size, SIZE, "sizeof(" #T ") is not " #SIZE) -template ::type>::type> -struct udt_message_compatible_same_type : std::is_same +template ::type>::type, T1>> +struct udt_message_compatible_same_type : base_type { - static_assert((std::is_same::value), "parameter type mismatch"); + static_assert(base_type::value, "parameter type mismatch"); }; template @@ -383,38 +346,25 @@ class assert_udt_message_compatible2 : public std::false_type { }; -template -class assert_udt_message_compatible2, std::tuple> : public udt_message_compatible_same_type -{ -}; - -template -class assert_udt_message_compatible2, std::tuple> : - public assert_udt_message_compatible2::value, message, std::tuple> +template +class assert_udt_message_compatible2, std::tuple> : + public std::integral_constant::value && ...)> { }; template -class assert_udt_message_compatible1; +class assert_udt_message_compatible; -template -class assert_udt_message_compatible1, std::tuple> : public assert_udt_message_compatible2, std::tuple> +template +class assert_udt_message_compatible, std::tuple> : public assert_udt_message_compatible2, std::tuple> { static_assert(sizeof...(Mn) <= sizeof...(Tn), "too few types in tuple"); static_assert(sizeof...(Mn) >= sizeof...(Tn), "too few types in message"); }; -template -class assert_udt_message_compatible; - template using class_type = message_type()))>; -template -class assert_udt_message_compatible> : public assert_udt_message_compatible1::as_message, std::tuple> -{ -}; - #define _SERIAL_UDT_UNWRAP_LIST(A1,...) A1, ## __VA_ARGS__ #define ASSERT_SERIAL_UDT_MESSAGE_TYPE(T, TYPELIST) \ @@ -422,7 +372,7 @@ class assert_udt_message_compatible> : public assert_ud ASSERT_SERIAL_UDT_MESSAGE_MUTABLE_TYPE(T, TYPELIST); \ #define _ASSERT_SERIAL_UDT_MESSAGE_TYPE(T, TYPELIST) \ - static_assert((serial::assert_udt_message_compatible>::value), "udt/message mismatch") + static_assert(serial::assert_udt_message_compatible::as_message, std::tuple<_SERIAL_UDT_UNWRAP_LIST TYPELIST>>::value, "udt/message mismatch") #define ASSERT_SERIAL_UDT_MESSAGE_CONST_TYPE(T, TYPELIST) \ _ASSERT_SERIAL_UDT_MESSAGE_TYPE(const T, TYPELIST) @@ -500,37 +450,33 @@ public: typedef message as_message; }; -template -class message_type> : +template +class message_type> : public detail::size_base< - message_type::maximum_size + message_type>::maximum_size, - message_type::minimum_size + message_type>::minimum_size + (0 + ... + message_dispatch_type>::effective_type::maximum_size), + (0 + ... + message_dispatch_type>::effective_type::minimum_size) > { public: - typedef message as_message; + using as_message = message; }; -template +template class message { - typedef std::tuple::type, typename detail::capture_type::type...> tuple_type; + static_assert(sizeof...(Args) > 0, "message must have at least one template argument"); + using tuple_type = std::tuple::type...>; template static void check_type() { static_assert(message_type::maximum_size > 0, "empty field in message"); } - static void check_types() - { - check_type(); - detail::sequence({(check_type(), static_cast(0))...}); - } tuple_type t; public: - message(A1 &&a1, Args &&... args) : - t(detail::capture_value(std::forward(a1)), detail::capture_value(std::forward(args))...) + message(Args &&... args) : + t(detail::capture_value(std::forward(args))...) { - check_types(); + (check_type(), ...); } const tuple_type &get_tuple() const { @@ -538,10 +484,10 @@ public: } }; -template -static inline message make_message(A1 &&a1, Args &&... args) +template +static inline message make_message(Args &&... args) { - return {std::forward(a1), std::forward(args)...}; + return {std::forward(args)...}; } #define SERIAL_DEFINE_SIZE_SPECIFIC_USWAP_BUILTIN(HBITS,BITS) \ @@ -733,24 +679,20 @@ static typename std::enable_if::value, void>::type process_buff template static inline void process_message_tuple(Accessor &&accessor, const std::tuple &t, std::index_sequence) { - detail::sequence({(process_buffer(accessor, detail::extract_value(std::get(t))), static_cast(0))...}); + (process_buffer(accessor, detail::extract_value(std::get(t))), ...); } -template -static void process_buffer(Accessor &&accessor, const message &m) +template +static void process_buffer(Accessor &&accessor, const message &m) { - process_message_tuple(std::forward(accessor), m.get_tuple(), std::make_index_sequence<1 + sizeof...(Args)>()); + process_message_tuple(std::forward(accessor), m.get_tuple(), std::make_index_sequence()); } /* Require at least two arguments to prevent self-selection */ -template -static typename std::enable_if<(sizeof...(An) > 0)>::type process_buffer(Accessor &&accessor, A1 &&a1, An &&... an) +template +static typename std::enable_if<(sizeof...(An) > 1)>::type process_buffer(Accessor &&accessor, An &&... an) { - detail::sequence({ - (process_buffer(accessor, std::forward(a1)), - static_cast(0)), - (process_buffer(accessor, std::forward(an)), static_cast(0))... - }); + (process_buffer(accessor, std::forward(an)), ...); } }