Skip to content

Commit 8b02268

Browse files
committed
Uses boost.describe to simplify json serialization.
1 parent 1b60eeb commit 8b02268

File tree

5 files changed

+152
-65
lines changed

5 files changed

+152
-65
lines changed

benchmarks/nodejs/README.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
$ npm install
3+
4+
$ node echo_server_over_redis.js

benchmarks/nodejs/echo_server_over_redis/echo_server_over_redis.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { createClient } from 'redis';
22
import * as net from 'net';
33

4-
const client = createClient({url: 'redis://db.occase.de:6379' });
4+
const client = createClient({url: 'redis://aedis.occase.de:63799' });
55
client.on('error', (err) => console.log('Redis Client Error', err));
66
await client.connect();
77

examples/common/serialization.hpp

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/* Copyright (c) 2018-2022 Marcelo Zimbres Silva ([email protected])
2+
*
3+
* Distributed under the Boost Software License, Version 1.0. (See
4+
* accompanying file LICENSE.txt)
5+
*/
6+
7+
#define BOOST_JSON_NO_LIB
8+
#define BOOST_CONTAINER_NO_LIB
9+
#include <boost/redis.hpp>
10+
#include <boost/describe.hpp>
11+
#include <boost/mp11.hpp>
12+
#include <boost/json.hpp>
13+
14+
using namespace boost::describe;
15+
using namespace boost::json;
16+
17+
template <
18+
class T,
19+
class Bd = describe_bases<T, mod_any_access>,
20+
class Md = describe_members<T, mod_any_access>,
21+
class En = std::enable_if_t<!std::is_union<T>::value>
22+
>
23+
std::ostream& operator<<(std::ostream& os, T const & t)
24+
{
25+
os << "{";
26+
27+
bool first = true;
28+
29+
boost::mp11::mp_for_each<Bd>([&](auto D){
30+
31+
if( !first ) { os << ", "; } first = false;
32+
33+
using B = typename decltype(D)::type;
34+
os << (B const&)t;
35+
36+
});
37+
38+
boost::mp11::mp_for_each<Md>([&](auto D){
39+
40+
if( !first ) { os << ", "; } first = false;
41+
42+
os << "." << D.name << " = " << t.*D.pointer;
43+
44+
});
45+
46+
os << "}";
47+
return os;
48+
}
49+
50+
template <
51+
class T,
52+
class D1 = boost::describe::describe_members<T, boost::describe::mod_public | boost::describe::mod_protected>,
53+
class D2 = boost::describe::describe_members<T, boost::describe::mod_private>,
54+
class En = std::enable_if_t<boost::mp11::mp_empty<D2>::value && !std::is_union<T>::value>
55+
>
56+
void tag_invoke(boost::json::value_from_tag const&, boost::json::value& v, T const & t)
57+
{
58+
auto& obj = v.emplace_object();
59+
60+
boost::mp11::mp_for_each<D1>([&](auto D){
61+
62+
obj[ D.name ] = boost::json::value_from( t.*D.pointer );
63+
64+
});
65+
}
66+
67+
template<class T> void extract( boost::json::object const & obj, char const * name, T & value )
68+
{
69+
value = boost::json::value_to<T>( obj.at( name ) );
70+
}
71+
72+
template<class T,
73+
class D1 = boost::describe::describe_members<T,
74+
boost::describe::mod_public | boost::describe::mod_protected>,
75+
class D2 = boost::describe::describe_members<T, boost::describe::mod_private>,
76+
class En = std::enable_if_t<boost::mp11::mp_empty<D2>::value && !std::is_union<T>::value> >
77+
78+
T tag_invoke( boost::json::value_to_tag<T> const&, boost::json::value const& v )
79+
{
80+
auto const& obj = v.as_object();
81+
82+
T t{};
83+
84+
boost::mp11::mp_for_each<D1>([&](auto D){
85+
86+
extract( obj, D.name, t.*D.pointer );
87+
88+
});
89+
90+
return t;
91+
}
92+
93+
// Serialization
94+
template <class T>
95+
void boost_redis_to_bulk(std::string& to, T const& u)
96+
{
97+
boost::redis::resp3::boost_redis_to_bulk(to, serialize(value_from(u)));
98+
}
99+
100+
template <class T>
101+
void boost_redis_from_bulk(T& u, std::string_view sv, boost::system::error_code&)
102+
{
103+
value jv = parse(sv);
104+
u = value_to<T>(jv);
105+
}

examples/cpp20_serialization.cpp

Lines changed: 35 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -6,108 +6,82 @@
66

77
#include <boost/asio.hpp>
88
#if defined(BOOST_ASIO_HAS_CO_AWAIT)
9-
#include <boost/asio/experimental/awaitable_operators.hpp>
10-
#define BOOST_JSON_NO_LIB
11-
#define BOOST_CONTAINER_NO_LIB
12-
#include <boost/json.hpp>
139
#include <boost/redis.hpp>
14-
#include <algorithm>
15-
#include <cstdint>
1610
#include <iostream>
1711
#include <set>
18-
#include <iterator>
1912
#include <string>
2013
#include "common/common.hpp"
14+
#include "common/serialization.hpp"
2115

2216
// Include this in no more than one .cpp file.
2317
#include <boost/json/src.hpp>
2418

