Commit 00583bb1 authored by Jan Koniarik's avatar Jan Koniarik
Browse files

another stage of porting protocols: now the complex protocol definition works...

another stage of porting protocols: now the complex protocol definition works and the basic lib is ported
parent 429f5bfb
Pipeline #98359 passed with stage
in 49 seconds
......@@ -291,8 +291,8 @@ template <
template < container LhContainer, container RhContainer, typename BinaryFunction >
constexpr void for_cross_joint( LhContainer&& lh_cont, RhContainer&& rh_cont, BinaryFunction&& f )
{
for_each( lh_cont, [&]( auto& lh_item ) { //
for_each( rh_cont, [&]( auto& rh_item ) { //
for_each( lh_cont, [&]( auto& lh_item ) {
for_each( rh_cont, [&]( auto& rh_item ) {
f( lh_item, rh_item );
} );
} );
......@@ -449,6 +449,16 @@ constexpr void for_each_index( NullFunction&& f )
}
}
template < std::size_t i, typename NullFunction >
constexpr bool until_index( NullFunction&& f )
{
if constexpr ( i != 0 ) {
return until_index< i - 1 >( f ) || f.template operator()< i - 1 >();
} else {
return false;
}
}
struct uncurry_impl
{
template < typename Callable >
......
......@@ -61,17 +61,11 @@ enum protocol_endianess_enum
PROTOCOL_LITTLE_ENDIAN,
PROTOCOL_PARENT_ENDIAN
};
// whe able to move to GCC11: using enum protocol_endianess_enum; and make it enum class
// when able to move to GCC11: using enum protocol_endianess_enum; and make it enum class
template < typename >
inline constexpr protocol_endianess_enum protocol_endianess = PROTOCOL_PARENT_ENDIAN;
template < typename... Ts >
struct protocol_tuple
{
using value_type = std::tuple< Ts... >;
};
template < typename... Ts >
struct protocol_group
{
......@@ -203,32 +197,4 @@ struct protocol_item_decl< protocol_group< Ts... > >
std::max< std::size_t >( { protocol_item_decl< Ts >::max_size... } );
};
#ifdef EMLABCPP_USE_STREAMS
// TODO: maybe put this in different header?
inline std::ostream& operator<<( std::ostream& os, const protocol_error_record& rec )
{
for ( char c : rec.ns ) {
os << c;
}
os << "::";
for ( char c : rec.err ) {
os << c;
}
return os << " (" << rec.byte_index << ")";
}
inline std::ostream& operator<<( std::ostream& os, const protocol_endianess_enum& val )
{
switch ( val ) {
case PROTOCOL_BIG_ENDIAN:
return os << "big endian";
case PROTOCOL_LITTLE_ENDIAN:
return os << "little endian";
case PROTOCOL_PARENT_ENDIAN:
return os << "parent's endian";
}
return os;
}
#endif
} // namespace emlabcpp
......@@ -19,34 +19,41 @@ struct protocol_command
template < typename... NewArgs >
using with_args = protocol_command< ID, Args..., NewArgs... >;
constexpr static value_type
make_val( const typename protocol_item_decl< Args >::value_type&... args )
{
return { tag< ID >{}, args... };
}
};
template < typename... Cmds >
struct protocol_command_group
{
static_assert( are_same_v< typename Cmds::id_type... > );
static_assert(
are_same_v< typename Cmds::id_type... >,
"Each command of one group has to use same type of id" );
using cmds_type = std::tuple< Cmds... >;
using def_type = protocol_group< typename Cmds::def_type... >;
using pitem_decl = protocol_item_decl< def_type >;
using value_type = typename pitem_decl::value_type;
using id_type = typename std::tuple_element_t< 0, cmds_type >::id_type;
static constexpr std::size_t max_size = protocol_item_decl< def_type >::max_size;
using message_type = protocol_message< max_size >;
// TODO: that ID type should be known
// TODO: make proper static check that at least one cmd matches the ID
template < auto id, typename... Args >
static value_type make_val( Args... args )
template < id_type id, typename... Args >
requires( ( id == Cmds::id ) || ... ) constexpr static value_type
make_val( const Args&... args )
{
std::optional< value_type > res;
for_each_index< sizeof...( Cmds ) >( [&]< std::size_t i >() {
using cmd = std::tuple_element_t< i, std::tuple< Cmds... > >;
using T = typename cmd::value_type;
using cmd = std::tuple_element_t< i, cmds_type >;
if constexpr ( cmd::id == id ) {
res.emplace( T{ tag< id >{}, args... } );
res.emplace( cmd::make_val( args... ) );
}
} );
......
......@@ -156,6 +156,7 @@ struct protocol_item< std::tuple< Ts... >, Endianess > : protocol_item_decl< std
static constexpr std::size_t min_size =
( protocol_subitem< Ts, Endianess >::size_type::min_val + ... );
using def_type = std::tuple< Ts... >;
using size_type = bounded< std::size_t, min_size, max_size >;
static constexpr size_type
......@@ -165,7 +166,7 @@ struct protocol_item< std::tuple< Ts... >, Endianess > : protocol_item_decl< std
for_each_index< sizeof...( Ts ) >( [&]< std::size_t i >() {
using sub_item =
protocol_subitem< std::tuple_element_t< i, value_type >, Endianess >;
protocol_subitem< std::tuple_element_t< i, def_type >, Endianess >;
std::span< uint8_t, sub_item::max_size > sub_view{
iter, sub_item::max_size };
......@@ -191,7 +192,7 @@ struct protocol_item< std::tuple< Ts... >, Endianess > : protocol_item_decl< std
return;
}
using T = std::tuple_element_t< i, value_type >;
using T = std::tuple_element_t< i, def_type >;
using sub_item = protocol_subitem< T, Endianess >;
sub_item::deserialize( view{ iter, buffer.end() } )
......@@ -225,8 +226,9 @@ struct protocol_item< std::variant< Ts... >, Endianess >
using protocol_item_decl< std::variant< Ts... > >::max_size;
using typename protocol_item_decl< std::variant< Ts... > >::value_type;
using id_type = uint8_t;
using id_item = protocol_subitem< id_type, Endianess >;
using def_type = std::variant< Ts... >;
using id_type = uint8_t;
using id_item = protocol_subitem< id_type, Endianess >;
static_assert( sizeof...( Ts ) < std::numeric_limits< id_type >::max() );
......@@ -242,19 +244,26 @@ struct protocol_item< std::variant< Ts... >, Endianess >
// TODO: maybe we should do serialization of id properly?
buffer[0] = static_cast< id_type >( item.index() );
return std::visit(
[&]< typename T >( const T& val ) -> size_type {
using subitem = protocol_subitem< T, Endianess >;
// this also asserts that id has static serialized size
return bounded_constant< id_item::max_size > +
subitem::serialize_at(
buffer.template subspan<
id_item::max_size,
subitem::max_size >(),
val );
},
item );
std::optional< size_type > opt_res;
until_index< sizeof...( Ts ) >( [&]< std::size_t i >() {
if ( i != item.index() ) {
return false;
}
using subitem = protocol_subitem<
std::variant_alternative_t< i, def_type >,
Endianess >;
// this also asserts that id has static serialized size
opt_res =
bounded_constant< id_item::max_size > +
subitem::serialize_at(
buffer.template subspan< id_item::max_size, subitem::max_size >(),
std::get< i >( item ) );
return true;
} );
EMLABCPP_ASSERT( opt_res );
return *opt_res;
}
static constexpr auto deserialize( const view< const uint8_t* >& buffer )
......@@ -265,11 +274,11 @@ struct protocol_item< std::variant< Ts... >, Endianess >
protocol_error_record{ PROTOCOL_NS, UNDEFVAR_ERR, 0 } };
auto item_view = tail( buffer );
for_each_index< sizeof...( Ts ) >( [&]< std::size_t i >() {
using T = std::variant_alternative_t< i, value_type >;
until_index< sizeof...( Ts ) >( [&]< std::size_t i >() {
using T = std::variant_alternative_t< i, def_type >;
if ( buffer[0] != i ) {
return;
return false;
}
protocol_subitem< T, Endianess >::deserialize( item_view )
......@@ -283,6 +292,7 @@ struct protocol_item< std::variant< Ts... >, Endianess >
rec.byte_index += 1;
res = rec;
} );
return true;
} );
return res;
......@@ -385,7 +395,6 @@ struct protocol_item< protocol_offset< T, Offset >, Endianess >
return sub_item::serialize_at( buffer, item + Offset );
}
// TODO: maybe this class could check sanity more?
static constexpr auto deserialize( const view< const uint8_t* >& buffer )
-> either< protocol_result< size_type, value_type >, protocol_error_record >
{
......@@ -559,6 +568,7 @@ struct protocol_item< tag< V >, Endianess > : protocol_item_decl< tag< V > >
}
};
// TODO: 'group' is not tested
template < typename... Ts, protocol_endianess_enum Endianess >
struct protocol_item< protocol_group< Ts... >, Endianess >
: protocol_item_decl< protocol_group< Ts... > >
......@@ -569,18 +579,27 @@ struct protocol_item< protocol_group< Ts... >, Endianess >
static constexpr std::size_t min_size =
std::min( { protocol_subitem< Ts, Endianess >::size_type::min_val... } );
using size_type = bounded< std::size_t, min_size, max_size >;
using def_variant = std::variant< Ts... >;
using size_type = bounded< std::size_t, min_size, max_size >;
static constexpr size_type
serialize_at( std::span< uint8_t, max_size > buffer, const value_type& item )
{
return std::visit(
[&]< typename T >( const T& val ) -> size_type {
using subitem = protocol_subitem< T, Endianess >;
return subitem::serialize_at(
buffer.template subspan< 0, subitem::max_size >(), val );
},
item );
std::optional< size_type > opt_res;
until_index< sizeof...( Ts ) >( [&]< std::size_t i >() {
if ( i != item.index() ) {
return false;
}
using subitem = protocol_subitem<
std::variant_alternative_t< i, def_variant >,
Endianess >;
opt_res = subitem::serialize_at(
buffer.template subspan< 0, subitem::max_size >(),
std::get< i >( item ) );
return true;
} );
EMLABCPP_ASSERT( opt_res );
return *opt_res;
}
static constexpr auto deserialize( const view< const uint8_t* >& buffer )
......@@ -588,13 +607,9 @@ struct protocol_item< protocol_group< Ts... >, Endianess >
{
std::optional< protocol_result< size_type, value_type > > opt_res;
for_each_index< sizeof...( Ts ) >( [&]< std::size_t i >() {
if ( opt_res ) {
return;
}
until_index< sizeof...( Ts ) >( [&]< std::size_t i >() {
using sub_item = protocol_subitem<
std::variant_alternative_t< i, value_type >,
std::variant_alternative_t< i, def_variant >,
Endianess >;
sub_item::deserialize( buffer ).match(
......@@ -602,6 +617,7 @@ struct protocol_item< protocol_group< Ts... >, Endianess >
opt_res.emplace( sub_res.used, value_type{ sub_res.val } );
},
[&]( protocol_error_record ) {} );
return bool( opt_res );
} );
if ( opt_res ) {
......
......@@ -2,28 +2,33 @@
#include "emlabcpp/protocol/base.h"
#ifdef EMLABCPP_USE_MAGIC_ENUM
#include "magic_enum.hpp"
#endif
#pragma once
namespace emlabcpp
{
inline std::ostream& operator<<( std::ostream& os, const protocol_error_record& rec )
{
return os << em::view{ rec.os } << "::" << em::view{ rec.err } << "::" << rec.byte_index;
for ( char c : rec.ns ) {
os << c;
}
os << "::";
for ( char c : rec.err ) {
os << c;
}
return os << " (" << rec.byte_index << ")";
}
template < auto ID >
inline std::ostream& operator<<( std::ostream& os, protocol_cmd_tag< ID > )
inline std::ostream& operator<<( std::ostream& os, const protocol_endianess_enum& val )
{
#ifdef EMLABCPP_USE_MAGIC_ENUM
return os << magic_enum::enum_name( ID );
#else
return os << ID;
#endif
switch ( val ) {
case PROTOCOL_BIG_ENDIAN:
return os << "big endian";
case PROTOCOL_LITTLE_ENDIAN:
return os << "little endian";
case PROTOCOL_PARENT_ENDIAN:
return os << "parent's endian";
}
return os;
}
} // namespace emlabcpp
......
#pragma once
namespace emlabcpp
{
template < typename... Ts >
struct protocol_tuple
{
using def_type = std::tuple< Ts... >;
using item_decl = protocol_item_decl< def_type >;
static constexpr std::size_t max_size = item_decl::max_size;
using value_type = typename item_decl::value_type;
using message_type = protocol_message< max_size >;
constexpr static value_type
make_val( const typename protocol_item_decl< Ts >::value_type&... args )
{
return value_type{ args... };
}
};
} // namespace emlabcpp
#include "emlabcpp/concepts.h"
#include "emlabcpp/types/base.h"
#ifdef EMLABCPP_USE_STREAMS
#include <ostream>
#endif
#pragma once
namespace emlabcpp
......@@ -48,4 +52,12 @@ struct tag
friend constexpr auto operator<=>( const tag&, const tag& ) = default;
};
#ifdef EMLABCPP_USE_STREAMS
template < auto ID >
inline std::ostream& operator<<( std::ostream& os, tag< ID > )
{
return os << ID;
}
#endif
} // namespace emlabcpp
#include "emlabcpp/iterators/convert.h"
#include "emlabcpp/protocol/item.h"
#include "emlabcpp/protocol/streams.h"
#include "util.h"
#include <gtest/gtest.h>
......
#include "emlabcpp/iterators/convert.h"
#include "emlabcpp/protocol/command_group.h"
#include "emlabcpp/protocol/handler.h"
#include "emlabcpp/protocol/tuple.h"
#include "util.h"
#include <gtest/gtest.h>
......@@ -32,6 +33,7 @@ enum compl_ids : uint16_t
CI = 23,
CJ = 24
};
using test_quantity = tagged_quantity< struct vtag, uint32_t >;
// TODO: test changed endinaess in one subitem
// TODO: add subprotocol
struct complex_group
......@@ -44,13 +46,17 @@ struct complex_group
protocol_command< CE >::with_args< std::variant< uint32_t, std::bitset< 13 > > >,
protocol_command< CF >::with_args< uint32_t, protocol_sizeless_message< 16 > >,
protocol_command< CG >::with_args< uint32_t, protocol_offset< uint8_t, 2 > >,
protocol_command< CH >::with_args< tagged_quantity< struct vtag, uint32_t >, uint16_t >,
protocol_command< CH >::with_args< test_quantity, uint16_t >,
protocol_command< CI >::
with_args< protocol_sized_buffer< uint8_t, protocol_sizeless_message< 8 > >, uint32_t >,
protocol_command< CJ >::with_args< protocol_group< uint32_t, uint8_t > > >
{
};
struct test_tuple : protocol_tuple< uint32_t, uint16_t, std::bitset< 13 >, uint32_t >
{
};
template < typename Group >
struct valid_test_case : protocol_test_fixture
{
......@@ -117,14 +123,41 @@ int main( int argc, char** argv )
{ 0, 4, 0, 2, 2, 154, 0, 42 } ),
make_valid_test_case< complex_group >(
complex_group::make_val< CD >(
std::tuple< uint32_t, uint8_t >{ 34567u, 255u }, -666, 666, 42, 9 ),
std::tuple< uint32_t, uint8_t >{ 34567u, 255u },
static_cast< int16_t >( -666 ),
static_cast< uint32_t >( 666 ),
static_cast< uint8_t >( 42 ),
static_cast< uint8_t >( 9 ) ),
{ 0, 9, 0, 0, 135, 7, 255, 253, 102, 0, 0, 2, 154, 42, 9 } ),
make_valid_test_case< complex_group >(
complex_group::make_val< CE >( 66u ), { 0, 13, 0, 0, 0, 0, 66 } ),
make_valid_test_case< complex_group >(
complex_group::make_val< CE >( std::bitset< 13 >{ 42 } ), { 0, 13, 1, 42, 0 } )
};
complex_group::make_val< CE >( std::bitset< 13 >{ 42 } ), { 0, 13, 1, 42, 0 } ),
make_valid_test_case< complex_group >(
complex_group::make_val< CF >(
666u,
*protocol_sizeless_message< 16 >::make( std::vector{ 1, 2, 3, 4, 5, 6 } ) ),
{ 0, 15, 0, 0, 2, 154, 1, 2, 3, 4, 5, 6 } ),
make_valid_test_case< complex_group >(
complex_group::make_val< CG >( 23456u, static_cast< uint8_t >( 8 ) ),
{ 0, 21, 0, 0, 91, 160, 10 } ),
make_valid_test_case< complex_group >(
complex_group::make_val< CH >(
test_quantity{ 2348974582u }, static_cast< uint16_t >( 666u ) ),
{ 0, 22, 140, 2, 129, 246, 2, 154 } ),
make_valid_test_case< complex_group >(
complex_group::make_val< CI >(
*protocol_sizeless_message< 8 >::make( std::vector{ 1, 2, 3, 4, 5 } ),
39439483u ),
{ 0, 23, 5, 1, 2, 3, 4, 5, 2, 89, 204, 123 } ),
make_valid_test_case< complex_group >(
complex_group::make_val< CJ >( static_cast< uint32_t >( 666 ) ),
{ 0, 24, 0, 0, 2, 154 } ),
make_valid_test_case< complex_group >(
complex_group::make_val< CJ >( static_cast< uint8_t >( 42 ) ), { 0, 24, 42 } ),
make_valid_test_case< test_tuple >(
test_tuple::make_val( 23657453, 666, std::bitset< 13 >{ 42 }, 6634343 ),
{ 1, 104, 251, 237, 2, 154, 42, 0, 0, 101, 59, 103 } ) };
exec_protocol_test_fixture_test( tests );
return RUN_ALL_TESTS();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment