Skip to content

Commit a6f9d09

Browse files
authored
Merge pull request #73 from qchateau/default-arguments
Default arguments
2 parents 2c0c8c5 + 59f827b commit a6f9d09

30 files changed

+1136
-415
lines changed

.github/build.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ def test_linux():
137137
builder.add(compiler=GCC, compiler_version="12", cppstd="17", options={"boost": "1.72.0", "packio:boost_json": False})
138138
builder.add(compiler=GCC, compiler_version="12", cppstd="17", options={"boost": "1.73.0", "packio:boost_json": False})
139139
builder.add(compiler=GCC, compiler_version="12", cppstd="20", options={"boost": "1.74.0", "packio:boost_json": False})
140-
builder.add(compiler=GCC, compiler_version="12", cppstd="20", options={"boost": "1.75.0"})
140+
builder.add(compiler=GCC, compiler_version="12", cppstd="20", options={"boost": "1.75.0", "packio:boost_json": False})
141141
builder.add(compiler=GCC, compiler_version="12", cppstd="20", options={"boost": "1.76.0"})
142142
builder.add(compiler=GCC, compiler_version="12", cppstd="20", options={"boost": "1.77.0"})
143143
builder.add(compiler=GCC, compiler_version="12", cppstd="20", options={"boost": "1.78.0"})

.github/workflows/tests.yml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,19 @@ jobs:
1414
include:
1515
- os: windows-2019
1616
conan-current-page: 1
17-
conan-total-pages: 1
17+
conan-total-pages: 2
18+
conan-cpu-count: 1
19+
- os: windows-2019
20+
conan-current-page: 2
21+
conan-total-pages: 2
1822
conan-cpu-count: 1
1923
- os: windows-2022
2024
conan-current-page: 1
21-
conan-total-pages: 1
25+
conan-total-pages: 2
26+
conan-cpu-count: 1
27+
- os: windows-2022
28+
conan-current-page: 2
29+
conan-total-pages: 2
2230
conan-cpu-count: 1
2331
- os: macos-11.0
2432
conan-current-page: 1