2519
namespace net = boost::asio;
2620
namespace redis = boost::redis;
27-
using namespace net::experimental::awaitable_operators;
28-
using namespace boost::json;
2921
using boost::redis::request;
3022
using boost::redis::response;
3123
using boost::redis::ignore_t;
24+
using boost::redis::operation;
3225

3326
struct user {
3427
std::string name;
3528
std::string age;
3629
std::string country;
3730

38-
friend auto operator<(user const& a, user const& b)
39-
{
40-
return std::tie(a.name, a.age, a.country) < std::tie(b.name, b.age, b.country);
41-
}
42-
43-
friend auto operator<<(std::ostream& os, user const& u) -> std::ostream&
44-
{
45-
os << "Name: " << u.name << "\n"
46-
<< "Age: " << u.age << "\n"
47-
<< "Country: " << u.country;
48-
49-
return os;
50-
}
31+
friend
32+
auto operator<(user const& a, user const& b)
33+
{ return std::tie(a.name, a.age, a.country) < std::tie(b.name, b.age, b.country); }
5134
};
5235

53-
// Boost.Json serialization.
54-
void tag_invoke(value_from_tag, value& jv, user const& u)
55-
{
56-
jv =
57-
{ {"name", u.name}
58-
, {"age", u.age}
59-
, {"country", u.country}
60-
};
61-
}
36+
BOOST_DESCRIBE_STRUCT(user, (), (name, age, country))
6237

63-
template<class T>
64-
void extract(object const& obj, T& t, std::string_view key)
38+
auto run(std::shared_ptr<connection> conn, std::string host, std::string port) -> net::awaitable<void>
6539
{
66-
t = value_to<T>(obj.at(key));
40+
co_await connect(conn, host, port);
41+
co_await conn->async_run();
6742
}
6843

69-
auto tag_invoke(value_to_tag<user>, value const& jv)
44+
auto hello(std::shared_ptr<connection> conn) -> net::awaitable<void>
7045
{
71-
user u;
72-
object const& obj = jv.as_object();
73-
extract(obj, u.name, "name");
74-
extract(obj, u.age, "age");
75-
extract(obj, u.country, "country");
76-
return u;
77-
}
46+
request req;
47+
req.push("HELLO", 3);
7848

79-
// Serialization
80-
void boost_redis_to_bulk(std::string& to, user const& u)
81-
{
82-
redis::resp3::boost_redis_to_bulk(to, serialize(value_from(u)));
49+
co_await conn->async_exec(req);
8350
}
8451

85-
void boost_redis_from_bulk(user& u, std::string_view sv, boost::system::error_code&)
86-
{
87-
value jv = parse(sv);
88-
u = value_to<user>(jv);
89-
}
90-
91-
net::awaitable<void> co_main(std::string host, std::string port)
52+
auto sadd(std::shared_ptr<connection> conn) -> net::awaitable<void>
9253
{
9354
std::set<user> users
9455
{{"Joao", "58", "Brazil"} , {"Serge", "60", "France"}};
9556

9657
request req;
97-
req.push("HELLO", 3);
9858
req.push_range("SADD", "sadd-key", users); // Sends
99-
req.push("SMEMBERS", "sadd-key"); // Retrieves
100-
req.push("QUIT");
10159

102-
response<ignore_t, int, std::set<user>, std::string> resp;
60+
co_await conn->async_exec(req);
61+
}
10362

104-
auto conn = std::make_shared<connection>(co_await net::this_coro::executor);
63+
auto smembers(std::shared_ptr<connection> conn) -> net::awaitable<void>
64+
{
65+
request req;
66+
req.push("SMEMBERS", "sadd-key");
10567

106-
co_await connect(conn, host, port);
107-
co_await (conn->async_run() || conn->async_exec(req, resp));
68+
response<std::set<user>> resp;
10869

109-
for (auto const& e: std::get<2>(resp).value())
70+
co_await conn->async_exec(req, resp);
71+
72+
for (auto const& e: std::get<0>(resp).value())
11073
std::cout << e << "\n";
11174
}
11275

76+
net::awaitable<void> co_main(std::string host, std::string port)
77+
{
78+
auto ex = co_await net::this_coro::executor;
79+
auto conn = std::make_shared<connection>(ex);
80+
net::co_spawn(ex, run(conn, host, port), net::detached);
81+
co_await hello(conn);
82+
co_await sadd(conn);
83+
co_await smembers(conn);
84+
conn->cancel(operation::run);
85+
}
86+
11387
#endif // defined(BOOST_ASIO_HAS_CO_AWAIT)

include/boost/redis/connection.hpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,13 @@ class basic_connection :
139139

140140
/** @brief Receives server side pushes asynchronously.
141141
*
142-
* Users that expect server pushes should call this function in a
143-
* loop. If a push arrives and there is no reader, the connection
144-
* will hang.
142+
* When pushes arrive and there is no async_receive operation in
143+
* progress, pushed data, requests, and responses will be paused
144+
* until async_receive is called again. Apps will usually want to
145+
* call `async_receive` in a loop.
146+
*
147+
* To cancel an ongoing receive operation apps should call
148+
* `connection::cancel(operation::receive)`.
145149
*
146150
* @param response The response object.
147151
* @param token The Asio completion token.

0 commit comments

Comments
 (0)