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

Ports to C++14.

This commit is contained in:
Marcelo Zimbres
2022-04-05 16:39:13 +02:00
parent 379da7a340
commit 5c23299a8a
40 changed files with 272 additions and 290 deletions

View File

@@ -6,6 +6,7 @@ DISTCHECK_CONFIGURE_FLAGS = CPPFLAGS="$(BOOST_CPPFLAGS) $(CPPFLAGS)" LDFLAGS="$(
AM_CPPFLAGS =
AM_CPPFLAGS += $(BOOST_CPPFLAGS)
AM_CPPFLAGS += -I$(top_srcdir)/include
AM_LDFLAGS =
AM_LDFLAGS += -pthread
@@ -60,34 +61,34 @@ high_level_chat_room_SOURCES = $(top_srcdir)/examples/high_level/chat_room.cpp
endif
nobase_include_HEADERS =\
$(top_srcdir)/aedis/src.hpp\
$(top_srcdir)/aedis/redis/command.hpp\
$(top_srcdir)/aedis/redis/client.hpp\
$(top_srcdir)/aedis/redis/receiver_base.hpp\
$(top_srcdir)/aedis/generic/client.hpp\
$(top_srcdir)/aedis/generic/receiver_base.hpp\
$(top_srcdir)/aedis/generic/serializer.hpp\
$(top_srcdir)/aedis/generic/detail/client_ops.hpp\
$(top_srcdir)/aedis/sentinel/command.hpp\
$(top_srcdir)/aedis/sentinel/client.hpp\
$(top_srcdir)/aedis/sentinel/receiver_base.hpp\
$(top_srcdir)/aedis/aedis.hpp\
$(top_srcdir)/aedis/adapter/detail/adapters.hpp\
$(top_srcdir)/aedis/adapter/error.hpp\
$(top_srcdir)/aedis/adapter/adapt.hpp\
$(top_srcdir)/aedis/adapter/response_traits.hpp\
$(top_srcdir)/aedis/resp3/node.hpp\
$(top_srcdir)/aedis/resp3/compose.hpp\
$(top_srcdir)/aedis/resp3/detail/read_ops.hpp\
$(top_srcdir)/aedis/resp3/detail/parser.hpp\
$(top_srcdir)/aedis/resp3/error.hpp\
$(top_srcdir)/aedis/resp3/type.hpp\
$(top_srcdir)/aedis/resp3/read.hpp\
$(top_srcdir)/aedis/redis/impl/command.ipp\
$(top_srcdir)/aedis/sentinel/impl/command.ipp\
$(top_srcdir)/aedis/resp3/detail/impl/parser.ipp\
$(top_srcdir)/aedis/resp3/impl/type.ipp\
$(top_srcdir)/aedis/redis/client.hpp
$(top_srcdir)/include/aedis/src.hpp\
$(top_srcdir)/include/aedis/redis/command.hpp\
$(top_srcdir)/include/aedis/redis/client.hpp\
$(top_srcdir)/include/aedis/redis/receiver_base.hpp\
$(top_srcdir)/include/aedis/generic/client.hpp\
$(top_srcdir)/include/aedis/generic/receiver_base.hpp\
$(top_srcdir)/include/aedis/generic/serializer.hpp\
$(top_srcdir)/include/aedis/generic/detail/client_ops.hpp\
$(top_srcdir)/include/aedis/sentinel/command.hpp\
$(top_srcdir)/include/aedis/sentinel/client.hpp\
$(top_srcdir)/include/aedis/sentinel/receiver_base.hpp\
$(top_srcdir)/include/aedis/aedis.hpp\
$(top_srcdir)/include/aedis/adapter/detail/adapters.hpp\
$(top_srcdir)/include/aedis/adapter/error.hpp\
$(top_srcdir)/include/aedis/adapter/adapt.hpp\
$(top_srcdir)/include/aedis/adapter/response_traits.hpp\
$(top_srcdir)/include/aedis/resp3/node.hpp\
$(top_srcdir)/include/aedis/resp3/compose.hpp\
$(top_srcdir)/include/aedis/resp3/detail/read_ops.hpp\
$(top_srcdir)/include/aedis/resp3/detail/parser.hpp\
$(top_srcdir)/include/aedis/resp3/error.hpp\
$(top_srcdir)/include/aedis/resp3/type.hpp\
$(top_srcdir)/include/aedis/resp3/read.hpp\
$(top_srcdir)/include/aedis/redis/impl/command.ipp\
$(top_srcdir)/include/aedis/sentinel/impl/command.ipp\
$(top_srcdir)/include/aedis/resp3/detail/impl/parser.ipp\
$(top_srcdir)/include/aedis/resp3/impl/type.ipp\
$(top_srcdir)/include/aedis/redis/client.hpp
nobase_noinst_HEADERS =\
$(top_srcdir)/examples/high_level/user_session.hpp\