README.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ The project is hosted on [GitHub](https://github.com/qchateau/packio/) and avail
1111

1212
#include <packio/packio.h>
1313

14+
using packio::allow_extra_arguments;
1415
using packio::arg;
1516
using packio::nl_json_rpc::completion_handler;
1617
using packio::nl_json_rpc::make_client;
1718
using packio::nl_json_rpc::make_server;
1819
using packio::nl_json_rpc::rpc;
20+
using namespace packio::arg_literals;
1921

2022
int main(int, char**)
2123
{
@@ -31,9 +33,13 @@ int main(int, char**)
3133
// Declare a synchronous callback with named arguments
3234
server->dispatcher()->add(
3335
"add", {"a", "b"}, [](int a, int b) { return a + b; });
34-
// Declare an asynchronous callback with named arguments
36+
// Declare an asynchronous callback with named arguments,
37+
// an argument with a default value and an option to
38+
// accept and discard extra arguments
3539
server->dispatcher()->add_async(
36-
"multiply", {"a", "b"}, [&io](completion_handler complete, int a, int b) {
40+
"multiply",
41+
{allow_extra_arguments, "a", "b"_arg = 2},
42+
[&io](completion_handler complete, int a, int b) {
3743
// Call the completion handler later
3844
packio::net::post(
3945
io, [a, b, complete = std::move(complete)]() mutable {
@@ -54,10 +60,11 @@ int main(int, char**)
5460
std::thread thread{[&] { io.run(); }};
5561

5662
// Make an asynchronous call with named arguments
63+
// using either `packio::arg` or `packio::arg_literals`
5764
std::promise<int> add1_result, multiply_result;
5865
client->async_call(
5966
"add",
60-
std::tuple{arg("a") = 42, arg("b") = 24},
67+
std::tuple{arg("a") = 42, "b"_arg = 24},
6168
[&](packio::error_code, const rpc::response_type& r) {
6269
add1_result.set_value(r.result.get<int>());
6370
});
@@ -124,6 +131,7 @@ If you're not using the conan package, `packio` will try to auto-detect whether
124131
### Boost before 1.75
125132
126133
If you're using the conan package with a boost version older than 1.75, you need to manually disable `Boost.Json` with the options `boost_json=False`.
134+
`Boost.Json` version 1.75 contains some bugs when using C-strings as arguments so I'd recommend at using at least version 1.76.
127135
128136
## Tested compilers
129137

conanfile.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
class PackioConan(ConanFile):
55
name = "packio"
6-
version = "2.3.0"
6+
version = "2.4.0"
77
license = "MPL-2.0"
88
author = "Quentin Chateau <[email protected]>"
99
url = "https://github.com/qchateau/packio"
@@ -41,7 +41,7 @@ def requirements(self):
4141
self.options.boost_json = not self.options.standalone_asio
4242

4343
if self.options.msgpack:
44-
self.requires("msgpack/3.2.1")
44+
self.requires("msgpack-cxx/4.1.3")
4545
if self.options.nlohmann_json:
4646
self.requires("nlohmann_json/3.9.1")
4747
if self.options.boost_json:

include/packio/arg.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,25 @@
66
#define PACKIO_ARG_H
77

88
//! @file
9-
//! Class arg
9+
//! Class @ref packio::arg "arg"
1010

1111
#include <string>
1212
#include <string_view>
1313

1414
namespace packio {
1515

16+
//! A named argument
1617
class arg {
1718
public:
19+
//! A named argument with a value
1820
template <typename T>
1921
struct with_value {
20-
const std::string name;
22+
std::string name;
2123
T value;
2224
};
2325

2426
explicit constexpr arg(std::string_view name) : name_{name} {}
27+
std::string_view name() const { return name_; }
2528

2629
template <typename T>
2730
constexpr with_value<T> operator=(T&& value)
@@ -48,6 +51,8 @@ struct is_arg : is_arg_impl<std::decay_t<T>> {
4851
template <typename T>
4952
constexpr bool is_arg_v = is_arg<T>::value;
5053

54+
//! @namespace arg_literals
55+
//! Namespace containing string literals to define arguments
5156
namespace arg_literals {
5257

5358
constexpr arg operator"" _arg(const char* str, std::size_t)

include/packio/args_specs.h

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
5+
#ifndef PACKIO_ARGS_SPECS_H
6+
#define PACKIO_ARGS_SPECS_H
7+
8+
//! @file
9+
//! Class @ref packio::args_specs "args_specs"
10+
11+
#include <optional>
12+
#include <stdexcept>
13+
#include <string>
14+
15+
#include "arg.h"
16+
#include "handler.h"
17+
#include "internal/utils.h"
18+
19+
namespace packio {
20+
namespace internal {
21+
22+
template <typename DefaultType>
23+
class arg_spec {
24+
public:
25+
explicit arg_spec(std::string name) : name_(std::move(name)) {}
26+
explicit arg_spec(const arg& arg) : name_(arg.name()) {}
27+
explicit arg_spec(arg::with_value<DefaultType> arg)
28+
: name_(std::move(arg.name)), default_value_(std::move(arg.value))
29+
{
30+
}
31+
32+
const std::string& name() const { return name_; }
33+
const std::optional<DefaultType>& default_value() const
34+
{
35+
return default_value_;
36+
}
37+
38+
private:
39+
std::string name_;
40+
std::optional<DefaultType> default_value_;
41+
};
42+
43+
template <typename F>
44+
using arg_specs_tuple_for_sync_procedure_t =
45+
map_tuple_t<arg_spec, decay_tuple_t<typename func_traits<F>::args_type>>;
46+
47+
template <typename F>
48+
using arg_specs_tuple_for_async_procedure_t =
49+
left_shift_tuple_t<arg_specs_tuple_for_sync_procedure_t<F>>;
50+
51+
template <typename, bool>
52+
struct arg_specs_tuple_for;
53+
54+
template <typename F>
55+
struct arg_specs_tuple_for<F, true> {
56+
using type = arg_specs_tuple_for_async_procedure_t<F>;
57+
};
58+
59+
template <typename F>
60+
struct arg_specs_tuple_for<F, false> {
61+
using type = arg_specs_tuple_for_sync_procedure_t<F>;
62+
};
63+
64+
template <typename F>
65+
using arg_specs_tuple_for_t =
66+
typename arg_specs_tuple_for<F, is_async_procedure_v<F>>::type;
67+
68+
template <typename T>
69+
struct args_specs_maker;
70+
71+
template <typename... Specs>
72+
struct args_specs_maker<std::tuple<Specs...>> {
73+
template <typename... Args>
74+
static std::tuple<Specs...> make(Args&&... args)
75+
{
76+
if constexpr (sizeof...(Args) == 0) {
77+
// default arg specs are arguments called "0", "1", ... and no default value
78+
return iota(std::make_index_sequence<sizeof...(Specs)>());
79+
}
80+
else {
81+
static_assert(
82+
sizeof...(Args) == sizeof...(Specs),
83+
"arguments specification must either match the number of "
84+
"arguments or be empty");
85+
return {Specs{std::forward<Args>(args)}...};
86+
}
87+
}
88+
89+
template <std::size_t... Idxs>
90+
static std::tuple<Specs...> iota(std::index_sequence<Idxs...>)
91+
{
92+
return {Specs{std::to_string(Idxs)}...};
93+
}
94+
};
95+
96+
//! Options available for the argument specifications
97+
//!
98+
//! You are never expected to construct this structure yourself
99+
//! but rather construct them by combining options such as
100+
//! @ref packio::allow_extra_arguments "allow_extra_arguments"
101+
//! with operator|
102+
struct args_specs_options {
103+
bool allow_extra_arguments = false;
104+
105+
constexpr args_specs_options operator|(const args_specs_options& other)
106+
{
107+
auto ret = *this;
108+
ret.allow_extra_arguments |= other.allow_extra_arguments;
109+
return ret;
110+
}
111+
};
112+
113+
template <typename SpecsTuple>
114+
class args_specs {
115+
public:
116+
args_specs() : args_specs(args_specs_options{}){};
117+
118+
template <
119+
typename T,
120+
typename... Args,
121+
typename = std::enable_if_t<!std::is_same_v<std::decay_t<T>, args_specs_options>>>
122+
args_specs(T&& arg0, Args&&... args)
123+
: args_specs(
124+
args_specs_options{},
125+
std::forward<T>(arg0),
126+
std::forward<Args>(args)...)
127+
{
128+
}
129+
130+
template <typename... Args>
131+
args_specs(args_specs_options opts, Args&&... args)
132+
: specs_{args_specs_maker<SpecsTuple>::make(std::forward<Args>(args)...)},
133+
opts_{std::move(opts)}
134+
{
135+
}
136+
137+
constexpr const args_specs_options& options() const { return opts_; }
138+
139+
template <std::size_t I>
140+
constexpr decltype(auto) get() const
141+
{
142+
return std::get<I>(specs_);
143+
}
144+
145+
static constexpr std::size_t size()
146+
{
147+
return std::tuple_size_v<SpecsTuple>;
148+
}
149+
150+
private:
151+
SpecsTuple specs_;
152+
args_specs_options opts_{};
153+
};
154+
} // internal
155+
156+
//! Procedure arguments specifications
157+
//! @tparam The procedure
158+
//!
159+
//! CLass that describes the arguments of the procedure
160+
//! They each have a name and optionally a default value
161+
//! This is a tuple-like class where each element can be
162+
//! constructured from:
163+
//! - a string, defining the name of the argument
164+
//! - a @ref arg, defining the name of the argument
165+
//! - a @ref arg::with_value, defining the name of the argyment
166+
//! and its default value
167+
//! Optionally, accepts
168+
//! @ref packio::internal::args_specs_options "args_specs_options"
169+
//! as first argument to customize the behavior of argument handling
170+
template <typename Procedure>
171+
class args_specs
172+
// Using the real implementation as the base class reduces
173+
// the number of templates instanciation
174+
: public internal::args_specs<internal::arg_specs_tuple_for_t<Procedure>> {
175+
public:
176+
using base = internal::args_specs<internal::arg_specs_tuple_for_t<Procedure>>;
177+
using base::base;
178+
};
179+
180+
//! Option to allo extra arguments, ignoring them
181+
constexpr auto allow_extra_arguments = internal::args_specs_options{true};
182+
183+
} // packio
184+
185+
#endif // PACKIO_ARGS_SPECS_H

include/packio/client.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,12 @@ class client : public std::enable_shared_from_this<client<Rpc, Socket, Map>> {
270270
PACKIO_TRACE("read: {}", length);
271271
parser.buffer_consumed(length);
272272

273-
while (auto response = parser.get_response()) {
273+
while (true) {
274+
auto response = parser.get_response();
275+
if (!response) {
276+
PACKIO_INFO("stop reading: {}", response.error());
277+
break;
278+
}
274279
self->async_call_handler(std::move(*response));
275280
}
276281

0 commit comments

Comments
 (0)