mirror of
https://github.com/boostorg/redis.git
synced 2026-01-19 04:42:09 +00:00
Adds protobuf example.
This commit is contained in:
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -115,6 +115,8 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
- name: Install CMake
|
||||
run: sudo apt-get -y install cmake
|
||||
- name: Install protobuf
|
||||
run: sudo apt-get -y install protobuf-compiler
|
||||
- name: Install compiler
|
||||
run: sudo apt-get install -y ${{ matrix.install }}
|
||||
- name: Install Redis
|
||||
|
||||
@@ -42,6 +42,12 @@ write_basic_package_version_file(
|
||||
)
|
||||
|
||||
find_package(Boost 1.80 REQUIRED)
|
||||
|
||||
# We test the protobuf example only on gcc.
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
find_package(Protobuf) # For the protobuf example.
|
||||
endif()
|
||||
|
||||
include_directories(${Boost_INCLUDE_DIRS})
|
||||
|
||||
find_package(OpenSSL REQUIRED)
|
||||
@@ -130,13 +136,26 @@ if (MSVC)
|
||||
target_compile_definitions(cpp20_resolve_with_sentinel PRIVATE _WIN32_WINNT=0x0601)
|
||||
endif()
|
||||
|
||||
add_executable(cpp20_json_serialization examples/cpp20_json_serialization.cpp)
|
||||
target_compile_features(cpp20_json_serialization PUBLIC cxx_std_20)
|
||||
target_link_libraries(cpp20_json_serialization common)
|
||||
add_test(cpp20_json_serialization cpp20_json_serialization)
|
||||
add_executable(cpp20_json examples/cpp20_json.cpp)
|
||||
target_compile_features(cpp20_json PUBLIC cxx_std_20)
|
||||
target_link_libraries(cpp20_json common)
|
||||
add_test(cpp20_json cpp20_json)
|
||||
if (MSVC)
|
||||
target_compile_options(cpp20_json_serialization PRIVATE /bigobj)
|
||||
target_compile_definitions(cpp20_json_serialization PRIVATE _WIN32_WINNT=0x0601)
|
||||
target_compile_options(cpp20_json PRIVATE /bigobj)
|
||||
target_compile_definitions(cpp20_json PRIVATE _WIN32_WINNT=0x0601)
|
||||
endif()
|
||||
|
||||
if (Protobuf_FOUND)
|
||||
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS examples/person.proto)
|
||||
add_executable(cpp20_protobuf examples/cpp20_protobuf.cpp ${PROTO_SRCS} ${PROTO_HDRS})
|
||||
target_compile_features(cpp20_protobuf PUBLIC cxx_std_20)
|
||||
target_link_libraries(cpp20_protobuf common ${Protobuf_LIBRARIES})
|
||||
target_include_directories(cpp20_protobuf PUBLIC ${Protobuf_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR})
|
||||
add_test(cpp20_protobuf cpp20_protobuf)
|
||||
if (MSVC)
|
||||
target_compile_options(cpp20_protobuf PRIVATE /bigobj)
|
||||
target_compile_definitions(cpp20_protobuf PRIVATE _WIN32_WINNT=0x0601)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (NOT MSVC)
|
||||
|
||||
51
README.md
51
README.md
@@ -513,56 +513,24 @@ and other data structures in general.
|
||||
<a name="serialization"></a>
|
||||
## Serialization
|
||||
|
||||
Boost.Redis provides native support for serialization with Boost.Json.
|
||||
To use it
|
||||
|
||||
* Include boost/redis/serialization.hpp
|
||||
* Describe your class with Boost.Describe.
|
||||
|
||||
For example
|
||||
|
||||
```cpp
|
||||
#include <boost/redis/json.hpp>
|
||||
|
||||
struct user {
|
||||
std::string name;
|
||||
std::string age;
|
||||
std::string country;
|
||||
};
|
||||
|
||||
BOOST_DESCRIBE_STRUCT(user, (), (name, age, country))
|
||||
```
|
||||
|
||||
After that you will be able to user your described `struct` both in
|
||||
requests and responses, for example
|
||||
|
||||
```cpp
|
||||
user foo{"Joao", "58", "Brazil"}
|
||||
|
||||
request req;
|
||||
req.push("PING", foo);
|
||||
|
||||
response<user> resp;
|
||||
|
||||
co_await conn->async_exec(req, resp);
|
||||
```
|
||||
|
||||
For other serialization formats it is necessary to define the
|
||||
serialization functions `boost_redis_to_bulk` and `boost_redis_from_bulk` and
|
||||
import them onto the global namespace so they become available over
|
||||
ADL. They must have the following signature
|
||||
Boost.Redis supports serialization of user defined types by means of
|
||||
the following customization points
|
||||
|
||||
```cpp
|
||||
|
||||
// Serialize
|
||||
// Serialize.
|
||||
void boost_redis_to_bulk(std::string& to, mystruct const& obj);
|
||||
|
||||
// Deserialize
|
||||
void boost_redis_from_bulk(mystruct& obj, char const* p, std::size_t size, boost::system::error_code& ec)
|
||||
```
|
||||
|
||||
Example cpp20_json_serialization.cpp shows how store json strings in Redis.
|
||||
These functions are accessed over ADL and therefore they must be
|
||||
imported in the global namespace by the user. In the
|
||||
[Examples](#Examples) section the reader can find examples showing how
|
||||
to serialize using json and [protobuf](https://protobuf.dev/).
|
||||
|
||||
<a name="examples"></a>
|
||||
## Examples
|
||||
|
||||
The examples below show how to use the features discussed so far
|
||||
@@ -571,7 +539,8 @@ The examples below show how to use the features discussed so far
|
||||
* cpp20_intro.cpp: Does not use awaitable operators.
|
||||
* cpp20_intro_tls.cpp: Communicates over TLS.
|
||||
* cpp20_containers.cpp: Shows how to send and receive STL containers and how to use transactions.
|
||||
* cpp20_json_serialization.cpp: Shows how to serialize types using Boost.Json.
|
||||
* cpp20_json.cpp: Shows how to serialize types using Boost.Json.
|
||||
* cpp20_protobuf.cpp: Shows how to serialize types using protobuf.
|
||||
* cpp20_resolve_with_sentinel.cpp: Shows how to resolve a master address using sentinels.
|
||||
* cpp20_subscriber.cpp: Shows how to implement pubsub with reconnection re-subscription.
|
||||
* cpp20_echo_server.cpp: A simple TCP echo server.
|
||||
|
||||
@@ -10,11 +10,10 @@
|
||||
#define BOOST_CONTAINER_NO_LIB
|
||||
#include <boost/redis.hpp>
|
||||
#include <boost/describe.hpp>
|
||||
#include <boost/redis/json.hpp>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include "common/common.hpp"
|
||||
#include "json.hpp"
|
||||
|
||||
// Include this in no more than one .cpp file.
|
||||
#include <boost/json/src.hpp>
|
||||
@@ -27,18 +26,23 @@ using boost::redis::response;
|
||||
using boost::redis::operation;
|
||||
using boost::redis::ignore_t;
|
||||
|
||||
// Struct that will be stored in Redis using json serialization.
|
||||
struct user {
|
||||
std::string name;
|
||||
std::string age;
|
||||
std::string country;
|
||||
|
||||
friend
|
||||
auto operator<(user const& a, user const& b)
|
||||
{ return std::tie(a.name, a.age, a.country) < std::tie(b.name, b.age, b.country); }
|
||||
};
|
||||
|
||||
// The type must be described for serialization to work.
|
||||
BOOST_DESCRIBE_STRUCT(user, (), (name, age, country))
|
||||
|
||||
// Boost.Redis customization points (examples/json.hpp)
|
||||
void boost_redis_to_bulk(std::string& to, user const& u)
|
||||
{ boost::redis::json::to_bulk(to, u); }
|
||||
|
||||
void boost_redis_from_bulk(user& u, std::string_view sv, boost::system::error_code& ec)
|
||||
{ boost::redis::json::from_bulk(u, sv, ec); }
|
||||
|
||||
auto run(std::shared_ptr<connection> conn, std::string host, std::string port) -> net::awaitable<void>
|
||||
{
|
||||
co_await connect(conn, host, port);
|
||||
@@ -51,44 +55,24 @@ net::awaitable<void> co_main(std::string host, std::string port)
|
||||
auto conn = std::make_shared<connection>(ex);
|
||||
net::co_spawn(ex, run(conn, host, port), net::detached);
|
||||
|
||||
// A set of users that will be automatically serialized to json.
|
||||
std::set<user> users
|
||||
{{"Joao", "58", "Brazil"} , {"Serge", "60", "France"}};
|
||||
// user object that will be stored in Redis in json format.
|
||||
user const u{"Joao", "58", "Brazil"};
|
||||
|
||||
// To simplify we send the set and retrieve it in the same
|
||||
// resquest.
|
||||
// Stores and retrieves in the same request.
|
||||
request req;
|
||||
req.push("HELLO", 3);
|
||||
req.push("SET", "json-key", u); // Stores in Redis.
|
||||
req.push("GET", "json-key"); // Retrieves from Redis.
|
||||
|
||||
// Stores a std::set in a Redis set data structure.
|
||||
req.push_range("SADD", "sadd-key", users);
|
||||
response<ignore_t, ignore_t, user> resp;
|
||||
|
||||
// Sends a ping and retrieves it as a string to show what json
|
||||
// serialization looks like.
|
||||
req.push("PING", *users.begin());
|
||||
|
||||
// Sends another ping and retrieves it directly in a user type.
|
||||
req.push("PING", *users.begin());
|
||||
|
||||
// Retrieves the set we have just stored.
|
||||
req.push("SMEMBERS", "sadd-key");
|
||||
|
||||
response<ignore_t, ignore_t, std::string, user, std::set<user>> resp;
|
||||
|
||||
// Sends the request and receives the response.
|
||||
co_await conn->async_exec(req, resp);
|
||||
|
||||
// Prints the first ping
|
||||
auto const& pong1 = std::get<2>(resp).value();
|
||||
std::cout << pong1 << "\n";
|
||||
|
||||
// Prints the second ping.
|
||||
auto const& pong2 = std::get<3>(resp).value();
|
||||
std::cout << pong2.name << " " << pong2.age << " " << pong2.country << "\n";
|
||||
|
||||
// Prints the set.
|
||||
for (auto const& e: std::get<4>(resp).value())
|
||||
std::cout << e.name << " " << e.age << " " << e.country << "\n";
|
||||
std::cout
|
||||
<< "Name: " << std::get<2>(resp).value().name << "\n"
|
||||
<< "Age: " << std::get<2>(resp).value().age << "\n"
|
||||
<< "Country: " << std::get<2>(resp).value().country << "\n";
|
||||
|
||||
conn->cancel(operation::run);
|
||||
}
|
||||
78
examples/cpp20_protobuf.cpp
Normal file
78
examples/cpp20_protobuf.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
/* Copyright (c) 2018-2022 Marcelo Zimbres Silva (mzimbres@gmail.com)
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See
|
||||
* accompanying file LICENSE.txt)
|
||||
*/
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#if defined(BOOST_ASIO_HAS_CO_AWAIT)
|
||||
#include <boost/redis.hpp>
|
||||
#include <iostream>
|
||||
#include "common/common.hpp"
|
||||
#include "protobuf.hpp"
|
||||
|
||||
// See the definition in person.proto. This header is automatically
|
||||
// generated by CMakeLists.txt.
|
||||
#include "person.pb.h"
|
||||
|
||||
namespace net = boost::asio;
|
||||
namespace redis = boost::redis;
|
||||
using boost::redis::request;
|
||||
using boost::redis::response;
|
||||
using boost::redis::operation;
|
||||
using boost::redis::ignore_t;
|
||||
|
||||
// The protobuf type described in examples/person.proto
|
||||
using tutorial::person;
|
||||
|
||||
// Boost.Redis customization points (examples/protobuf.hpp)
|
||||
namespace tutorial
|
||||
{
|
||||
|
||||
void boost_redis_to_bulk(std::string& to, person const& u)
|
||||
{ boost::redis::protobuf::to_bulk(to, u); }
|
||||
|
||||
void boost_redis_from_bulk(person& u, std::string_view sv, boost::system::error_code& ec)
|
||||
{ boost::redis::protobuf::from_bulk(u, sv, ec); }
|
||||
|
||||
} // tutorial
|
||||
|
||||
using tutorial::boost_redis_to_bulk;
|
||||
using tutorial::boost_redis_from_bulk;
|
||||
|
||||
auto run(std::shared_ptr<connection> conn, std::string host, std::string port) -> net::awaitable<void>
|
||||
{
|
||||
co_await connect(conn, host, port);
|
||||
co_await conn->async_run();
|
||||
}
|
||||
|
||||
net::awaitable<void> co_main(std::string host, std::string port)
|
||||
{
|
||||
auto ex = co_await net::this_coro::executor;
|
||||
auto conn = std::make_shared<connection>(ex);
|
||||
net::co_spawn(ex, run(conn, host, port), net::detached);
|
||||
|
||||
person p;
|
||||
p.set_name("Louis");
|
||||
p.set_id(3);
|
||||
p.set_email("No email yet.");
|
||||
|
||||
request req;
|
||||
req.push("HELLO", 3);
|
||||
req.push("SET", "protobuf-key", p);
|
||||
req.push("GET", "protobuf-key");
|
||||
|
||||
response<ignore_t, ignore_t, person> resp;
|
||||
|
||||
// Sends the request and receives the response.
|
||||
co_await conn->async_exec(req, resp);
|
||||
|
||||
std::cout
|
||||
<< "Name: " << std::get<2>(resp).value().name() << "\n"
|
||||
<< "Age: " << std::get<2>(resp).value().id() << "\n"
|
||||
<< "Email: " << std::get<2>(resp).value().email() << "\n";
|
||||
|
||||
conn->cancel(operation::run);
|
||||
}
|
||||
|
||||
#endif // defined(BOOST_ASIO_HAS_CO_AWAIT)
|
||||
@@ -14,13 +14,13 @@ namespace boost::redis::json
|
||||
{
|
||||
|
||||
template <class T>
|
||||
void boost_redis_to_bulk(std::string& to, T const& u)
|
||||
void to_bulk(std::string& to, T const& u)
|
||||
{
|
||||
redis::resp3::boost_redis_to_bulk(to, boost::json::serialize(boost::json::value_from(u)));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void boost_redis_from_bulk(T& u, std::string_view sv, system::error_code&)
|
||||
void from_bulk(T& u, std::string_view sv, system::error_code&)
|
||||
{
|
||||
auto const jv = boost::json::parse(sv);
|
||||
u = boost::json::value_to<T>(jv);
|
||||
@@ -28,7 +28,4 @@ void boost_redis_from_bulk(T& u, std::string_view sv, system::error_code&)
|
||||
|
||||
} // boost::redis::json
|
||||
|
||||
using boost::redis::json::boost_redis_to_bulk;
|
||||
using boost::redis::json::boost_redis_from_bulk;
|
||||
|
||||
#endif // BOOST_REDIS_JSON_HPP
|
||||
9
examples/person.proto
Normal file
9
examples/person.proto
Normal file
@@ -0,0 +1,9 @@
|
||||
syntax = "proto2";
|
||||
|
||||
package tutorial;
|
||||
|
||||
message person {
|
||||
optional string name = 1;
|
||||
optional int32 id = 2;
|
||||
optional string email = 3;
|
||||
}
|
||||
40
examples/protobuf.hpp
Normal file
40
examples/protobuf.hpp
Normal file
@@ -0,0 +1,40 @@
|
||||
/* Copyright (c) 2018-2022 Marcelo Zimbres Silva (mzimbres@gmail.com)
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See
|
||||
* accompanying file LICENSE.txt)
|
||||
*/
|
||||
|
||||
#ifndef BOOST_REDIS_PROTOBUF_HPP
|
||||
#define BOOST_REDIS_PROTOBUF_HPP
|
||||
|
||||
#include <boost/redis/resp3/serialization.hpp>
|
||||
#include <boost/system/errc.hpp>
|
||||
|
||||
namespace boost::redis::protobuf
|
||||
{
|
||||
|
||||
// Below I am using a Boost.Redis to indicate a protobuf error, this
|
||||
// is ok for an example, users however might want to define their own
|
||||
// error codes.
|
||||
|
||||
template <class T>
|
||||
void to_bulk(std::string& to, T const& u)
|
||||
{
|
||||
std::string tmp;
|
||||
if (!u.SerializeToString(&tmp))
|
||||
throw system::system_error(redis::error::invalid_data_type);
|
||||
|
||||
boost::redis::resp3::boost_redis_to_bulk(to, tmp);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void from_bulk(T& u, std::string_view sv, system::error_code& ec)
|
||||
{
|
||||
std::string const tmp {sv};
|
||||
if (!u.ParseFromString(tmp))
|
||||
ec = redis::error::invalid_data_type;
|
||||
}
|
||||
|
||||
} // boost::redis::json
|
||||
|
||||
#endif // BOOST_REDIS_PROTOBUF_HPP
|
||||
Reference in New Issue
Block a user