View File

@@ -1,117 +0,0 @@
/* Copyright (c) 2019 Marcelo Zimbres Silva (mzimbres@gmail.com)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <string>
#include <string_view>
#include <utility>
namespace aedis {
namespace resp3 {
/** @brief Adds data to stora.
* @ingroup any
*/
template <class Storage>
void to_bulk(Storage& to, std::string_view data)
{
auto const str = std::to_string(std::size(data));
to += "$";
to.append(std::cbegin(str), std::cend(str));
to += "\r\n";
to += data;
to += "\r\n";
}
template <class Storage, class T, typename = typename std::enable_if<std::is_integral<T>::value>::type>
void to_bulk(Storage& to, T n)
{
auto const s = std::to_string(n);
to_bulk(to, std::string_view{s});
}
namespace detail {
template <class>
struct needs_to_string : std::true_type {};
template <> struct needs_to_string<std::string> : std::false_type {};
template <> struct needs_to_string<std::string_view> : std::false_type {};
template <> struct needs_to_string<char const*> : std::false_type {};
template <> struct needs_to_string<char*> : std::false_type {};
template <std::size_t N>
struct needs_to_string<char[N]> : std::false_type {};
template <std::size_t N>
struct needs_to_string<char const[N]> : std::false_type {};
template <class Storage, class T>
struct add_bulk_impl {
static void add(Storage& to, T const& from)
{
using namespace aedis::resp3;
to_bulk(to, from);
}
};
template <class Storage, class U, class V>
struct add_bulk_impl<Storage, std::pair<U, V>> {
static void add(Storage& to, std::pair<U, V> const& from)
{
using namespace aedis::resp3;
to_bulk(to, from.first);
to_bulk(to, from.second);
}
};
} // detail
/** @brief Adds a resp3 header to the store to.
* @ingroup any
*/
template <class Storage>
void add_header(Storage& to, std::size_t size)
{
auto const str = std::to_string(size);
to += "*";
to.append(std::cbegin(str), std::cend(str));
to += "\r\n";
}
/** @brief Adds a rep3 bulk to the storage.
* @ingroup any
*
* This function adds \c data as a bulk string to the storage \c to.
*/
template <class Storage, class T>
void add_bulk(Storage& to, T const& data)
{
detail::add_bulk_impl<Storage, T>::add(to, data);
}
/** @brief Counts the number of bulks required by a given type.
* @ingroup any
*/
template <class>
struct bulk_counter;
template <class>
struct bulk_counter {
static constexpr auto size = 1U;
};
template <class T, class U>
struct bulk_counter<std::pair<T, U>> {
static constexpr auto size = 2U;
};
} // resp3
} // aedis

View File

@@ -18,7 +18,7 @@ AC_CHECK_HEADER_STDBOOL
AC_TYPE_UINT64_T
AC_CHECK_TYPES([ptrdiff_t])
AX_CXX_COMPILE_STDCXX(17, , mandatory)
AX_CXX_COMPILE_STDCXX(14, , mandatory)
AX_CXX_COMPILE_STDCXX(20, , optional)
AM_CONDITIONAL(HAVE_CXX20,[test x$HAVE_CXX20 == x1])

View File

@@ -823,7 +823,7 @@ WARN_LOGFILE =
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.
INPUT = aedis examples
INPUT = include/aedis examples
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@@ -850,52 +850,7 @@ INPUT_ENCODING = UTF-8
# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f, *.for, *.tcl, *.vhd,
# *.vhdl, *.ucf, *.qsf and *.ice.
FILE_PATTERNS = *.c \
*.cc \
*.cxx \
*.cpp \
*.c++ \
*.java \
*.ii \
*.ixx \
*.ipp \
*.i++ \
*.inl \
*.ddl \
*.odl \
*.h \
*.hh \
*.hxx \
*.hpp \
*.h++ \
*.cs \
*.d \
*.php \
*.php4 \
*.php5 \
*.phtml \
*.inc \
*.m \
*.markdown \
*.md \
*.mm \
*.dox \
*.doc \
*.txt \
*.py \
*.pyw \
*.f90 \
*.f95 \
*.f03 \
*.f08 \
*.f \
*.for \
*.tcl \
*.vhd \
*.vhdl \
*.ucf \
*.qsf \
*.ice
FILE_PATTERNS = *.hpp
# The RECURSIVE tag can be used to specify whether or not subdirectories should
# be searched for input files as well.
@@ -910,7 +865,7 @@ RECURSIVE = YES
# Note that relative paths are relative to the directory from which doxygen is
# run.
EXCLUDE = include/aedis/impl include/aedis/resp3/impl include/aedis/resp3/detail
EXCLUDE =
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded

View File

