2
0
mirror of https://github.com/boostorg/redis.git synced 2026-01-19 04:42:09 +00:00

Simplifications.

This commit is contained in:
Marcelo Zimbres
2022-04-15 18:26:07 +02:00
parent 27b3bb89fb
commit b68e413351
17 changed files with 150 additions and 189 deletions

View File

@@ -12,17 +12,25 @@
namespace aedis {
namespace adapter {
/** \brief Creates a void response adapter.
/** \brief Creates a dummy response adapter.
\ingroup any
The adapter returned by this function ignores responses and is
useful to avoid wasting time with responses on which the user is
not insterested in.
Example usage:
The adapter returned by this function is dummy which means it
ignores responses. It is useful to avoid wasting time with
responses which are not needed. For example
@code
co_await async_read(socket, buffer, adapt());
// Pushes and writes some commands to the server.
sr.push(command::hello, 3);
sr.push(command::ping);
sr.push(command::quit);
net::write(socket, net::buffer(request));
// Ignores all responses except for the response to ping.
std::string buffer;
resp3::read(socket, dynamic_buffer(buffer), adapt()); // hello
resp3::read(socket, dynamic_buffer(buffer), adapt(resp)); // ping
resp3::read(socket, dynamic_buffer(buffer, adapt())); // quit
@endcode
*/
inline

View File

@@ -27,6 +27,8 @@
#include <aedis/resp3/node.hpp>
#include <aedis/adapter/error.hpp>
// TODO: Add an adapter for float and double, from spirit.
namespace aedis {
namespace adapter {
namespace detail {

View File

@@ -96,94 +96,27 @@ struct assigner<0> {
}
};
template <std::size_t N>
struct assigner2 {
template <class T1, class T2>
static void assign(T1& dest, T2& from)
{
std::get<N>(dest) = internal_adapt(std::get<N>(from));
assigner2<N - 1>::assign(dest, from);
}
};
template <>
struct assigner2<0> {
template <class T1, class T2>
static void assign(T1& dest, T2& from)
{
std::get<0>(dest) = internal_adapt(std::get<0>(from));
}
};
} // detail
/** @brief Return a specific adapter from the tuple.
*
* \param t A tuple of response adapters.
* \return The adapter that corresponds to type T.
*/
template <class T, class Tuple>
auto& get(Tuple& t)
{
return std::get<typename response_traits<T>::adapter_type>(t);
}
template <class Tuple>
using adapters_array_t =
std::array<
boost::mp11::mp_unique<
boost::mp11::mp_rename<
boost::mp11::mp_transform<
adapter_t, Tuple>,
boost::variant2::variant>>,
std::tuple_size<Tuple>::value>;
template <class Tuple>
adapters_array_t<Tuple> make_adapters_array(Tuple& t)
{
adapters_array_t<Tuple> ret;
detail::assigner<std::tuple_size<Tuple>::value - 1>::assign(ret, t);
return ret;
}
/** @brief Transaforms a tuple of responses.
*
* @return Transaforms a tuple of responses into a tuple of adapters.
*/
template <class Tuple>
using adapters_tuple_t =
boost::mp11::mp_rename<
boost::mp11::mp_transform<
adapter_t, Tuple>,
std::tuple>;
/** @brief Make a tuple of adapters.
*
* \param t Tuple of responses.
* \return Tuple of adapters.
*/
template <class Tuple>
auto
make_adapters_tuple(Tuple& t)
{
adapters_tuple_t<Tuple> ret;
detail::assigner2<std::tuple_size<Tuple>::value - 1>::assign(ret, t);
return ret;
}
namespace detail {
template <class Tuple>
class static_aggregate_adapter {
private:
using adapters_array_type =
std::array<
boost::mp11::mp_unique<
boost::mp11::mp_rename<
boost::mp11::mp_transform<
adapter_t, Tuple>,
boost::variant2::variant>>,
std::tuple_size<Tuple>::value>;
std::size_t i_ = 0;
std::size_t aggregate_size_ = 0;
adapters_array_t<Tuple> adapters_;
adapters_array_type adapters_;
public:
static_aggregate_adapter(Tuple* r = nullptr)
: adapters_(make_adapters_array(*r))
{}
{
detail::assigner<std::tuple_size<Tuple>::value - 1>::assign(adapters_, *r);
}
void count(resp3::node<boost::string_view> const& nd)
{

View File

@@ -61,17 +61,19 @@
tcp_socket socket{co_await net::this_coro::executor};
co_await socket.async_connect(ep);
std::string request, read_buffer, response;
std::string buffer, response;
auto sr = make_serializer(request);
sr.push(command::hello, 3);
sr.push(command::set, "key", "Value", "EX", "2", "get");
sr.push(command::quit);
co_await net::async_write(socket, net::buffer(request));
co_await net::async_write(socket, net::buffer(buffer));
buffer.clear();
co_await resp3::async_read(socket, dynamic_buffer(read_buffer)); // Hello (ignored).
co_await resp3::async_read(socket, dynamic_buffer(read_buffer), adapt(response)); // Set
co_await resp3::async_read(socket, dynamic_buffer(read_buffer)); // Quit (ignored)
auto dbuffer = net::dynamic_buffer(read_buffer);
co_await resp3::async_read(socket, dbuffer); // Hello ignored.
co_await resp3::async_read(socket, dbuffer, adapt(response)); // Set
co_await resp3::async_read(socket, dbuffer); // Quit ignored.
co_return response;
}

View File

@@ -19,6 +19,8 @@
// TODO: What to do if users send a discard command not contained in a
// transaction. The client object will try to pop the queue until a
// multi is found.
//
// TODO: Pass max_size as a config parameter to the dynamic buffer.
namespace aedis {
namespace generic {
@@ -267,6 +269,7 @@ private:
std::string requests_;
// The commands contained in the requests.
// TODO: also keep the keys in addition to the commands.
std::vector<Command> commands_;
// Info about the requests.

View File

@@ -170,7 +170,7 @@ struct read_op {
}
assert(!cli->read_buffer_.empty());
t = resp3::detail::to_type(cli->read_buffer_.front());
t = resp3::to_type(cli->read_buffer_.front());
cmd = Command::invalid;
if (t != resp3::type::push) {
assert(!cli->commands_.empty());

View File

@@ -22,29 +22,6 @@ std::size_t parse_uint(char const* data, std::size_t size, boost::system::error_
return ret;
}
type to_type(char c)
{
switch (c) {
case '!': return type::blob_error;
case '=': return type::verbatim_string;
case '$': return type::blob_string;
case ';': return type::streamed_string_part;
case '-': return type::simple_error;
case ':': return type::number;
case ',': return type::doublean;
case '#': return type::boolean;
case '(': return type::big_number;
case '+': return type::simple_string;
case '_': return type::null;
case '>': return type::push;
case '~': return type::set;
case '*': return type::array;
case '|': return type::attribute;
case '%': return type::map;
default: return type::invalid;
}
}
} // detail
} // resp3
} // aedis

View File

@@ -24,9 +24,6 @@ namespace detail {
std::size_t parse_uint(char const* data, std::size_t size, boost::system::error_code& ec);
// Converts a wire-format RESP3 type (char) to a resp3 type.
type to_type(char c);
template <class ResponseAdapter>
class parser {
private:

View File

@@ -90,5 +90,28 @@ char to_code(type t)
}
}
type to_type(char c)
{
switch (c) {
case '!': return type::blob_error;
case '=': return type::verbatim_string;
case '$': return type::blob_string;
case ';': return type::streamed_string_part;
case '-': return type::simple_error;
case ':': return type::number;
case ',': return type::doublean;
case '#': return type::boolean;
case '(': return type::big_number;
case '+': return type::simple_string;
case '_': return type::null;
case '>': return type::push;
case '~': return type::set;
case '*': return type::array;
case '|': return type::attribute;
case '%': return type::map;
default: return type::invalid;
}
}
} // resp3
} // aedis

View File

@@ -86,13 +86,11 @@ bool is_aggregate(type t);
*/
std::size_t element_multiplicity(type t);
/** @brief Returns the wire code of a given type.
* @ingroup any
*
* @param t The RESP3 type.
* @return The code (\c char) that identifies the data type on the wire.
*/
// Returns the wire code of a given type.
char to_code(type t);
// Converts a wire-format RESP3 type (char) to a resp3 type.
type to_type(char c);
} // resp3
} // aedis

View File

@@ -16,10 +16,12 @@
namespace net = boost::asio;
using aedis::resp3::node;
using aedis::adapter::adapt;
using aedis::adapter::adapter_t;
using aedis::redis::command;
using client_type = aedis::generic::client<net::ip::tcp::socket, command>;
using aedis::generic::client;
using client_type = client<net::ip::tcp::socket, command>;
using response_type = std::vector<node<std::string>>;
using adapter_type = aedis::adapter::adapter_t<response_type>;
// Prints aggregates that don't contain any nested aggregates.
void print_aggregate(response_type const& v)
@@ -96,7 +98,7 @@ public:
private:
response_type resp_;
adapter_type adapter_;
adapter_t<response_type> adapter_;
client_type* db_;
};

