diff --git a/CMakeLists.txt b/CMakeLists.txt index 588b3279..4fe6e42d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -134,13 +134,13 @@ if (MSVC) target_compile_definitions(cpp20_resolve_with_sentinel PRIVATE _WIN32_WINNT=0x0601) endif() -add_executable(cpp20_serialization examples/cpp20_serialization.cpp) -target_compile_features(cpp20_serialization PUBLIC cxx_std_20) -target_link_libraries(cpp20_serialization common) -add_test(cpp20_serialization cpp20_serialization) +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) if (MSVC) - target_compile_options(cpp20_serialization PRIVATE /bigobj) - target_compile_definitions(cpp20_serialization PRIVATE _WIN32_WINNT=0x0601) + target_compile_options(cpp20_json_serialization PRIVATE /bigobj) + target_compile_definitions(cpp20_json_serialization PRIVATE _WIN32_WINNT=0x0601) endif() add_executable(cpp20_subscriber examples/cpp20_subscriber.cpp) diff --git a/README.md b/README.md index da615b2a..0ce44e7d 100644 --- a/README.md +++ b/README.md @@ -881,6 +881,7 @@ Acknowledgement to people that helped shape Boost.Redis next. Now requests are continuously coalesced and written to the socket. This made the request::coalesce unnecessary and threfore it was removed. +* Native json support for Boost.Describe'd classes. ### v1.4.0-1 diff --git a/examples/common/serialization.hpp b/examples/common/serialization.hpp deleted file mode 100644 index 6bdf850b..00000000 --- a/examples/common/serialization.hpp +++ /dev/null @@ -1,105 +0,0 @@ -/* Copyright (c) 2018-2022 Marcelo Zimbres Silva (mzimbres@gmail.com) - * - * Distributed under the Boost Software License, Version 1.0. (See - * accompanying file LICENSE.txt) - */ - -#define BOOST_JSON_NO_LIB -#define BOOST_CONTAINER_NO_LIB -#include -#include -#include -#include - -using namespace boost::describe; -using namespace boost::json; - -template < - class T, - class Bd = describe_bases, - class Md = describe_members, - class En = std::enable_if_t::value> - > -std::ostream& operator<<(std::ostream& os, T const & t) -{ - os << "{"; - - bool first = true; - - boost::mp11::mp_for_each([&](auto D){ - - if( !first ) { os << ", "; } first = false; - - using B = typename decltype(D)::type; - os << (B const&)t; - - }); - - boost::mp11::mp_for_each([&](auto D){ - - if( !first ) { os << ", "; } first = false; - - os << "." << D.name << " = " << t.*D.pointer; - - }); - - os << "}"; - return os; -} - -template < - class T, - class D1 = boost::describe::describe_members, - class D2 = boost::describe::describe_members, - class En = std::enable_if_t::value && !std::is_union::value> - > -void tag_invoke(boost::json::value_from_tag const&, boost::json::value& v, T const & t) -{ - auto& obj = v.emplace_object(); - - boost::mp11::mp_for_each([&](auto D){ - - obj[ D.name ] = boost::json::value_from( t.*D.pointer ); - - }); -} - -template void extract( boost::json::object const & obj, char const * name, T & value ) -{ - value = boost::json::value_to( obj.at( name ) ); -} - -template, - class D2 = boost::describe::describe_members, - class En = std::enable_if_t::value && !std::is_union::value> > - - T tag_invoke( boost::json::value_to_tag const&, boost::json::value const& v ) -{ - auto const& obj = v.as_object(); - - T t{}; - - boost::mp11::mp_for_each([&](auto D){ - - extract( obj, D.name, t.*D.pointer ); - - }); - - return t; -} - -// Serialization -template -void boost_redis_to_bulk(std::string& to, T const& u) -{ - boost::redis::resp3::boost_redis_to_bulk(to, serialize(value_from(u))); -} - -template -void boost_redis_from_bulk(T& u, std::string_view sv, boost::system::error_code&) -{ - value jv = parse(sv); - u = value_to(jv); -} diff --git a/examples/cpp20_serialization.cpp b/examples/cpp20_json_serialization.cpp similarity index 70% rename from examples/cpp20_serialization.cpp rename to examples/cpp20_json_serialization.cpp index c07da1b0..1be5b39a 100644 --- a/examples/cpp20_serialization.cpp +++ b/examples/cpp20_json_serialization.cpp @@ -6,22 +6,26 @@ #include #if defined(BOOST_ASIO_HAS_CO_AWAIT) +#define BOOST_JSON_NO_LIB +#define BOOST_CONTAINER_NO_LIB #include -#include +#include +#include #include #include +#include #include "common/common.hpp" -#include "common/serialization.hpp" // Include this in no more than one .cpp file. #include namespace net = boost::asio; namespace redis = boost::redis; +using namespace boost::describe; using boost::redis::request; using boost::redis::response; -using boost::redis::ignore_t; using boost::redis::operation; +using boost::redis::ignore_t; struct user { std::string name; @@ -41,46 +45,34 @@ auto run(std::shared_ptr conn, std::string host, std::string port) - co_await conn->async_run(); } -auto hello(std::shared_ptr conn) -> net::awaitable -{ - request req; - req.push("HELLO", 3); - - co_await conn->async_exec(req); -} - -auto sadd(std::shared_ptr conn) -> net::awaitable -{ - std::set users - {{"Joao", "58", "Brazil"} , {"Serge", "60", "France"}}; - - request req; - req.push_range("SADD", "sadd-key", users); // Sends - - co_await conn->async_exec(req); -} - -auto smembers(std::shared_ptr conn) -> net::awaitable -{ - request req; - req.push("SMEMBERS", "sadd-key"); - - response> resp; - - co_await conn->async_exec(req, resp); - - for (auto const& e: std::get<0>(resp).value()) - std::cout << e << "\n"; -} - net::awaitable co_main(std::string host, std::string port) { auto ex = co_await net::this_coro::executor; auto conn = std::make_shared(ex); net::co_spawn(ex, run(conn, host, port), net::detached); - co_await hello(conn); - co_await sadd(conn); - co_await smembers(conn); + + // A set of users that will be automatically serialized to json. + std::set users + {{"Joao", "58", "Brazil"} , {"Serge", "60", "France"}}; + + // To simplify we send the set and retrieve it in the same + // resquest. + request req; + req.push("HELLO", 3); + req.push_range("SADD", "sadd-key", users); + req.push("SMEMBERS", "sadd-key"); + + // The response will contain the deserialized set, which should + // match the one we sent. + response> resp; + + // Sends the request and receives the response. + co_await conn->async_exec(req, resp); + + // Print. + for (auto const& e: std::get<2>(resp).value()) + std::cout << e.name << " " << e.age << " " << e.country << "\n"; + conn->cancel(operation::run); } diff --git a/include/boost/redis/json.hpp b/include/boost/redis/json.hpp new file mode 100644 index 00000000..2d75c941 --- /dev/null +++ b/include/boost/redis/json.hpp @@ -0,0 +1,34 @@ +/* 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_JSON_HPP +#define BOOST_REDIS_JSON_HPP + +#include +#include + +namespace boost::redis::json +{ + +template +void boost_redis_to_bulk(std::string& to, T const& u) +{ + redis::resp3::boost_redis_to_bulk(to, boost::json::serialize(boost::json::value_from(u))); +} + +template +void boost_redis_from_bulk(T& u, std::string_view sv, system::error_code&) +{ + auto const jv = boost::json::parse(sv); + u = boost::json::value_to(jv); +} + +} // boost::redis::json + +using boost::redis::json::boost_redis_to_bulk; +using boost::redis::json::boost_redis_from_bulk; + +#endif // BOOST_REDIS_JSON_HPP diff --git a/include/boost/redis/resp3/serialization.hpp b/include/boost/redis/resp3/serialization.hpp index 3977cb2c..48637851 100644 --- a/include/boost/redis/resp3/serialization.hpp +++ b/include/boost/redis/resp3/serialization.hpp @@ -43,7 +43,7 @@ template ::value void boost_redis_to_bulk(std::string& payload, T n) { auto const s = std::to_string(n); - boost_redis_to_bulk(payload, std::string_view{s}); + boost::redis::resp3::boost_redis_to_bulk(payload, std::string_view{s}); } template