Commit aee523a3 authored by Jan Koniarik's avatar Jan Koniarik
Browse files

final refactoring of protocol lib and simple checks from clang tidy

parent ba0646b9
Pipeline #99396 failed with stage
in 1 minute and 7 seconds
......@@ -2,7 +2,7 @@
Checks: >
-*,
bugprone-*,
cppcore*, -cppcoreguidelines-owning-memory,-cppcoreguidelines-special-member-functions,-cppcoreguidelines-pro-bounds-constant-array-index,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-macro-usage,-cppcoreguidelines-avoid-c-arrays,-cppcoreguidelines-pro-type-union-access,
cppcore*, -cppcoreguidelines-owning-memory,-cppcoreguidelines-special-member-functions,-cppcoreguidelines-pro-bounds-constant-array-index,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-macro-usage,-cppcoreguidelines-avoid-c-arrays,-cppcoreguidelines-pro-type-union-access,-cppcoreguidelines-pro-type-reinterpret-cast,
performance*,
modernize*, -modernize-use-trailing-return-type,-modernize-avoid-c-arrays
readbility*,
......
......@@ -8,7 +8,7 @@ clean:
rm -rf ./build
build_test:
cmake -Bbuild $(EXTRAARGS)
cmake -Bbuild $(EXTRAARGS) -DCMAKE_EXPORT_COMPILE_COMMANDS=1
make -Cbuild -j
exec_test: build_test
......
......@@ -103,6 +103,9 @@ public:
either& operator=( either&& other ) noexcept
{
if ( this == &other ) {
return *this;
}
if ( other.id_ == item::LEFT ) {
*this = std::move( other.left_ );
} else {
......@@ -134,11 +137,12 @@ public:
return *this;
}
either& operator=( either_unique_right< LH, RH > auto&& other )
template < either_unique_right< LH, RH > T >
either& operator=( T&& other )
{
destruct();
id_ = item::RIGHT;
new ( &right_ ) right_item( std::move( other ) );
new ( &right_ ) right_item( std::forward< T >( other ) );
return *this;
}
......@@ -401,8 +405,8 @@ inline auto assemble_left_collect_right( FirstE&& first, Eithers&&... others ) r
auto convert = [&]< typename Either >( Either&& either ) {
using left_type = typename std::remove_reference_t< Either >::left_item;
return std::move( either )
.convert_left( [&]( auto item ) { //
return std::forward< Either >( either )
.convert_left( [&]( auto item ) {
return std::make_optional( std::move( item ) );
} )
.convert_right( [&]( auto item ) {
......@@ -415,7 +419,7 @@ inline auto assemble_left_collect_right( FirstE&& first, Eithers&&... others ) r
return assemble_optionals(
convert( std::forward< FirstE >( first ) ),
convert( std::forward< Eithers >( others ) )... )
.convert_right( [&]( empty_assembly_tag ) { //
.convert_right( [&]( empty_assembly_tag ) {
return collection;
} );
}
......
......@@ -20,6 +20,9 @@ struct protocol_result
T val;
};
template < bounded_derived size_type, typename T >
protocol_result( size_type, T ) -> protocol_result< size_type, T >;
// Concept that matches types considered base - serialized directly by using byte shifting.
template < typename T >
concept protocol_base_type = std::is_integral_v< T > || std::is_enum_v< T >;
......
......@@ -79,7 +79,7 @@ struct protocol_decl< protocol_sizeless_message< N > >
template < protocol_declarable D, auto Offset >
struct protocol_decl< protocol_offset< D, Offset > >
{
using def_type = protocol_offset< D, Offset >::def_type;
using def_type = typename protocol_offset< D, Offset >::def_type;
using def_decl = protocol_decl< def_type >;
using value_type = typename def_decl::value_type;
......@@ -107,7 +107,7 @@ struct protocol_decl< protocol_sized_buffer< CounterDecl, D > >
{
using counter_decl = protocol_decl< CounterDecl >;
using sub_decl = protocol_decl< D >;
using value_type = sub_decl::value_type;
using value_type = typename sub_decl::value_type;
static constexpr std::size_t max_size = counter_decl::max_size + sub_decl::max_size;
};
......
......@@ -12,9 +12,14 @@
namespace emlabcpp
{
// protocol_def<T,E> structure defines how type T should be serialized and deserialized. Each type
// or kind of types should overlead this structure and use same attributes as protocol_decl<T,E>. E
// is edianess of the serialization used.
template < typename, protocol_endianess_enum >
struct protocol_def;
// protocol_def_check<T> concept verifies that 'T' is valid overload of protocol_def. Use this in
// tests of custom protocol_def overloads.
template < typename T >
concept protocol_def_check = requires()
{
......@@ -608,7 +613,8 @@ struct protocol_def< protocol_group< Ds... >, Endianess >
sub_def::deserialize( buffer ).match(
[&]( auto sub_res ) {
opt_res.emplace( sub_res.used, value_type{ sub_res.val } );
opt_res = protocol_result< size_type, value_type >{
sub_res.used, value_type{ sub_res.val } };
},
[&]( protocol_error_record ) {} );
return bool( opt_res );
......
......@@ -9,7 +9,9 @@ namespace emlabcpp
// unique in that namespace, part of the error is also index of byte that caused the problem. These
// can be easily sent with the protocol lib itself.
using protocol_mark = std::array< char, 8 >;
struct protocol_mark : std::array< char, 8 >
{
};
struct protocol_error_record
{
......
......@@ -5,6 +5,11 @@
namespace emlabcpp
{
// protocol_handler< T > should be used to execute actual serialization and deserealization of
// protocol definition. It provides serialize/extract methods that should be used by the user.
//
// You may want to have this class (With the include) to be present in separate .cpp file, as the
// compile time can be quite heavy.
template < typename T >
struct protocol_handler
{
......@@ -14,7 +19,7 @@ struct protocol_handler
static message_type serialize( value_type val )
{
std::array< uint8_t, decl::max_size > buffer;
std::array< uint8_t, decl::max_size > buffer{};
bounded used = decl::serialize_at( buffer, val );
EMLABCPP_ASSERT( *used <= decl::max_size );
......
......@@ -9,6 +9,8 @@
namespace emlabcpp
{
// Protocol library has custom type that represents message, however this is just simple overaly
// over std::array that remembers how many bytes are used.
template < std::size_t N >
class protocol_message
{
......@@ -117,6 +119,10 @@ protected:
}
};
// Sizeless message is class that behaves in a same way as normal protocol_message, however it is
// serialized differently. Protocol message stores how many bytes it's made of before the data
// itself in the final message, sizless message does not and greedely tries to parse rest of the
// buffer during the parsing process.
template < std::size_t N >
class protocol_sizeless_message : public protocol_message< N >
{
......@@ -148,6 +154,7 @@ namespace detail
}
} // namespace detail
// concept matches any type that is protocol_message or derives from it.
template < typename T >
concept protocol_message_derived = requires( T val )
{
......
......@@ -6,6 +6,10 @@
namespace emlabcpp
{
// Handler for serialization and extraction of datatypes used by the register_map. This provides
// interface for handling conversion of bytes to types used in the map. `serialize` and `extract`
// works directly with the types used by the map, based on compile time key. `select` and `insert`
// works with the map itself based on runtime information.
template < typename Map >
struct protocol_register_handler
{
......@@ -31,7 +35,7 @@ struct protocol_register_handler
return *message_type::make( view_n( buffer.begin(), *used ) );
}
static message_type serialize( const map_type& m, key_type key )
static message_type select( const map_type& m, key_type key )
{
return m.with_register( key, [&]< typename reg_type >( const reg_type& reg ) {
return serialize< reg_type::key >( reg.value );
......@@ -49,6 +53,22 @@ struct protocol_register_handler
return sub_res.val;
} );
}
template < typename Buffer >
static std::optional< protocol_error_record >
insert( map_type& m, key_type key, Buffer&& buff )
{
std::optional< protocol_error_record > res;
m.setup_register( key, [&]< typename reg_type >() {
return extract< reg_type::key >( buff )
.convert_right( [&]( auto err ) {
res = err;
return m.template get_val< reg_type::key >();
} )
.join();
} );
return res;
}
};
} // namespace emlabcpp
......@@ -6,14 +6,14 @@
namespace emlabcpp
{
template < auto Key, typename DefType >
// Structure that represents definition of one register in the map. It also contains the value
// itself.
template < auto Key, protocol_declarable D >
struct protocol_reg
{
using key_type = decltype( Key );
static constexpr key_type key = Key;
using def_type = DefType;
using def_type = D;
using key_type = decltype( Key );
static constexpr key_type key = Key;
using decl = protocol_decl< def_type >;
using value_type = typename decl::value_type;
static constexpr std::size_t size = decl::max_size;
......@@ -21,6 +21,13 @@ struct protocol_reg
value_type value;
};
// Register map is abstraction to work with registers of external devices. It stores values of
// serializable types that can be accessed based on key (usually enum representing address of
// register in the device).You can access the value based on the key itself, both at compile time
// and at runtime. You can also iterate over the values and there is handler that allows
// serialization and deserialization of bytes into the values defined in the map. This includes
// additional information that can be accessed about the map. This can also be used as simple table
// of configuration values.
template < typename... Regs >
class protocol_register_map
{
......
......@@ -26,16 +26,17 @@ inline std::ostream& operator<<( std::ostream& os, const protocol_message< N >&
return os;
}
inline std::ostream& operator<<( std::ostream& os, const protocol_error_record& rec )
inline std::ostream& operator<<( std::ostream& os, const protocol_mark& m )
{
for ( char c : rec.ns ) {
os << c;
}
os << "::";
for ( char c : rec.err ) {
for ( char c : m ) {
os << c;
}
return os << " (" << rec.byte_index << ")";
return os;
}
inline std::ostream& operator<<( std::ostream& os, const protocol_error_record& rec )
{
return os << rec.ns << "::" << rec.err << " (" << rec.byte_index << ")";
}
inline std::ostream& operator<<( std::ostream& os, const protocol_endianess_enum& val )
......
......@@ -5,6 +5,16 @@
namespace emlabcpp
{
// protocol_tuple is high levle alternative to use just 'std::tuple' that is more friendly for
// standalone protocols. It is designed for message that are simply a set of serialized items. It
// also provieds more readable syntax. The template arguments at first configurate the protocol and
// later is follow by items present in the tuple, this can also be added with
// protocol_tuple::with_items alias that appends the items. For example:
//
// protocol_tuple< PROTOCOL_BIG_ENDIAN >::with_items< uint32_t, uint32_t >;
//
// serializes/deserializes in same way as 'std::tuple<uint32_t,uint32_t>' configured for big
// endianess.
template < protocol_endianess_enum Endianess, typename... Ds >
struct protocol_tuple : protocol_def_type_base
{
......
......@@ -53,11 +53,10 @@ public:
}
static_vector& operator=( const static_vector& other )
{
if ( this == &other ) {
return *this;
if ( this != &other ) {
this->~static_vector();
::new ( this ) static_vector( other );
}
this->~static_vector();
::new ( this ) static_vector( other );
return *this;
}
static_vector& operator=( static_vector&& other ) noexcept
......@@ -187,8 +186,7 @@ private:
::new ( gen_ptr ) T( std::forward< Args >( args )... );
}
// Reference to the item in data_storage. std::launder is necessary here per the paper
// linked above.
// Reference to the item in data_storage.
[[nodiscard]] reference ref_item( size_type i )
{
return *std::launder( reinterpret_cast< T* >( &data_[i] ) );
......
......@@ -59,7 +59,8 @@ struct valid_test_case : protocol_test_fixture
};
template < protocol_endianess_enum Endianess, typename T >
std::function< protocol_test_fixture*() > make_valid_test_case( T val, std::vector< uint8_t > buff )
std::function< protocol_test_fixture*() >
make_valid_test_case( T val, const std::vector< uint8_t >& buff )
{
return [=]() {
return new valid_test_case< Endianess, T >( val, buff );
......@@ -68,7 +69,7 @@ std::function< protocol_test_fixture*() > make_valid_test_case( T val, std::vect
template < protocol_endianess_enum Endianess, typename T >
std::function< protocol_test_fixture*() > make_specific_valid_test_case(
typename protocol_def< T, Endianess >::value_type val,
std::vector< uint8_t > buff )
const std::vector< uint8_t >& buff )
{
return [=]() {
return new valid_test_case< Endianess, T >( val, buff );
......@@ -117,7 +118,7 @@ struct invalid_test_case : protocol_test_fixture
template < typename T >
std::function< protocol_test_fixture*() >
make_invalid_test_case( std::vector< uint8_t > buff, protocol_error_record rec )
make_invalid_test_case( const std::vector< uint8_t >& buff, const protocol_error_record& rec )
{
return [=]() {
return new invalid_test_case< T >( buff, rec );
......
......@@ -100,10 +100,10 @@ struct valid_test_case : protocol_test_fixture
template < typename Group >
std::function< protocol_test_fixture*() >
make_valid_test_case( typename Group::value_type val, std::vector< uint8_t > buff )
make_valid_test_case( typename Group::value_type val, const std::vector< uint8_t >& buff )
{
return [=]() {
return new valid_test_case< Group >( val, std::move( buff ) );
return new valid_test_case< Group >( val, buff );
};
}
......
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