View File

@@ -18,13 +18,14 @@
namespace net = boost::asio;
using aedis::resp3::node;
using aedis::adapter::adapt;
using aedis::adapter::adapter_t;
using aedis::redis::command;
using aedis::generic::client;
using aedis::user_session;
using aedis::user_session_base;
using client_type = client<net::ip::tcp::socket, command>;
using response_type = std::vector<node<std::string>>;
using adapter_type = aedis::adapter::adapter_t<response_type>;
class receiver {
public:
@@ -78,7 +79,7 @@ public:
private:
response_type resp_;
adapter_type adapter_;
adapter_t<response_type> adapter_;
std::shared_ptr<client_type> db_;
std::vector<std::shared_ptr<user_session_base>> sessions_;
};

View File

@@ -20,13 +20,14 @@
namespace net = boost::asio;
using aedis::resp3::node;
using aedis::adapter::adapt;
using aedis::adapter::adapter_t;
using aedis::redis::command;
using aedis::generic::client;
using aedis::user_session;
using aedis::user_session_base;
using client_type = client<net::ip::tcp::socket, command>;
using response_type = std::vector<node<std::string>>;
using adapter_type = aedis::adapter::adapter_t<response_type>;
class receiver {
public:
@@ -76,7 +77,7 @@ public:
private:
response_type resp_;
adapter_type adapter_;
adapter_t<response_type> adapter_;
std::shared_ptr<client_type> db_;
std::queue<std::shared_ptr<user_session_base>> sessions_;
};

