Skip to content

Commit 1bacc74

Browse files
committed
Uses class specialization instead of function overload. [skip-ci]
1 parent 31ceed9 commit 1bacc74

File tree

6 files changed

+178
-53
lines changed

6 files changed

+178
-53
lines changed

Diff for: example/cpp20_containers.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,10 @@ auto transaction(std::shared_ptr<connection> conn) -> awaitable<void>
8080
ignore_t, // multi
8181
ignore_t, // lrange
8282
ignore_t, // hgetall
83-
response<std::optional<std::vector<int>>, std::optional<std::map<std::string, std::string>>> // exec
83+
response<
84+
std::optional<std::vector<int>>,
85+
std::optional<std::map<std::string, std::string>>
86+
> // exec
8487
> resp;
8588

8689
co_await conn->async_exec(req, resp);

Diff for: example/cpp20_json.cpp

+9-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <boost/redis/resp3/serialization.hpp>
2222

2323
namespace asio = boost::asio;
24+
namespace resp3 = boost::redis::resp3;
2425
using namespace boost::describe;
2526
using boost::redis::request;
2627
using boost::redis::response;
@@ -42,8 +43,14 @@ BOOST_DESCRIBE_STRUCT(user, (), (name, age, country))
4243
void boost_redis_to_bulk(std::string& to, user const& u)
4344
{ boost::redis::resp3::boost_redis_to_bulk(to, boost::json::serialize(boost::json::value_from(u))); }
4445

45-
void boost_redis_from_bulk(user& u, std::string_view sv, boost::system::error_code&)
46-
{ u = boost::json::value_to<user>(boost::json::parse(sv)); }
46+
void
47+
boost_redis_from_bulk(
48+
user& u,
49+
resp3::node_view const& node,
50+
boost::system::error_code&)
51+
{
52+
u = boost::json::value_to<user>(boost::json::parse(node.value));
53+
}
4754

4855
auto co_main(config cfg) -> asio::awaitable<void>
4956
{

Diff for: example/cpp20_protobuf.cpp

+7-2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#if defined(BOOST_ASIO_HAS_CO_AWAIT)
2020

2121
namespace asio = boost::asio;
22+
namespace resp3 = boost::redis::resp3;
2223
using boost::redis::request;
2324
using boost::redis::response;
2425
using boost::redis::operation;
@@ -45,9 +46,13 @@ void boost_redis_to_bulk(std::string& to, person const& u)
4546
boost::redis::resp3::boost_redis_to_bulk(to, tmp);
4647
}
4748

48-
void boost_redis_from_bulk(person& u, std::string_view sv, boost::system::error_code& ec)
49+
void
50+
boost_redis_from_bulk(
51+
person& u,
52+
resp3::node_view const& node,
53+
boost::system::error_code& ec)
4954
{
50-
std::string const tmp {sv};
55+
std::string const tmp {node.value};
5156
if (!u.ParseFromString(tmp))
5257
ec = boost::redis::error::invalid_data_type;
5358
}

Diff for: include/boost/redis/adapter/detail/adapters.hpp

