Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Task/new api #30

Merged
merged 5 commits into from
Nov 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 39 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
[![Library-coverage](https://tonda-kriz.github.io/simple-protobuf/library/coverage.svg)](https://tonda-kriz.github.io/simple-protobuf/library)
[![Compiler-coverage](https://tonda-kriz.github.io/simple-protobuf/compiler/coverage.svg)](https://tonda-kriz.github.io/simple-protobuf/compiler)

**simple data struct** serialization library for C++. With this library you can serialize and deserialize C++ structs directly to [JSON](https://json.org) or [protobuf](https://github.com/protocolbuffers/protobuf).
**simple data struct** serialization library for C++. With this library you can serialize and deserialize *POD* C++ structs directly to [JSON](https://json.org) or [protobuf](https://github.com/protocolbuffers/protobuf).
When used together with [etl library](https://github.com/ETLCPP/etl) it doesn't need to allocate any memory, so its suitable for embedded environments (see [extensions](doc/extensions.md)).

```CPP
Expand Down Expand Up @@ -37,9 +37,13 @@ auto john = PhoneBook::Person{
.id = 1234,
.email = "jdoe@example.com",
};
auto json = spb::json::serialize( john );
//- serialize john to json-string
auto json = spb::json::serialize< std::string >( john );
//- deserialize john from json-string
auto person = spb::json::deserialize< PhoneBook::Person >( json );
auto pb = spb::pb::serialize( john );
//- serialize john to protobuf-vector
auto pb = spb::pb::serialize< std::vector< std::byte > >( john );
//- deserialize john from protobuf-vector
auto person2 = spb::pb::deserialize< PhoneBook::Person >( pb );
//- john == person == person2
```
Expand All @@ -64,7 +68,7 @@ spb is an alternative implementation of [protobuf](https://github.com/protocolbu

* C++ compiler (at least C++20)
* cmake
* *(optional) std library*
* std library
* *(optional) clang-format for code formatting*

### cheat sheet
Expand All @@ -76,8 +80,8 @@ add_subdirectory(external/spb-proto)
spb_protobuf_generate(PROTO_SRCS PROTO_HDRS ${CMAKE_SOURCE_DIR}/proto/person.proto)
# add generated files to your project
add_executable(myapp ${PROTO_SRCS} ${PROTO_HDRS})
# `spb-proto` is an interface library
# the main purpose is to update include path of `myapp`
# `spb-proto` is an interface library
# the main purpose is to update include path of `myapp`
target_link_libraries(myapp PUBLIC spb-proto)
```

Expand Down Expand Up @@ -164,32 +168,49 @@ All generated messages (and enums) are using the following API [`include/spb/jso

```CPP
//- serialize message via writer (all other `serialize` are just wrappers around this one)
//- example: `auto serialized_size = spb::pb::serialize( message, my_writer );`
auto serialize( const auto & message, spb::io::writer on_write ) -> size_t;

//- return size in bytes of serialized message
//- example: `auto serialized_size = spb::pb::serialize_size( message );`
auto serialize_size( const auto & message ) -> size_t;

//- serialize message into string
auto serialize( const auto & message ) -> std::string
//- serialize message into container like std::string, std::vector, ...
//- example: `auto serialized_size = spb::pb::serialize( message, my_string );`
template < typename Message, spb::resizable_container Container >
auto serialize( const Message & message, Container & result ) -> size_t;

//- serialize message and return container like std::string, std::vector, ...
//- example: `auto my_string = spb::pb::serialize< std::string >( message );`
template < spb::resizable_container Container = std::string, typename Message >
auto serialize( const Message & message ) -> Container;

```

```CPP
//- deserialize message from reader (all other `deserialize` are just wrappers around this one)
void deserialize( auto & result, spb::io::reader on_read );
//- example: `spb::pb::deserialize( message, my_reader );`
void deserialize( auto & message, spb::io::reader on_read );

//- deserialize message from data
void deserialize( auto & message, std::string_view data )
//- deserialize message from container like std::string, std::vector, ...
//- example: `spb::pb::deserialize( message, my_string );`
template < typename Message, spb::size_container Container >
void deserialize( Message & message, const Container & protobuf );

//- return deserialized message from reader
auto deserialize< Message >( spb::io::reader on_read ) -> Message

//- return deserialized message from data
auto deserialize< Message >( std::string_view data ) -> Message
```
//- return deserialized message from container like std::string, std::vector, ...
//- example: `auto message = spb::pb::deserialize< Message >( my_string );`
template < typename Message, spb::size_container Container >
auto deserialize( const Container & protobuf ) -> Message;

API is prefixed with `spb::json::` for **json** and `spb::pb::` for **protobuf**
//- return deserialized message from reader
//- example: `auto message = spb::pb::deserialize< Message >( my_reader );`
template < typename Message >
auto deserialize( spb::io::reader reader ) -> Message;
```

[`spb::io::reader`](include/spb/io/io.hpp) and [`spb::io::writer`](include/spb/io/io.hpp) are user specified *functions* for IO, more info at [`include/io/io.hpp`](include/spb/io/io.hpp)
API is prefixed with `spb::json::` for **json** and `spb::pb::` for **protobuf**,
template concepts [`spb::size_container`](include/spb/concepts.h) and [`spb::resizable_container`](include/spb/concepts.h) are defined in [`include/spb/concepts.h`](include/spb/concepts.h), [`spb::io::reader`](include/spb/io/io.hpp) and [`spb::io::writer`](include/spb/io/io.hpp) are user specified *functions* for IO, more info at [`include/io/io.hpp`](include/spb/io/io.hpp)

## type mapping

Expand Down
28 changes: 20 additions & 8 deletions include/spb/concepts.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,26 @@
#include <cstddef>
#include <type_traits>

namespace spb::detail
namespace spb
{
template < class T >
concept resizable_container = requires( T container ) {
{ container.data( ) } -> std::same_as< typename std::decay_t< T >::value_type * >;
{ container.resize( 1 ) };
typename std::decay_t< T >::value_type;
{ sizeof( typename std::decay_t< T >::value_type ) == sizeof( char ) };
};

template < class T >
concept size_container = requires( T container ) {
{ container.data( ) };
{ container.size( ) } -> std::convertible_to< std::size_t >;
typename std::decay_t< T >::value_type;
{ sizeof( typename std::decay_t< T >::value_type ) == sizeof( char ) };
};

namespace detail
{
template < class T >
concept proto_field_int_or_float = std::is_integral_v< T > || std::is_floating_point_v< T >;

Expand Down Expand Up @@ -66,17 +83,12 @@ concept proto_label_optional = requires( T container ) {
typename T::value_type;
};

/**
* @brief proto `enum` converted to C++ enum
*/
template < class T >
concept proto_enum = std::is_enum_v< T >;

/**
* @brief proto `message` converted to C++ struct
*/
template < class T >
concept proto_message = std::is_class_v< T > && !proto_field_string< T > &&
!proto_field_bytes< T > && !proto_label_repeated< T > && !proto_label_optional< T >;

}// namespace spb::detail
}// namespace detail
}// namespace spb
42 changes: 33 additions & 9 deletions include/spb/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#include "spb/io/io.hpp"
#include "json/deserialize.hpp"
#include "json/serialize.hpp"
#include <cstdint>
#include <cstdlib>

namespace spb::json
{
Expand Down Expand Up @@ -47,17 +47,37 @@ static inline auto serialize( const auto & message, spb::io::writer on_write ) -
* @param message to be serialized
* @return serialized json
* @throws std::runtime_error on error
* @example `auto serialized = std::vector< std::byte >();`
* `spb::json::serialize( message, serialized );`
*/
[[nodiscard]] static inline auto serialize( const auto & message ) -> std::string
template < typename Message, spb::resizable_container Container >
static inline auto serialize( const Message & message, Container & result ) -> size_t
{
auto result = std::string( serialize_size( message ), '\0' );
const auto size = serialize_size( message );
result.resize( size );
auto writer = [ ptr = result.data( ) ]( const void * data, size_t size ) mutable
{
memcpy( ptr, data, size );
ptr += size;
};

serialize( message, writer );
return size;
}

/**
* @brief serialize message into json
*
* @param[in] message to be serialized
* @return serialized json
* @throws std::runtime_error on error
* @example `auto serialized_message = spb::json::serialize( message );`
*/
template < spb::resizable_container Container = std::string, typename Message >
[[nodiscard]] static inline auto serialize( const Message & message ) -> Container
{
auto result = Container( );
serialize< Message, Container >( message, result );
return result;
}

Expand All @@ -79,8 +99,12 @@ static inline void deserialize( auto & result, spb::io::reader on_read )
* @param json string with json
* @param message deserialized json
* @throws std::runtime_error on error
* @example `auto serialized = std::string( ... );`
* `auto message = Message();`
* `spb::json::deserialize( message, serialized );`
*/
static inline void deserialize( auto & message, std::string_view json )
template < typename Message, spb::size_container Container >
static inline void deserialize( Message & message, const Container & json )
{
auto reader = [ ptr = json.data( ), end = json.data( ) + json.size( ) ](
void * data, size_t size ) mutable -> size_t
Expand All @@ -97,14 +121,14 @@ static inline void deserialize( auto & message, std::string_view json )

/**
* @brief deserialize json-string into variable
* use it like `auto message = spb::json::deserialize< Message >( json_string )`
*
* @param json string with json
* @return deserialized json or throw an exception
* @throws std::runtime_error on error
* @example `auto serialized = std::string( ... );`
* `auto message = spb::json::deserialize< Message >( serialized );`
*/
template < typename Message >
[[nodiscard]] static inline auto deserialize( std::string_view json ) -> Message
template < typename Message, spb::size_container Container >
[[nodiscard]] static inline auto deserialize( const Container & json ) -> Message
{
auto message = Message{ };
deserialize( message, json );
Expand All @@ -113,11 +137,11 @@ template < typename Message >

/**
* @brief deserialize json-string into variable
* use it like `auto message = spb::json::deserialize< Message >( reader )`
*
* @param on_read function for handling reads
* @return deserialized json
* @throws std::runtime_error on error
* @example `auto message = spb::json::deserialize< Message >( reader )`
*/
template < typename Message >
[[nodiscard]] static inline auto deserialize( spb::io::reader on_read ) -> Message
Expand Down
47 changes: 36 additions & 11 deletions include/spb/pb.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
\***************************************************************************/
#pragma once

#include "concepts.h"
#include "pb/deserialize.hpp"
#include "pb/serialize.hpp"
#include "spb/io/io.hpp"
#include <algorithm>
#include <cstdint>
#include <cstdlib>

namespace spb::pb
{
Expand Down Expand Up @@ -45,19 +45,40 @@ static inline auto serialize( const auto & message, spb::io::writer on_write ) -
* @brief serialize message into protobuf
*
* @param[in] message to be serialized
* @return serialized protobuf
* @param[out] result serialized protobuf
* @return serialized size in bytes
* @throws std::runtime_error on error
* @example `auto serialized = std::vector< std::byte >();`
* `spb::pb::serialize( message, serialized );`
*/
[[nodiscard]] static inline auto serialize( const auto & message ) -> std::string
template < typename Message, spb::resizable_container Container >
static inline auto serialize( const Message & message, Container & result ) -> size_t
{
auto result = std::string( serialize_size( message ), '\0' );
const auto size = serialize_size( message );
result.resize( size );
auto writer = [ ptr = result.data( ) ]( const void * data, size_t size ) mutable
{
memcpy( ptr, data, size );
ptr += size;
};

serialize( message, writer );
return size;
}

/**
* @brief serialize message into protobuf
*
* @param[in] message to be serialized
* @return serialized protobuf
* @throws std::runtime_error on error
* @example `auto serialized_message = spb::pb::serialize< std::vector< std::byte > >( message );`
*/
template < spb::resizable_container Container = std::string, typename Message >
[[nodiscard]] static inline auto serialize( const Message & message ) -> Container
{
auto result = Container( );
serialize< Message, Container >( message, result );
return result;
}

Expand All @@ -79,8 +100,12 @@ static inline void deserialize( auto & message, spb::io::reader reader )
* @param[in] protobuf string with protobuf
* @param[out] message deserialized message
* @throws std::runtime_error on error
* @example `auto serialized = std::vector< std::byte >( ... );`
* `auto message = Message();`
* `spb::pb::deserialize( message, serialized );`
*/
static inline void deserialize( auto & message, std::string_view protobuf )
template < typename Message, spb::size_container Container >
static inline void deserialize( Message & message, const Container & protobuf )
{
auto reader = [ ptr = protobuf.data( ), end = protobuf.data( ) + protobuf.size( ) ](
void * data, size_t size ) mutable -> size_t
Expand All @@ -97,14 +122,15 @@ static inline void deserialize( auto & message, std::string_view protobuf )

/**
* @brief deserialize message from protobuf
* use it like `auto message = spb::pb::deserialize< Message >( protobuf_str )`
*
* @param[in] protobuf string with protobuf
* @param[in] protobuf serialized protobuf
* @return deserialized message
* @throws std::runtime_error on error
* @example `auto serialized = std::vector< std::byte >( ... );`
* `auto message = spb::pb::deserialize< Message >( serialized );`
*/
template < typename Message >
[[nodiscard]] static inline auto deserialize( std::string_view protobuf ) -> Message
template < typename Message, spb::size_container Container >
[[nodiscard]] static inline auto deserialize( const Container & protobuf ) -> Message
{
auto message = Message{ };
deserialize( message, protobuf );
Expand All @@ -113,7 +139,6 @@ template < typename Message >

/**
* @brief deserialize message from reader
* use it like `auto message = spb::pb::deserialize< Message >( protobuf_str )`
*
* @param[in] reader function for handling reads
* @return deserialized message
Expand Down
Loading