@@ -56,7 +56,7 @@ using transaction_type =
// One tuple element for each expected request.
using receiver_type =
receiver_base<
std::optional<mystruct>, // get
boost::optional<mystruct>, // get
std::list<mystruct>, // lrange
std::set<mystruct>, // smembers
std::map<std::string, mystruct>, // hgetall
@@ -73,7 +73,7 @@ private:
int to_index_impl(command cmd) override
{
switch (cmd) {
case command::get: return index_of<std::optional<mystruct>>();
case command::get: return index_of<boost::optional<mystruct>>();
case command::lrange: return index_of<std::list<mystruct>>();
case command::smembers: return index_of<std::set<mystruct>>();
case command::hgetall: return index_of<std::map<std::string, mystruct>>();
@@ -124,9 +124,9 @@ private:
case command::get:
{
if (get<std::optional<mystruct>>().has_value()) {
std::cout << get<std::optional<mystruct>>().value() << "\n\n";
get<std::optional<mystruct>>().reset();
if (get<boost::optional<mystruct>>().has_value()) {
std::cout << get<boost::optional<mystruct>>().value() << "\n\n";
get<boost::optional<mystruct>>().reset();
} else {
std::cout << "Expired." << "\n";
}

View File

@@ -32,7 +32,7 @@ void print_and_clear(Container& cont)
using receiver_type =
receiver_base<
std::list<int>,
std::optional<std::set<std::string>>,
boost::optional<std::set<std::string>>,
std::vector<node<std::string>>
>;
@@ -47,7 +47,7 @@ private:
{
switch (cmd) {
case command::lrange: return index_of<std::list<int>>();
case command::smembers: return index_of<std::optional<std::set<std::string>>>();
case command::smembers: return index_of<boost::optional<std::set<std::string>>>();
default: return -1;
}
}
@@ -86,7 +86,7 @@ private:
break;
case command::smembers:
print_and_clear(get<std::optional<std::set<std::string>>>().value());
print_and_clear(get<boost::optional<std::set<std::string>>>().value());
break;
default:;

View File

@@ -25,7 +25,7 @@ using net::write;
using net::buffer;
using net::dynamic_buffer;
using tcp_socket = net::use_awaitable_t<>::as_default_on_t<net::ip::tcp::socket>;
using response_type = std::tuple<std::string, std::optional<std::string>>;
using response_type = std::tuple<std::string, boost::optional<std::string>>;
net::awaitable<void> example()
{
@@ -46,7 +46,7 @@ net::awaitable<void> example()
sr.push(command::quit);
co_await net::async_write(socket, buffer(request));
std::tuple<std::string, std::optional<std::string>> response;
std::tuple<std::string, boost::optional<std::string>> response;
std::string buffer;
co_await resp3::async_read(socket, dynamic_buffer(buffer));

View File

@@ -26,7 +26,7 @@ using net::write;
using net::buffer;
using net::dynamic_buffer;
using tcp_socket = net::use_awaitable_t<>::as_default_on_t<net::ip::tcp::socket>;
using response_type = std::tuple<std::string, std::optional<std::string>>;
using response_type = std::tuple<std::string, boost::optional<std::string>>;
net::awaitable<void> example()
{

View File

@@ -55,7 +55,7 @@ auto adapt() noexcept
*
* - `std::unordered_map<T>`. Can be used with RESP3 hash type.
*
* All these types can be wrapped in an `std::optional<T>`. This
* All these types can be wrapped in an `boost::optional<T>`. This
* function also support \c std::tuple to read the response to
* tuples. At the moment this feature supports only transactions that
* contain simple types or aggregates that don't contain aggregates

View File

@@ -10,17 +10,18 @@
#include <set>
#include <unordered_set>
#include <forward_list>
#include <optional>
#include <system_error>
#include <map>
#include <unordered_map>
#include <list>
#include <deque>
#include <vector>
#include <charconv>
#include <array>
#include <boost/optional.hpp>
#include <aedis/resp3/type.hpp>
#include <aedis/resp3/detail/parser.hpp>
#include <aedis/generic/serializer.hpp>
#include <aedis/resp3/node.hpp>
#include <aedis/adapter/error.hpp>
@@ -39,9 +40,7 @@ from_string(
std::size_t data_size,
boost::system::error_code& ec)
{
auto const res = std::from_chars(value, value + data_size, i);
if (res.ec != std::errc())
ec = std::make_error_code(res.ec);
i = resp3::detail::parse_uint(value, data_size, ec);
}
void from_string(
@@ -384,13 +383,13 @@ public:
};
template <class T>
class wrapper<std::optional<T>> {
class wrapper<boost::optional<T>> {
private:
std::optional<T>* result_;
boost::optional<T>* result_;
typename impl_map<T>::type impl_;
public:
wrapper(std::optional<T>* o = nullptr) : result_(o), impl_{} {}
wrapper(boost::optional<T>* o = nullptr) : result_(o), impl_{} {}
void operator()( resp3::type t, std::size_t aggregate_size, std::size_t depth, char const* value, std::size_t size, boost::system::error_code& ec)
{

View File

@@ -8,11 +8,10 @@
#pragma once
#include <vector>
#include <charconv>
#include <tuple>
#include <variant>
#include <boost/mp11.hpp>
#include <boost/variant2.hpp>
#include <aedis/resp3/type.hpp>
#include <aedis/resp3/read.hpp>
@@ -95,8 +94,12 @@ struct assigner<0> {
template <class Tuple>
class static_aggregate_adapter {
private:
using foo = boost::mp11::mp_rename<boost::mp11::mp_transform<response_traits_t, Tuple>, std::variant>;
using variant_type = boost::mp11::mp_unique<foo>;
using variant_type =
boost::mp11::mp_unique<
boost::mp11::mp_rename<
boost::mp11::mp_transform<
response_traits_t, Tuple>,
boost::variant2::variant>>;
std::size_t i_ = 0;
std::size_t aggregate_size_ = 0;
@@ -130,6 +133,8 @@ public:
std::size_t size,
boost::system::error_code& ec)
{
using boost::variant2::visit;
if (depth == 0) {
auto const real_aggr_size = aggregate_size * element_multiplicity(t);
if (real_aggr_size != std::tuple_size<Tuple>::value)
@@ -138,7 +143,7 @@ public:
return;
}
std::visit([&](auto& arg){arg(t, aggregate_size, depth, data, size, ec);}, adapters_[i_]);
visit([&](auto& arg){arg(t, aggregate_size, depth, data, size, ec);}, adapters_[i_]);
count(t, aggregate_size, depth);
}
};

View File

@@ -196,7 +196,7 @@
Blob string | \c std::string, \c std::vector | Simple
Blob error | \c std::string, \c std::vector | Simple
Number | `long long`, `int`, \c std::string | Simple
Null | `std::optional<T>` | Simple
Null | `boost::optional<T>` | Simple
Array | \c std::vector, \c std::list, \c std::array, \c std::deque | Aggregate
Map | \c std::vector, \c std::map, \c std::unordered_map | Aggregate
Set | \c std::vector, \c std::set, \c std::unordered_set | Aggregate
@@ -247,11 +247,11 @@
It is not uncommon for apps to access keys that do not exist or
that have already expired in the Redis server, to support such usecases Aedis
provides support for \c std::optional. To use it just wrap your
type around \c std::optional like this
provides support for \c boost::optional. To use it just wrap your
type around \c boost::optional like this
@code
std::optional<std::unordered_map<T, U>> umap;
boost::optional<std::unordered_map<T, U>> umap;
co_await resp3::async_read(socket, dynamic_buffer(buffer), adapt(umap));
@endcode
@@ -295,9 +295,9 @@
@code
std::tuple<
std::optional<std::string>, // Response to get
std::optional<std::vector<std::string>>, // Response to lrange
std::optional<std::map<std::string, std::string>> // Response to hgetall
boost::optional<std::string>, // Response to get
boost::optional<std::vector<std::string>>, // Response to lrange
boost::optional<std::map<std::string, std::string>> // Response to hgetall
> trans;
co_await resp3::async_read(socket, dynamic_buffer(buffer)); // Ignore multi
@@ -588,13 +588,18 @@
- Boost 1.78 or greater.
- Unix Shell and Make.
- C++17. Some examples require C++20 with coroutine support.
- C++14. Some examples require C++20 with coroutine support.
- Redis server.
Some examples will also require interaction with
- redis-cli: Used in one example.
- Redis Sentinel Server: used in some examples.
Aedis has been tested with the following compilers
- Tested with gcc: 7.5.0, 8.4.0, 9.3.0, 10.3.0.
- Tested with clang: 11.0.0, 10.0.0, 9.0.1, 8.0.1, 7.0.1.
\subsection Installation

View File

@@ -87,7 +87,7 @@ private:
*/
bool prepare_next()
{
if (std::empty(req_info_)) {
if (req_info_.empty()) {
req_info_.push_back({});
return true;
}
@@ -108,8 +108,8 @@ private:
// TODO: If the response to a discard is received we have to
// remove all commands up until multi.
assert(!std::empty(req_info_));
assert(!std::empty(commands_));
assert(!req_info_.empty());
assert(!commands_.empty());
commands_.erase(std::begin(commands_));
@@ -118,7 +118,7 @@ private:
req_info_.erase(std::begin(req_info_));
return !std::empty(req_info_);
return !req_info_.empty();
}
// Reads messages asynchronously.
@@ -191,9 +191,9 @@ public:
auto const can_write = prepare_next();
serializer<std::string> sr(requests_);
auto const before = std::size(requests_);
auto const before = requests_.size();
sr.push(cmd, args...);
auto const after = std::size(requests_);
auto const after = requests_.size();
assert(after - before != 0);
req_info_.front().size += after - before;;
@@ -217,9 +217,9 @@ public:
auto const can_write = prepare_next();
serializer<std::string> sr(requests_);
auto const before = std::size(requests_);
auto const before = requests_.size();
sr.push_range2(cmd, key, begin, end);
auto const after = std::size(requests_);
auto const after = requests_.size();
assert(after - before != 0);
req_info_.front().size += after - before;;
@@ -243,9 +243,9 @@ public:
auto const can_write = prepare_next();
serializer<std::string> sr(requests_);
auto const before = std::size(requests_);
auto const before = requests_.size();
sr.push_range2(cmd, begin, end);
auto const after = std::size(requests_);
auto const after = requests_.size();
assert(after - before != 0);
req_info_.front().size += after - before;;

View File

@@ -95,9 +95,9 @@ struct writer_op {
boost::ignore_unused(n);
assert(!std::empty(cli->req_info_));
assert(!cli->req_info_.empty());
assert(cli->req_info_.front().size != 0);
assert(!std::empty(cli->requests_));
assert(!cli->requests_.empty());
yield
boost::asio::async_write(
@@ -151,7 +151,7 @@ struct read_op {
boost::ignore_unused(n);
if (std::empty(cli->read_buffer_)) {
if (cli->read_buffer_.empty()) {
yield
boost::asio::async_read_until(
cli->socket_,
@@ -166,11 +166,11 @@ struct read_op {
}
}
assert(!std::empty(cli->read_buffer_));
assert(!cli->read_buffer_.empty());
t = resp3::detail::to_type(cli->read_buffer_.front());
cmd = Command::invalid;
if (t != resp3::type::push) {
assert(!std::empty(cli->commands_));
assert(!cli->commands_.empty());
cmd = cli->commands_.front();
}

View File

@@ -8,10 +8,10 @@
#pragma once
#include <array>
#include <variant>
#include <tuple>
#include <boost/mp11.hpp>
#include <boost/variant2.hpp>
#include <aedis/resp3/type.hpp>
#include <aedis/adapter/response_traits.hpp>
@@ -26,7 +26,7 @@ template <class Command, class ...Ts>
class receiver_base {
private:
using tuple_type = std::tuple<Ts...>;
using variant_type = boost::mp11::mp_rename<boost::mp11::mp_transform<adapter::response_traits_t, tuple_type>, std::variant>;
using variant_type = boost::mp11::mp_rename<boost::mp11::mp_transform<adapter::response_traits_t, tuple_type>, boost::variant2::variant>;
tuple_type resps_;
std::array<variant_type, std::tuple_size<tuple_type>::value> adapters_;
@@ -60,11 +60,13 @@ public:
std::size_t size,
boost::system::error_code& ec)
{
using boost::variant2::visit;
auto const i = to_tuple_index(cmd);
if (i == -1)
return;
std::visit([&](auto& arg){arg(t, aggregate_size, depth, data, size, ec);}, adapters_[i]);
visit([&](auto& arg){arg(t, aggregate_size, depth, data, size, ec);}, adapters_[i]);
}
void on_read(Command cmd)

View File

@@ -7,6 +7,7 @@
#pragma once
#include <boost/hana.hpp>
#include <aedis/resp3/compose.hpp>
namespace aedis {
@@ -77,11 +78,14 @@ public:
template <class Command, class... Ts>
void push(Command cmd, Ts const&... args)
{
using boost::hana::for_each;
using boost::hana::make_tuple;
auto constexpr pack_size = sizeof...(Ts);
resp3::add_header(*request_, 1 + pack_size);
resp3::add_bulk(*request_, to_string(cmd));
(resp3::add_bulk(*request_, args), ...);
resp3::add_bulk(*request_, make_tuple(args...));
}
/** @brief Appends a new command to the end of the request.

View File

@@ -0,0 +1,121 @@
/* Copyright (c) 2019 Marcelo Zimbres Silva (mzimbres@gmail.com)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <string>
#include <tuple>
#include <boost/hana.hpp>
#include <boost/utility/string_view.hpp>
namespace aedis {
namespace resp3 {
/** @brief Adds data to the request.
* @ingroup any
*/
template <class Request>
void to_bulk(Request& to, boost::string_view data)
{
auto const str = std::to_string(data.size());
to += "$";
to.append(std::cbegin(str), std::cend(str));
to += "\r\n";
to.append(std::cbegin(data), std::cend(data));
to += "\r\n";
}
template <class Request, class T, typename = typename std::enable_if<std::is_integral<T>::value>::type>
void to_bulk(Request& to, T n)
{
auto const s = std::to_string(n);
to_bulk(to, boost::string_view{s});
}
namespace detail {
template <class T>
struct add_bulk_impl {
template <class Request>
static void add(Request& to, T const& from)
{
using namespace aedis::resp3;
to_bulk(to, from);
}
};
template <class U, class V>
struct add_bulk_impl<std::pair<U, V>> {
template <class Request>
static void add(Request& to, std::pair<U, V> const& from)
{
using namespace aedis::resp3;
to_bulk(to, from.first);
to_bulk(to, from.second);
}
};
template <class ...Ts>
struct add_bulk_impl<boost::hana::tuple<Ts...>> {
template <class Request>
static void add(Request& to, boost::hana::tuple<Ts...> const& from)
{
using boost::hana::for_each;
// Fold expressions is C++17 so we use hana.
//(resp3::add_bulk(*request_, args), ...);
for_each(from, [&](auto const& e) {
using namespace aedis::resp3;
to_bulk(to, e);
});
}
};
} // detail
/** @brief Adds a resp3 header to the store to.
* @ingroup any
*/
template <class Request>
void add_header(Request& to, std::size_t size)
{
auto const str = std::to_string(size);
to += "*";
to.append(std::cbegin(str), std::cend(str));
to += "\r\n";
}
/** @brief Adds a rep3 bulk to the request.
* @ingroup any
*
* This function adds \c data as a bulk string to the request \c to.
*/
template <class Request, class T>
void add_bulk(Request& to, T const& data)
{
detail::add_bulk_impl<T>::add(to, data);
}
template <class>
struct bulk_counter;
template <class>
struct bulk_counter {
static constexpr auto size = 1U;
};
template <class T, class U>
struct bulk_counter<std::pair<T, U>> {
static constexpr auto size = 2U;
};
} // resp3
} // aedis

View File

@@ -12,6 +12,16 @@ namespace aedis {
namespace resp3 {
namespace detail {
std::size_t parse_uint(char const* data, std::size_t size, boost::system::error_code& ec)
{
static constexpr boost::spirit::x3::uint_parser<std::size_t, 10> p{};
std::size_t ret;
if (!parse(data, data + size, p, ret))
ec = error::not_a_number;
return ret;
}
type to_type(char c)
{
switch (c) {

View File

@@ -8,16 +8,20 @@
#pragma once
#include <string_view>
#include <charconv>
#include <system_error>
#include <limits>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/home/x3.hpp>
#include <aedis/resp3/error.hpp>
namespace aedis {
namespace resp3 {
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);
@@ -82,11 +86,9 @@ public:
switch (t) {
case type::streamed_string_part:
{
auto const r = std::from_chars(data + 1, data + n - 2, bulk_length_);
if (r.ec != std::errc()) {
ec = error::not_a_number;
bulk_length_ = parse_uint(data + 1, n - 2, ec);
if (ec)
return 0;
}
if (bulk_length_ == 0) {
adapter_(type::streamed_string_part, 1, depth_, nullptr, 0, ec);
@@ -107,11 +109,9 @@ public:
// 0.
sizes_[++depth_] = (std::numeric_limits<std::size_t>::max)();
} else {
auto const r = std::from_chars(data + 1, data + n - 2, bulk_length_);
if (r.ec != std::errc()) {
ec = error::not_a_number;
return 0;
}
bulk_length_ = parse_uint(data + 1, n - 2, ec);
if (ec)
return 0;
bulk_ = t;
}
@@ -172,12 +172,9 @@ public:
case type::attribute:
case type::map:
{
std::size_t l;
auto const r = std::from_chars(data + 1, data + n - 2, l);
if (r.ec != std::errc()) {
ec = error::not_a_number;
return 0;
}
auto const l = parse_uint(data + 1, n - 2, ec);
if (ec)
return 0;
adapter_(t, l, depth_, nullptr, 0, ec);
if (ec)

View File

@@ -33,7 +33,7 @@ void expect_error(boost::system::error_code a, boost::system::error_condition ex
template <class T>
void check_empty(T const& t)
{
if (std::empty(t)) {
if (t.empty()) {
//std::cout << "Success: " << std::endl;
} else {
std::cout << "Error: Not empty" << std::endl;

View File

@@ -100,16 +100,16 @@ void test_async(net::any_io_executor ex, expect<Result> e)
void test_number(net::io_context& ioc)
{
std::optional<int> ok;
boost::optional<int> ok;
ok = 11;
// Success
auto const in01 = expect<node_type>{":3\r\n", node_type{resp3::type::number, 1UL, 0UL, {"3"}}, "number.node (positive)"};
auto const in02 = expect<node_type>{":-3\r\n", node_type{resp3::type::number, 1UL, 0UL, {"-3"}}, "number.node (negative)"};
auto const in03 = expect<int>{":11\r\n", int{11}, "number.int"};
auto const in04 = expect<std::optional<int>>{":11\r\n", ok, "number.optional.int"};
auto const in04 = expect<boost::optional<int>>{":11\r\n", ok, "number.optional.int"};
auto const in05 = expect<std::tuple<int>>{"*1\r\n:11\r\n", std::tuple<int>{11}, "number.tuple.int"};
auto const in06 = expect<std::optional<int>>{"%11\r\n", std::optional<int>{}, "number.optional.int", aedis::adapter::make_error_condition(aedis::adapter::error::expects_simple_type)};
auto const in06 = expect<boost::optional<int>>{"%11\r\n", boost::optional<int>{}, "number.optional.int", aedis::adapter::make_error_condition(aedis::adapter::error::expects_simple_type)};
auto const in07 = expect<std::set<std::string>>{":11\r\n", std::set<std::string>{}, "number.optional.int", aedis::adapter::make_error_condition(aedis::adapter::error::expects_set_aggregate)};
auto const in08 = expect<std::unordered_set<std::string>>{":11\r\n", std::unordered_set<std::string>{}, "number.optional.int", aedis::adapter::make_error_condition(aedis::adapter::error::expects_set_aggregate)};
auto const in09 = expect<std::map<std::string, std::string>>{":11\r\n", std::map<std::string, std::string>{}, "number.optional.int", aedis::adapter::make_error_condition(aedis::adapter::error::expects_map_like_aggregate)};
@@ -145,7 +145,7 @@ void test_number(net::io_context& ioc)
void test_bool(net::io_context& ioc)
{
std::optional<bool> ok;
boost::optional<bool> ok;
ok = true;
// Success.
@@ -153,10 +153,10 @@ void test_bool(net::io_context& ioc)
auto const in09 = expect<node_type>{"#t\r\n", node_type{resp3::type::boolean, 1UL, 0UL, {"t"}}, "bool.node (true)"};
auto const in10 = expect<bool>{"#t\r\n", bool{true}, "bool.bool (true)"};
auto const in11 = expect<bool>{"#f\r\n", bool{false}, "bool.bool (true)"};
auto const in13 = expect<std::optional<bool>>{"#t\r\n", ok, "optional.int"};
auto const in13 = expect<boost::optional<bool>>{"#t\r\n", ok, "optional.int"};
// Error
auto const in01 = expect<std::optional<bool>>{"#11\r\n", std::optional<bool>{}, "bool.error", aedis::resp3::make_error_condition(aedis::resp3::error::unexpected_bool_value)};
auto const in01 = expect<boost::optional<bool>>{"#11\r\n", boost::optional<bool>{}, "bool.error", aedis::resp3::make_error_condition(aedis::resp3::error::unexpected_bool_value)};
auto const in03 = expect<std::set<int>>{"#t\r\n", std::set<int>{}, "bool.error", aedis::adapter::make_error_condition(aedis::adapter::error::expects_set_aggregate)};
auto const in04 = expect<std::unordered_set<int>>{"#t\r\n", std::unordered_set<int>{}, "bool.error", aedis::adapter::make_error_condition(aedis::adapter::error::expects_set_aggregate)};
auto const in05 = expect<std::map<int, int>>{"#t\r\n", std::map<int, int>{}, "bool.error", aedis::adapter::make_error_condition(aedis::adapter::error::expects_map_like_aggregate)};
@@ -246,8 +246,8 @@ void test_map(net::io_context& ioc)
using umap_type = std::unordered_map<std::string, std::string>;
using mumap_type = std::unordered_multimap<std::string, std::string>;
using vec_type = std::vector<std::string>;
using op_map_type = std::optional<std::map<std::string, std::string>>;
using op_vec_type = std::optional<std::vector<std::string>>;
using op_map_type = boost::optional<std::map<std::string, std::string>>;
using op_vec_type = boost::optional<std::vector<std::string>>;
using tuple_type = std::tuple<std::string, std::string, std::string, std::string, std::string, std::string, std::string, std::string>;
std::string const wire = "%4\r\n$4\r\nkey1\r\n$6\r\nvalue1\r\n$4\r\nkey2\r\n$6\r\nvalue2\r\n$4\r\nkey3\r\n$6\r\nvalue3\r\n$4\r\nkey3\r\n$6\r\nvalue3\r\n";
@@ -434,7 +434,7 @@ void test_set(net::io_context& ioc)
using uset_type = std::unordered_set<std::string>;
using muset_type = std::unordered_multiset<std::string>;
using vec_type = std::vector<std::string>;
using op_vec_type = std::optional<std::vector<std::string>>;
using op_vec_type = boost::optional<std::vector<std::string>>;
std::string const wire = "~6\r\n+orange\r\n+apple\r\n+one\r\n+two\r\n+three\r\n+orange\r\n";
std::vector<node_type> const expected1a
@@ -509,7 +509,7 @@ void test_blob_string(net::io_context& ioc)
std::string wire;
wire += '$';
wire += std::to_string(std::size(str));
wire += std::to_string(str.size());
wire += "\r\n";
wire += str;
wire += "\r\n";
@@ -593,14 +593,14 @@ void test_big_number(net::io_context& ioc)
void test_simple_string(net::io_context& ioc)
{
std::optional<std::string> ok1, ok2;
boost::optional<std::string> ok1, ok2;
ok1 = "OK";
ok2 = "";
auto in00 = expect<std::string>{"+OK\r\n", std::string{"OK"}, "simple_string.string"};
auto in01 = expect<std::string>{"+\r\n", std::string{""}, "simple_string.string.empty"};
auto in02 = expect<std::optional<std::string>>{"+OK\r\n", std::optional<std::string>{"OK"}, "simple_string.optional"};
auto in03 = expect<std::optional<std::string>>{"+\r\n", std::optional<std::string>{""}, "simple_string.optional.empty"};
auto in02 = expect<boost::optional<std::string>>{"+OK\r\n", boost::optional<std::string>{"OK"}, "simple_string.optional"};
auto in03 = expect<boost::optional<std::string>>{"+\r\n", boost::optional<std::string>{""}, "simple_string.optional.empty"};
auto ex = ioc.get_executor();
@@ -618,9 +618,9 @@ void test_simple_string(net::io_context& ioc)
void test_resp3(net::io_context& ioc)
{
auto const in01 = expect<int>{"s11\r\n", int{}, "number.error", aedis::resp3::make_error_condition(aedis::resp3::error::invalid_type)};
auto const in02 = expect<int>{":adf\r\n", int{11}, "number.int", boost::system::errc::errc_t::invalid_argument};
auto const in02 = expect<int>{":adf\r\n", int{11}, "number.int", aedis::resp3::make_error_condition(aedis::resp3::error::not_a_number)};
auto const in03 = expect<int>{":\r\n", int{}, "number.error (empty field)", aedis::resp3::make_error_condition(aedis::resp3::error::empty_field)};
auto const in04 = expect<std::optional<bool>>{"#\r\n", std::optional<bool>{}, "bool.error", aedis::resp3::make_error_condition(aedis::resp3::error::empty_field)};
auto const in04 = expect<boost::optional<bool>>{"#\r\n", boost::optional<bool>{}, "bool.error", aedis::resp3::make_error_condition(aedis::resp3::error::empty_field)};
auto const in05 = expect<std::string>{",\r\n", std::string{}, "double.error (empty field)", aedis::resp3::make_error_condition(aedis::resp3::error::empty_field)};
auto ex = ioc.get_executor();
@@ -640,15 +640,15 @@ void test_resp3(net::io_context& ioc)
void test_null(net::io_context& ioc)
{
using op_type_01 = std::optional<bool>;
using op_type_02 = std::optional<int>;
using op_type_03 = std::optional<std::string>;
using op_type_04 = std::optional<std::vector<std::string>>;
using op_type_05 = std::optional<std::list<std::string>>;
using op_type_06 = std::optional<std::map<std::string, std::string>>;
using op_type_07 = std::optional<std::unordered_map<std::string, std::string>>;
using op_type_08 = std::optional<std::set<std::string>>;
using op_type_09 = std::optional<std::unordered_set<std::string>>;
using op_type_01 = boost::optional<bool>;
using op_type_02 = boost::optional<int>;
using op_type_03 = boost::optional<std::string>;
using op_type_04 = boost::optional<std::vector<std::string>>;
using op_type_05 = boost::optional<std::list<std::string>>;
using op_type_06 = boost::optional<std::map<std::string, std::string>>;
using op_type_07 = boost::optional<std::unordered_map<std::string, std::string>>;
using op_type_08 = boost::optional<std::set<std::string>>;
using op_type_09 = boost::optional<std::unordered_set<std::string>>;
auto const in01 = expect<op_type_01>{"_\r\n", op_type_01{}, "null.optional.bool"};
auto const in02 = expect<op_type_02>{"_\r\n", op_type_02{}, "null.optional.int"};

View File

@@ -36,13 +36,13 @@ std::string toupper(std::string s)
std::vector<std::string>
get_cmd_names(std::vector<node<std::string>> const& resp)
{
if (std::empty(resp)) {
if (resp.empty()) {
std::cerr << "Response is empty." << std::endl;
return {};
}
std::vector<std::string> ret;
for (auto i = 0ULL; i < std::size(resp); ++i) {
for (auto i = 0ULL; i < resp.size(); ++i) {
if (resp.at(i).depth == 1)
ret.push_back(resp.at(i + 1).value);
}