+121-48
Original file line numberDiff line numberDiff line change
@@ -37,49 +37,120 @@
3737
namespace boost::redis::adapter::detail
3838
{
3939

40-
// Serialization.
40+
template <class> struct is_integral : std::false_type {};
4141

42-
template <class T>
43-
auto boost_redis_from_bulk(T& i, std::string_view sv, system::error_code& ec) -> typename std::enable_if<std::is_integral<T>::value, void>::type
44-
{
45-
auto const res = std::from_chars(sv.data(), sv.data() + std::size(sv), i);
46-
if (res.ec != std::errc())
47-
ec = redis::error::not_a_number;
48-
}
42+
template <> struct is_integral<long long int > : std::true_type {};
43+
template <> struct is_integral<unsigned long long int> : std::true_type {};
44+
template <> struct is_integral<int > : std::true_type {};
4945

50-
inline
51-
void boost_redis_from_bulk(bool& t, std::string_view sv, system::error_code&)
52-
{
53-
t = *sv.data() == 't';
54-
}
46+
template<class T, bool = is_integral<T>::value>
47+
struct converter;
5548

56-
inline
57-
void boost_redis_from_bulk(double& d, std::string_view sv, system::error_code& ec)
58-
{
49+
template<class T>
50+
struct converter<T, true> {
51+
template <class String>
52+
static void
53+
apply(
54+
T& i,
55+
resp3::basic_node<String> const& node,
56+
system::error_code& ec)
57+
{
58+
auto const res =
59+
std::from_chars(node.value.data(), node.value.data() + node.value.size(), i);
60+
if (res.ec != std::errc())
61+
ec = redis::error::not_a_number;
62+
}
63+
};
64+
65+
template<>
66+
struct converter<bool, false> {
67+
template <class String>
68+
static void
69+
apply(
70+
bool& t,
71+
resp3::basic_node<String> const& node,
72+
system::error_code& ec)
73+
{
74+
t = *node.value.data() == 't';
75+
}
76+
};
77+
78+
template<>
79+
struct converter<double, false> {
80+
template <class String>
81+
static void
82+
apply(
83+
double& d,
84+
resp3::basic_node<String> const& node,
85+
system::error_code& ec)
86+
{
5987
#ifdef _LIBCPP_VERSION
60-
// The string in sv is not null terminated and we also don't know
61-
// if there is enough space at the end for a null char. The easiest
62-
// thing to do is to create a temporary.
63-
std::string const tmp{sv.data(), sv.data() + std::size(sv)};
64-
char* end{};
65-
d = std::strtod(tmp.data(), &end);
66-
if (d == HUGE_VAL || d == 0)
67-
ec = redis::error::not_a_double;
88+
// The string in node.value is not null terminated and we also
89+
// don't know if there is enough space at the end for a null
90+
// char. The easiest thing to do is to create a temporary.
91+
std::string const tmp{node.value.data(), node.value.data() + node.value.size()};
92+
char* end{};
93+
d = std::strtod(tmp.data(), &end);
94+
if (d == HUGE_VAL || d == 0)
95+
ec = redis::error::not_a_double;
6896
#else
69-
auto const res = std::from_chars(sv.data(), sv.data() + std::size(sv), d);
70-
if (res.ec != std::errc())
71-
ec = redis::error::not_a_double;
97+
auto const res = std::from_chars(node.value.data(), node.value.data() + node.value.size(), d);
98+
if (res.ec != std::errc())
99+
ec = redis::error::not_a_double;
72100
#endif // _LIBCPP_VERSION
73-
}
101+
}
102+
};
74103

75104
template <class CharT, class Traits, class Allocator>
105+
struct converter<std::basic_string<CharT, Traits, Allocator>, false> {
106+
template <class String>
107+
static void
108+
apply(
109+
std::basic_string<CharT, Traits, Allocator>& s,
110+
resp3::basic_node<String> const& node,
111+
system::error_code&)
112+
{
113+
s.append(node.value.data(), node.value.size());
114+
}
115+
};
116+
117+
template <class T>
118+
struct from_bulk_impl {
119+
template <class String>
120+
static void
121+
apply(
122+
T& t,
123+
resp3::basic_node<String> const& node,
124+
system::error_code& ec)
125+
{
126+
converter<T>::apply(t, node, ec);
127+
}
128+
};
129+
130+
template <class T>
131+
struct from_bulk_impl<std::optional<T>> {
132+
template <class String>
133+
static void
134+
apply(
135+
std::optional<T>& op,
136+
resp3::basic_node<String> const& node,
137+
system::error_code& ec)
138+
{
139+
if (node.data_type != resp3::type::null) {
140+
op.emplace(T{});
141+
converter<T>::apply(op.value(), node, ec);
142+
}
143+
}
144+
};
145+
146+
template <class T, class String>
76147
void
77148
boost_redis_from_bulk(
78-
std::basic_string<CharT, Traits, Allocator>& s,
79-
std::string_view sv,
80-
system::error_code&)
149+
T& t,
150+
resp3::basic_node<String> const& node,
151+
system::error_code& ec)
81152
{
82-
s.append(sv.data(), sv.size());
153+
from_bulk_impl<T>::apply(t, node, ec);
83154
}
84155

85156
//================================================
@@ -138,14 +209,14 @@ class simple_impl {
138209
void on_value_available(Result&) {}
139210

140211
template <class String>
141-
void operator()(Result& result, resp3::basic_node<String> const& n, system::error_code& ec)
212+
void operator()(Result& result, resp3::basic_node<String> const& node, system::error_code& ec)
142213
{
143-
if (is_aggregate(n.data_type)) {
214+
if (is_aggregate(node.data_type)) {
144215
ec = redis::error::expects_resp3_simple_type;
145216
return;
146217
}
147218

148-
boost_redis_from_bulk(result, n.value, ec);
219+
boost_redis_from_bulk(result, node, ec);
149220
}
150221
};
151222

@@ -175,7 +246,7 @@ class set_impl {
175246
}
176247

177248
typename Result::key_type obj;
178-
boost_redis_from_bulk(obj, nd.value, ec);
249+
boost_redis_from_bulk(obj, nd, ec);
179250
hint_ = result.insert(hint_, std::move(obj));
180251
}
181252
};
@@ -208,11 +279,11 @@ class map_impl {
208279

209280
if (on_key_) {
210281
typename Result::key_type obj;
211-
boost_redis_from_bulk(obj, nd.value, ec);
282+
boost_redis_from_bulk(obj, nd, ec);
212283
current_ = result.insert(current_, {std::move(obj), {}});
213284
} else {
214285
typename Result::mapped_type obj;
215-
boost_redis_from_bulk(obj, nd.value, ec);
286+
boost_redis_from_bulk(obj, nd, ec);
216287
current_->second = std::move(obj);
217288
}
218289

@@ -233,7 +304,7 @@ class vector_impl {
233304
result.reserve(result.size() + m * nd.aggregate_size);
234305
} else {
235306
result.push_back({});
236-
boost_redis_from_bulk(result.back(), nd.value, ec);
307+
boost_redis_from_bulk(result.back(), nd, ec);
237308
}
238309
}
239310
};
@@ -266,7 +337,7 @@ class array_impl {
266337
}
267338

