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:
57
Makefile.am
57
Makefile.am
@@ -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\
|
||||
|
||||
@@ -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
|
||||
@@ -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])
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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:;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
{
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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;;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
@@ -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.
|
||||
121
include/aedis/resp3/compose.hpp
Normal file
121
include/aedis/resp3/compose.hpp
Normal 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
|
||||
@@ -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) {
|
||||
@@ -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)
|
||||
@@ -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;
|
||||
|
||||
@@ -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"};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user