View File

@@ -13,12 +13,13 @@
namespace net = boost::asio;
using aedis::resp3::node;
using aedis::adapter::adapter_t;
using aedis::adapter::adapt;
using aedis::redis::command;
using aedis::generic::client;
using aedis::adapter::adapt;
using client_type = client<net::ip::tcp::socket, command>;
using response_type = node<std::string>;
using adapter_type = aedis::adapter::adapter_t<response_type>;
struct myreceiver {
public:
@@ -61,7 +62,7 @@ public:
private:
response_type resp_;
adapter_type adapter_;
adapter_t<response_type> adapter_;
client_type* db_;
};

View File

@@ -20,20 +20,17 @@ struct mystruct {
namespace net = boost::asio;
namespace adapter = aedis::adapter;
using aedis::resp3::node;
using aedis::adapter::adapters_tuple_t;
using aedis::adapter::make_adapters_tuple;
using aedis::adapter::get;
using aedis::adapter::adapt;
using aedis::adapter::adapter_t;
using aedis::redis::command;
using aedis::generic::client;
using client_type = client<net::ip::tcp::socket, command>;
using responses_tuple_type =
std::tuple<
boost::optional<mystruct>,
std::list<mystruct>,
std::set<mystruct>,
std::map<std::string, mystruct>
>;
using adapters_tuple_type = adapters_tuple_t<responses_tuple_type>;
// Response types used in the example.
using T0 = boost::optional<mystruct>;
using T1 = std::list<mystruct>;
using T2 = std::set<mystruct>;
using T3 = std::map<std::string, mystruct>;
std::ostream& operator<<(std::ostream& os, mystruct const& obj)
{
@@ -64,7 +61,10 @@ void from_string(mystruct& obj, boost::string_view sv, boost::system::error_code
class receiver {
public:
receiver(client_type& db)
: adapters_(make_adapters_tuple(resps_))
: adapter0_(adapt(resp0_))
, adapter1_(adapt(resp1_))
, adapter2_(adapt(resp2_))
, adapter3_(adapt(resp3_))
, db_{&db} {}
void on_connect()
@@ -79,10 +79,10 @@ public:
boost::system::error_code& ec)
{
switch (cmd) {
case command::get: adapter::get<boost::optional<mystruct>>(adapters_)(nd, ec); break;
case command::lrange: adapter::get<std::list<mystruct>>(adapters_)(nd, ec); break;
case command::smembers: adapter::get<std::set<mystruct>>(adapters_)(nd, ec); break;
case command::hgetall: adapter::get<std::map<std::string, mystruct>>(adapters_)(nd, ec); break;
case command::get: adapter0_(nd, ec); break;
case command::lrange: adapter1_(nd, ec); break;
case command::smembers: adapter2_(nd, ec); break;
case command::hgetall: adapter3_(nd, ec); break;
default:; // Ignore
}
}
@@ -122,33 +122,33 @@ public:
case command::get:
{
if (std::get<boost::optional<mystruct>>(resps_).has_value()) {
std::cout << std::get<boost::optional<mystruct>>(resps_).value() << "\n\n";
std::get<boost::optional<mystruct>>(resps_).reset();
if (resp0_.has_value()) {
std::cout << resp0_.value() << "\n\n";
resp0_.reset();
} else {
std::cout << "Expired." << "\n";
}
} break;
case command::lrange:
for (auto const& e: std::get<std::list<mystruct>>(resps_))
for (auto const& e: resp1_)
std::cout << e << "\n";
std::cout << "\n";
std::get<std::list<mystruct>>(resps_).clear();
resp1_.clear();
break;
case command::smembers:
for (auto const& e: std::get<std::set<mystruct>>(resps_))
for (auto const& e: resp2_)
std::cout << e << "\n";
std::cout << "\n";
std::get<std::set<mystruct>>(resps_).clear();
resp2_.clear();
break;
case command::hgetall:
for (auto const& e: std::get<std::map<std::string, mystruct>>(resps_))
for (auto const& e: resp3_)
std::cout << e.first << ", " << e.second << std::endl;
std::cout << "\n";
std::get<std::map<std::string, mystruct>>(resps_).clear();
resp3_.clear();
break;
default:;
@@ -163,8 +163,16 @@ public:
void on_push() { }
private:
responses_tuple_type resps_;
adapters_tuple_type adapters_;
T0 resp0_;
T1 resp1_;
T2 resp2_;
T3 resp3_;
adapter_t<T0> adapter0_;
adapter_t<T1> adapter1_;
adapter_t<T2> adapter2_;
adapter_t<T3> adapter3_;
client_type* db_;
};

View File

@@ -15,19 +15,16 @@
namespace net = boost::asio;
namespace adapter = aedis::adapter;
using aedis::resp3::node;
using aedis::adapter::adapt;
using aedis::adapter::adapter_t;
using aedis::redis::command;
using aedis::generic::client;
using aedis::adapter::adapt;
using aedis::adapter::adapters_tuple_t;
using aedis::adapter::make_adapters_tuple;
// Responses used in the example.
using T0 = std::list<int>;
using T1 = boost::optional<std::set<std::string>>;
using client_type = client<net::ip::tcp::socket, command>;
using responses_tuple_type =
std::tuple<
std::list<int>,
boost::optional<std::set<std::string>>,
std::vector<node<std::string>>
>;
using adapters_tuple_type = adapters_tuple_t<responses_tuple_type>;
template <class Container>
void print_and_clear(Container& cont)
@@ -38,10 +35,11 @@ void print_and_clear(Container& cont)
cont.clear();
}
class myreceiver {
class receiver {
public:
myreceiver(client_type& db)
: adapters_(make_adapters_tuple(resps_))
receiver(client_type& db)
: adapter0_(adapt(resp0_))
, adapter1_(adapt(resp1_))
, db_{&db} {}
void on_connect()
@@ -56,8 +54,8 @@ public:
boost::system::error_code& ec)
{
switch (cmd) {
case command::lrange: adapter::get<std::list<int>>(adapters_)(nd, ec); break;
case command::smembers: adapter::get<boost::optional<std::set<std::string>>>(adapters_)(nd, ec); break;
case command::lrange: adapter0_(nd, ec); break;
case command::smembers: adapter1_(nd, ec); break;
default:;
}
}
@@ -92,11 +90,11 @@ public:
} break;
case command::lrange:
print_and_clear(std::get<std::list<int>>(resps_));
print_and_clear(resp0_);
break;
case command::smembers:
print_and_clear(std::get<boost::optional<std::set<std::string>>>(resps_).value());
print_and_clear(resp1_.value());
break;
default:;
@@ -111,8 +109,14 @@ public:
void on_push() { }
private:
responses_tuple_type resps_;
adapters_tuple_type adapters_;
// Responses
T0 resp0_;
T1 resp1_;
// Adapters.
adapter_t<T0> adapter0_;
adapter_t<T1> adapter1_;
client_type* db_;
};
@@ -120,7 +124,7 @@ int main()
{
net::io_context ioc;
client_type db{ioc.get_executor()};
myreceiver recv{db};
receiver recv{db};
db.async_run(
recv,

View File

@@ -41,22 +41,23 @@ int main()
net::connect(socket, res);
// Creates and sends a request to redis.
std::string request;
auto sr = make_serializer(request);
std::string buffer;
auto sr = make_serializer(buffer);
sr.push(command::hello, 3);
sr.push(command::ping);
sr.push(command::quit);
net::write(socket, net::buffer(request));
net::write(socket, net::buffer(buffer));
buffer.clear();
// Responses
std::string resp;
hello_type hello;
// Reads the responses to all commands in the request.
std::string buffer;
resp3::read(socket, dynamic_buffer(buffer), adapt(hello)); // hello
resp3::read(socket, dynamic_buffer(buffer), adapt(resp));
resp3::read(socket, dynamic_buffer(buffer)); // quit (ignored)
auto dbuffer = dynamic_buffer(buffer);
resp3::read(socket, dbuffer, adapt(hello));
resp3::read(socket, dbuffer, adapt(resp));
resp3::read(socket, dbuffer);
std::cout << std::get<0>(hello) << ": " << std::get<1>(hello) << std::endl;
std::cout << "Ping: " << resp << std::endl;