268339
BOOST_ASSERT(nd.aggregate_size == 1);
269-
boost_redis_from_bulk(result.at(i_), nd.value, ec);
340+
boost_redis_from_bulk(result.at(i_), nd, ec);
270341
}
271342

272343
++i_;
@@ -289,7 +360,7 @@ struct list_impl {
289360
}
290361

291362
result.push_back({});
292-
boost_redis_from_bulk(result.back(), nd.value, ec);
363+
boost_redis_from_bulk(result.back(), nd, ec);
293364
}
294365
}
295366
};
@@ -340,13 +411,14 @@ struct impl_map<std::deque<T, Allocator>> { using type = list_impl<std::deque<T,
340411
template <class>
341412
class wrapper;
342413

343-
template <class Result>
344-
class wrapper<result<Result>> {
414+
template <class T>
415+
class wrapper<result<T>> {
345416
public:
346-
using response_type = result<Result>;
417+
using response_type = result<T>;
347418
private:
348419
response_type* result_;
349-
typename impl_map<Result>::type impl_;
420+
typename impl_map<T>::type impl_;
421+
bool called_once_ = false;
350422

351423
template <class String>
352424
bool set_if_resp3_error(resp3::basic_node<String> const& nd) noexcept
@@ -366,7 +438,7 @@ class wrapper<result<Result>> {
366438
explicit wrapper(response_type* t = nullptr) : result_(t)
367439
{
368440
if (result_) {
369-
result_->value() = Result{};
441+
result_->value() = T{};
370442
impl_.on_value_available(result_->value());
371443
}
372444
}
@@ -379,7 +451,7 @@ class wrapper<result<Result>> {
379451
if (result_->has_error())
380452
return;
381453

382-
if (set_if_resp3_error(nd))
454+
if (!std::exchange(called_once_, true) && set_if_resp3_error(nd))
383455
return;
384456

385457
BOOST_ASSERT(result_);
@@ -395,6 +467,7 @@ class wrapper<result<std::optional<T>>> {
395467
private:
396468
response_type* result_;
397469
typename impl_map<T>::type impl_{};
470+
bool called_once_ = false;
398471

399472
template <class String>
400473
bool set_if_resp3_error(resp3::basic_node<String> const& nd) noexcept
@@ -426,7 +499,7 @@ class wrapper<result<std::optional<T>>> {
426499
if (set_if_resp3_error(nd))
427500
return;
428501

429-
if (nd.data_type == resp3::type::null)
502+
if (!std::exchange(called_once_, true) && nd.data_type == resp3::type::null)
430503
return;
431504

432505
if (!result_->value().has_value()) {

Diff for: include/boost/redis/resp3/node.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ auto operator==(basic_node<String> const& a, basic_node<String> const& b)
5959
*/
6060
using node = basic_node<std::string>;
6161

62+
using node_view = basic_node<std::string_view>;
63+
6264
} // boost::redis::resp3
6365

6466
#endif // BOOST_REDIS_RESP3_NODE_HPP

Diff for: test/test_low_level_sync_sans_io.cpp

+35
Original file line numberDiff line numberDiff line change
@@ -200,3 +200,38 @@ BOOST_AUTO_TEST_CASE(issue_210_no_nested)
200200
}
201201
}
202202

203+
BOOST_AUTO_TEST_CASE(issue_233_array_with_null)
204+
{
205+
try {
206+
result<std::vector<std::optional<std::string>>> resp;
207+
208+
char const* wire = "*3\r\n+one\r\n_\r\n+two\r\n";
209+
deserialize(wire, adapt2(resp));
210+
211+
BOOST_CHECK_EQUAL(resp.value().at(0).value(), "one");
212+
BOOST_TEST(!resp.value().at(1).has_value());
213+
BOOST_CHECK_EQUAL(resp.value().at(2).value(), "two");
214+
215+
} catch (std::exception const& e) {
216+
std::cerr << e.what() << std::endl;
217+
exit(EXIT_FAILURE);
218+
}
219+
}
220+
221+
BOOST_AUTO_TEST_CASE(issue_233_optional_array_with_null)
222+
{
223+
try {
224+
result<std::optional<std::vector<std::optional<std::string>>>> resp;
225+
226+
char const* wire = "*3\r\n+one\r\n_\r\n+two\r\n";
227+
deserialize(wire, adapt2(resp));
228+
229+
BOOST_CHECK_EQUAL(resp.value().value().at(0).value(), "one");
230+
BOOST_TEST(!resp.value().value().at(1).has_value());
231+
BOOST_CHECK_EQUAL(resp.value().value().at(2).value(), "two");
232+
233+
} catch (std::exception const& e) {
234+
std::cerr << e.what() << std::endl;
235+
exit(EXIT_FAILURE);
236+
}
237+
}

0 commit comments

Comments
 (0)