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

another stage of porting of protocol lib with extra types

parent 5ed354ee
Pipeline #98308 passed with stage
in 53 seconds
---
Checks: '-*,bugprone-*,cppcore*,performance*,modernize*,readbility*,portability*,-modernize-use-trailing-return-type,-cppcoreguidelines-owning-memory,-cppcoreguidelines-special-member-functions,-cppcoreguidelines-pro-bounds-constant-array-index'
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,
performance*,
modernize*, -modernize-use-trailing-return-type,-modernize-avoid-c-arrays
readbility*,
portability*,
WarningsAsErrors: '-*'
HeaderFilterRegex: '.*'
AnalyzeTemporaryDtors: true
......
......@@ -440,6 +440,15 @@ template < range_container Container, typename T >
return res;
}
template < std::size_t i, typename NullFunction >
constexpr void for_each_index( NullFunction&& f )
{
if constexpr ( i != 0 ) {
for_each_index< i - 1 >( f );
f.template operator()< i - 1 >();
}
}
struct uncurry_impl
{
template < typename Callable >
......
#include <optional>
#include <type_traits>
#ifdef EMLABCPP_USE_STREAMS
#include <ostream>
#endif
#pragma once
namespace emlabcpp
......
#include "emlabcpp/bounded.h"
#include "emlabcpp/protocol/message.h"
#include "emlabcpp/quantity.h"
#include "emlabcpp/types.h"
#include <bitset>
#include <cstring>
#include <type_traits>
#include <variant>
#pragma once
......@@ -38,6 +42,8 @@ static constexpr auto LOWSIZE_ERR = make_protocol_mark( "LOWSIZE " );
static constexpr auto BIGSIZE_ERR = make_protocol_mark( "BIGSIZE " );
static constexpr auto BOUND_ERR = make_protocol_mark( "BOUND " );
static constexpr auto UNDEFVAR_ERR = make_protocol_mark( "UNDEFVAR" );
static constexpr auto BADVAL_ERR = make_protocol_mark( "BADVAL " );
static constexpr auto GROUP_ERR = make_protocol_mark( "GRPMATCH" );
template < bounded_derived size_type, typename T >
struct protocol_result
......@@ -49,15 +55,28 @@ struct protocol_result
template < typename T >
concept protocol_base_type = std::is_integral_v< T > || std::is_enum_v< T >;
enum class protocol_endianess_enum
enum protocol_endianess_enum
{
BIG,
LITTLE,
PARENT
PROTOCOL_BIG_ENDIAN,
PROTOCOL_LITTLE_ENDIAN,
PROTOCOL_PARENT_ENDIAN
};
// whe 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_endianess_enum::PARENT;
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
{
using options_type = std::variant< Ts... >;
};
template < typename CounterType, typename T >
struct protocol_sized_buffer
......@@ -66,12 +85,122 @@ struct protocol_sized_buffer
using value_type = T;
};
template < typename Tag, typename T, T Offset >
class protocol_offset : public quantity< protocol_offset< Tag, T, Offset >, T >
template < typename T, T Offset >
struct protocol_offset
{
public:
static constexpr T offset = Offset;
using quantity< protocol_offset< Tag, T, Offset >, T >::quantity;
using value_type = T;
};
template < typename T >
struct protocol_item_decl;
template < typename T >
concept protocol_itemizable = std::default_initializable< T > && requires( T val )
{
protocol_item_decl< T >{};
};
template < protocol_base_type T >
struct protocol_item_decl< T >
{
using value_type = T;
static constexpr std::size_t max_size = sizeof( T );
};
template < protocol_itemizable T, std::size_t N >
struct protocol_item_decl< std::array< T, N > >
{
using value_type = std::array< T, N >;
static constexpr std::size_t max_size = protocol_item_decl< T >::max_size * N;
};
template < protocol_itemizable... Ts >
struct protocol_item_decl< std::tuple< Ts... > >
{
using value_type = std::tuple< typename protocol_item_decl< Ts >::value_type... >;
static constexpr std::size_t max_size = ( protocol_item_decl< Ts >::max_size + ... );
};
template < protocol_itemizable... Ts >
struct protocol_item_decl< std::variant< Ts... > >
{
using id_item = uint8_t;
using id_decl = protocol_item_decl< id_item >;
using value_type = std::variant< typename protocol_item_decl< Ts >::value_type... >;
static constexpr std::size_t max_size =
id_decl::max_size +
std::max< std::size_t >( { protocol_item_decl< Ts >::max_size... } );
};
template < std::size_t N >
struct protocol_item_decl< std::bitset< N > >
{
using value_type = std::bitset< N >;
static constexpr std::size_t max_size = ( N + 7 ) / 8;
};
template < std::size_t N >
struct protocol_item_decl< protocol_sizeless_message< N > >
{
using value_type = protocol_sizeless_message< N >;
static constexpr std::size_t max_size = N;
};
template < protocol_itemizable T, T Offset >
struct protocol_item_decl< protocol_offset< T, Offset > >
{
using value_type = T;
static constexpr std::size_t max_size = protocol_item_decl< T >::max_size;
};
template < quantity_derived T >
struct protocol_item_decl< T >
{
using value_type = T;
static constexpr std::size_t max_size =
protocol_item_decl< typename T::value_type >::max_size;
};
template < protocol_itemizable T, T Min, T Max >
struct protocol_item_decl< bounded< T, Min, Max > >
{
using value_type = bounded< T, Min, Max >;
static constexpr std::size_t max_size = protocol_item_decl< T >::max_size;
};
template < typename CounterType, typename T >
struct protocol_item_decl< protocol_sized_buffer< CounterType, T > >
{
using counter_item = protocol_item_decl< CounterType >;
using sub_item = protocol_item_decl< T >;
using value_type = sub_item::value_type;
static constexpr std::size_t max_size = counter_item::max_size + sub_item::max_size;
};
template < auto V >
struct protocol_item_decl< tag< V > >
{
using sub_item = protocol_item_decl< decltype( V ) >;
using value_type = tag< V >;
static constexpr std::size_t max_size = sub_item::max_size;
};
template < typename... Ts >
struct protocol_item_decl< protocol_group< Ts... > >
{
using value_type = std::variant< typename protocol_item_decl< Ts >::value_type... >;
static constexpr std::size_t max_size =
std::max< std::size_t >( { protocol_item_decl< Ts >::max_size... } );
};
#ifdef EMLABCPP_USE_STREAMS
......@@ -91,11 +220,11 @@ inline std::ostream& operator<<( std::ostream& os, const protocol_error_record&
inline std::ostream& operator<<( std::ostream& os, const protocol_endianess_enum& val )
{
switch ( val ) {
case protocol_endianess_enum::BIG:
case PROTOCOL_BIG_ENDIAN:
return os << "big endian";
case protocol_endianess_enum::LITTLE:
case PROTOCOL_LITTLE_ENDIAN:
return os << "little endian";
case protocol_endianess_enum::PARENT:
case PROTOCOL_PARENT_ENDIAN:
return os << "parent's endian";
}
return os;
......
#include "emlabcpp/assert.h"
#include "emlabcpp/protocol/base.h"
#include "emlabcpp/types.h"
#pragma once
namespace emlabcpp
{
template < auto ID, typename... Args >
struct protocol_command
{
using id_type = decltype( ID );
using value_type =
std::tuple< tag< ID >, typename protocol_item_decl< Args >::value_type... >;
using def_type = std::tuple< tag< ID >, Args... >;
static constexpr id_type id = ID;
template < typename... NewArgs >
using with_args = protocol_command< ID, Args..., NewArgs... >;
};
template < typename... Cmds >
struct protocol_command_group
{
static_assert( are_same_v< typename Cmds::id_type... > );
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;
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 )
{
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;
if constexpr ( cmd::id == id ) {
res.emplace( T{ tag< id >{}, args... } );
}
} );
EMLABCPP_ASSERT( res );
return *res;
}
};
template <>
struct protocol_command_group<>
{
template < typename... NewCommands >
using with_commands = protocol_command_group< NewCommands... >;
};
} // namespace emlabcpp
#include "emlabcpp/protocol/item.h"
#pragma once
namespace emlabcpp
{
template < typename T >
struct protocol_handler
{
using def_type = typename T::def_type;
using value_type = typename T::value_type;
using message_type = typename T::message_type;
using pitem = protocol_item< def_type, PROTOCOL_BIG_ENDIAN >;
static message_type serialize( value_type val )
{
std::array< uint8_t, pitem::max_size > buffer;
bounded used = pitem::serialize_at( buffer, val );
return *message_type::make( view_n( buffer.begin(), *used ) );
};
static either< value_type, protocol_error_record > extract( const message_type& msg )
{
return pitem::deserialize( msg ).convert_left( [&]( auto sub_res ) {
return sub_res.val;
} );
}
};
} // namespace emlabcpp
This diff is collapsed.
#include "emlabcpp/concepts.h"
#include <array>
......@@ -40,29 +41,29 @@ public:
static_assert( M <= N );
}
std::size_t size() const
[[nodiscard]] std::size_t size() const
{
return used_;
}
uint8_t front() const
[[nodiscard]] uint8_t front() const
{
return data_.front();
}
uint8_t back() const
[[nodiscard]] uint8_t back() const
{
return data_[used_ - 1];
}
const_iterator begin() const
[[nodiscard]] const_iterator begin() const
{
return data_.begin();
}
const_iterator end() const
[[nodiscard]] const_iterator end() const
{
return data_.end();
return data_.begin() + used_;
}
uint8_t operator[]( std::size_t i ) const
......
......@@ -36,33 +36,6 @@ struct mapped< Container, UnaryFunction >
template < typename Container, typename UnaryFunction >
using mapped_t = typename mapped< Container, UnaryFunction >::type;
// ------------------------------------------------------------------------------------------------
/// tuple_of_constants<Is..> is a tuple of integral constants in ranage Is...
template < std::size_t... Is >
using tuple_of_constants_t = std::tuple< std::integral_constant< std::size_t, Is >... >;
namespace detail
{
template < typename >
struct make_sequence_tuple_impl;
template < std::size_t... Is >
struct make_sequence_tuple_impl< std::index_sequence< Is... > >
{
using type = tuple_of_constants_t< Is... >;
};
} // namespace detail
template < std::size_t N >
struct make_sequence_tuple
{
using type =
typename detail::make_sequence_tuple_impl< std::make_index_sequence< N > >::type;
};
template < std::size_t N >
using make_sequence_tuple_t = typename make_sequence_tuple< N >::type;
// ------------------------------------------------------------------------------------------------
//// tag<V> type can be used for tagging f-calls for function overloading
template < auto V >
......@@ -71,6 +44,8 @@ struct tag
using value_type = decltype( V );
static constexpr value_type value = V;
friend constexpr auto operator<=>( const tag&, const tag& ) = default;
};
} // namespace emlabcpp
......@@ -148,8 +148,8 @@ struct impl::is_view< view< Iter > > : std::true_type
template < typename Iter >
constexpr view< Iter > view_n( Iter begin, std::size_t n )
{
auto end =
std::next( begin, static_cast< std::iterator_traits< Iter >::difference_type >( n ) );
auto end = std::next(
begin, static_cast< typename std::iterator_traits< Iter >::difference_type >( n ) );
return view< Iter >{ std::move( begin ), end };
}
......
......@@ -97,7 +97,7 @@ public:
private:
template < typename std::size_t... Idx >
constexpr bool
[[nodiscard]] constexpr bool
equals( const zip_iterator< Iterators... >& other, std::index_sequence< Idx... > ) const
{
return ( ( std::get< Idx >( iters_ ) == std::get< Idx >( other.iters_ ) ) || ... );
......
......@@ -13,7 +13,7 @@ set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
add_compile_options(
-g
-gdwarf
-Werror
-Wextra
-Wpedantic
......@@ -35,11 +35,13 @@ add_compile_options(
-DEMLABCPP_USE_STREAMS
)
# TODO: this should use top level cmake
include_directories(../include/)
function(add_emlabcpp_test name)
add_executable(${name} ${name}.cpp)
target_link_libraries(${name} GTest::GTest GTest::Main)
target_include_directories(${name} PRIVATE ../tests/include/)
add_test(NAME ${name} COMMAND ${name})
endfunction()
......@@ -53,3 +55,4 @@ add_emlabcpp_test(zip_test)
add_emlabcpp_test(static_vector_test)
add_emlabcpp_test(pid_test)
add_emlabcpp_test(protocol_item_test)
add_emlabcpp_test(protocol_sophisticated_test)
#include "emlabcpp/zip.h"
#include <gtest/gtest.h>
namespace emlabcpp
{
struct protocol_test_fixture : public testing::Test
{
virtual void generate_name( std::ostream& ) const = 0;
};
inline std::ostream& operator<<( std::ostream& os, const protocol_test_fixture& f )
{
f.generate_name( os );
return os;
}
template < typename T >
inline std::string pretty_name()
{
int tmp = 0;
char* name = abi::__cxa_demangle( typeid( T ).name(), nullptr, nullptr, &tmp );
std::string res{ name };
free( name );
return res;
}
inline void exec_protocol_test_fixture_test(
const std::vector< std::function< protocol_test_fixture*() > >& factories )
{
for ( auto [i, factory] : enumerate( factories ) ) {
std::unique_ptr< protocol_test_fixture > test_ptr{ factory() };
testing::RegisterTest(
"protocol_test_fixture",
( "test " + ::testing::PrintToString( *test_ptr ) ).c_str(),
nullptr,
nullptr,
__FILE__,
__LINE__,
factory );
}
}
} // namespace emlabcpp
#include "emlabcpp/iterators/convert.h"
#include "emlabcpp/protocol/item.h"
#include "emlabcpp/zip.h"
#include "util.h"
#include <gtest/gtest.h>
using namespace emlabcpp;
struct protocol_test_fixture : public testing::Test
{
virtual void generate_name( std::ostream& ) const = 0;
};
inline std::ostream& operator<<( std::ostream& os, const protocol_test_fixture& f )
{
f.generate_name( os );
return os;
}
template < typename T >
inline std::string pretty_name()
{
int tmp = 0;
char* name = abi::__cxa_demangle( typeid( T ).name(), nullptr, nullptr, &tmp );
std::string res{ name };
free( name );
return res;
}
template < protocol_endianess_enum Endianess, typename T >
struct valid_test_case : protocol_test_fixture
{
......@@ -39,18 +18,18 @@ struct valid_test_case : protocol_test_fixture
std::vector< uint8_t > expected_buffer;
valid_test_case( value_type v, std::vector< uint8_t > buff )
: val( v )
, expected_buffer( buff )
: val( std::move( v ) )
, expected_buffer( std::move( buff ) )
{
}
void TestBody()
void TestBody() final
{
std::array< uint8_t, pitem::max_size > buffer;
std::array< uint8_t, pitem::max_size > buffer{};
std::span bspan{ buffer };
std::size_t used =
pitem::serialize_at( bspan.template first< pitem::max_size >(), val );
*pitem::serialize_at( bspan.template first< pitem::max_size >(), val );
EXPECT_EQ( used, expected_buffer.size() );
auto serialized = convert_view_n< int >( buffer.begin(), used );
......@@ -59,14 +38,18 @@ struct valid_test_case : protocol_test_fixture
<< "serialized : " << serialized << "\n"
<< "expected : " << convert_view< int >( expected_buffer ) << "\n";
auto res_val = pitem::deserialize( view_n( buffer.begin(), used ) );
EXPECT_FALSE( std::holds_alternative< protocol_error_record >( res_val ) );
EXPECT_EQ( *std::get< 0 >( res_val ).used, expected_buffer.size() );
EXPECT_EQ( std::get< 0 >( res_val ).val, val );
pitem::deserialize( view_n( buffer.begin(), used ) )
.match(
[&]( auto res ) {
EXPECT_EQ( *res.used, expected_buffer.size() );
EXPECT_EQ( res.val, val );
},
[&]( protocol_error_record ) {
FAIL();
} );
}
void generate_name( std::ostream& os ) const
void generate_name( std::ostream& os ) const final
{
os << "test case for: " << pretty_name< T >() << ";"
<< " " << Endianess << ";" << std::hex
......@@ -95,34 +78,35 @@ template < typename T >
struct invalid_test_case : protocol_test_fixture
{