Skip to content

Commit

Permalink
Merge pull request #73 from qchateau/default-arguments
Browse files Browse the repository at this point in the history
Default arguments
  • Loading branch information
qchateau authored Dec 29, 2022
2 parents 2c0c8c5 + 59f827b commit a6f9d09
Show file tree
Hide file tree
Showing 30 changed files with 1,136 additions and 415 deletions.
2 changes: 1 addition & 1 deletion .github/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ def test_linux():
builder.add(compiler=GCC, compiler_version="12", cppstd="17", options={"boost": "1.72.0", "packio:boost_json": False})
builder.add(compiler=GCC, compiler_version="12", cppstd="17", options={"boost": "1.73.0", "packio:boost_json": False})
builder.add(compiler=GCC, compiler_version="12", cppstd="20", options={"boost": "1.74.0", "packio:boost_json": False})
builder.add(compiler=GCC, compiler_version="12", cppstd="20", options={"boost": "1.75.0"})
builder.add(compiler=GCC, compiler_version="12", cppstd="20", options={"boost": "1.75.0", "packio:boost_json": False})
builder.add(compiler=GCC, compiler_version="12", cppstd="20", options={"boost": "1.76.0"})
builder.add(compiler=GCC, compiler_version="12", cppstd="20", options={"boost": "1.77.0"})
builder.add(compiler=GCC, compiler_version="12", cppstd="20", options={"boost": "1.78.0"})
Expand Down
12 changes: 10 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,19 @@ jobs:
include:
- os: windows-2019
conan-current-page: 1
conan-total-pages: 1
conan-total-pages: 2
conan-cpu-count: 1
- os: windows-2019
conan-current-page: 2
conan-total-pages: 2
conan-cpu-count: 1
- os: windows-2022
conan-current-page: 1
conan-total-pages: 1
conan-total-pages: 2
conan-cpu-count: 1
- os: windows-2022
conan-current-page: 2
conan-total-pages: 2
conan-cpu-count: 1
- os: macos-11.0
conan-current-page: 1
Expand Down
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ The project is hosted on [GitHub](https://github.com/qchateau/packio/) and avail

#include <packio/packio.h>

using packio::allow_extra_arguments;
using packio::arg;
using packio::nl_json_rpc::completion_handler;
using packio::nl_json_rpc::make_client;
using packio::nl_json_rpc::make_server;
using packio::nl_json_rpc::rpc;
using namespace packio::arg_literals;

int main(int, char**)
{
Expand All @@ -31,9 +33,13 @@ int main(int, char**)
// Declare a synchronous callback with named arguments
server->dispatcher()->add(
"add", {"a", "b"}, [](int a, int b) { return a + b; });
// Declare an asynchronous callback with named arguments
// Declare an asynchronous callback with named arguments,
// an argument with a default value and an option to
// accept and discard extra arguments
server->dispatcher()->add_async(
"multiply", {"a", "b"}, [&io](completion_handler complete, int a, int b) {
"multiply",
{allow_extra_arguments, "a", "b"_arg = 2},
[&io](completion_handler complete, int a, int b) {
// Call the completion handler later
packio::net::post(
io, [a, b, complete = std::move(complete)]() mutable {
Expand All @@ -54,10 +60,11 @@ int main(int, char**)
std::thread thread{[&] { io.run(); }};

// Make an asynchronous call with named arguments
// using either `packio::arg` or `packio::arg_literals`
std::promise<int> add1_result, multiply_result;
client->async_call(
"add",
std::tuple{arg("a") = 42, arg("b") = 24},
std::tuple{arg("a") = 42, "b"_arg = 24},
[&](packio::error_code, const rpc::response_type& r) {
add1_result.set_value(r.result.get<int>());
});
Expand Down Expand Up @@ -124,6 +131,7 @@ If you're not using the conan package, `packio` will try to auto-detect whether
### Boost before 1.75
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`.
`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.
## Tested compilers
Expand Down
4 changes: 2 additions & 2 deletions conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

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

if self.options.msgpack:
self.requires("msgpack/3.2.1")
self.requires("msgpack-cxx/4.1.3")
if self.options.nlohmann_json:
self.requires("nlohmann_json/3.9.1")
if self.options.boost_json:
Expand Down
9 changes: 7 additions & 2 deletions include/packio/arg.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,25 @@
#define PACKIO_ARG_H

//! @file
//! Class arg
//! Class @ref packio::arg "arg"

#include <string>
#include <string_view>

namespace packio {

//! A named argument
class arg {
public:
//! A named argument with a value
template <typename T>
struct with_value {
const std::string name;
std::string name;
T value;
};

explicit constexpr arg(std::string_view name) : name_{name} {}
std::string_view name() const { return name_; }

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

//! @namespace arg_literals
//! Namespace containing string literals to define arguments
namespace arg_literals {

constexpr arg operator"" _arg(const char* str, std::size_t)
Expand Down
185 changes: 185 additions & 0 deletions include/packio/args_specs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
// 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/.

#ifndef PACKIO_ARGS_SPECS_H
#define PACKIO_ARGS_SPECS_H

//! @file
//! Class @ref packio::args_specs "args_specs"

#include <optional>
#include <stdexcept>
#include <string>

#include "arg.h"
#include "handler.h"
#include "internal/utils.h"

namespace packio {
namespace internal {

template <typename DefaultType>
class arg_spec {
public:
explicit arg_spec(std::string name) : name_(std::move(name)) {}
explicit arg_spec(const arg& arg) : name_(arg.name()) {}
explicit arg_spec(arg::with_value<DefaultType> arg)
: name_(std::move(arg.name)), default_value_(std::move(arg.value))
{
}

const std::string& name() const { return name_; }
const std::optional<DefaultType>& default_value() const
{
return default_value_;
}

private:
std::string name_;
std::optional<DefaultType> default_value_;
};

template <typename F>
using arg_specs_tuple_for_sync_procedure_t =
map_tuple_t<arg_spec, decay_tuple_t<typename func_traits<F>::args_type>>;

template <typename F>
using arg_specs_tuple_for_async_procedure_t =
left_shift_tuple_t<arg_specs_tuple_for_sync_procedure_t<F>>;

template <typename, bool>
struct arg_specs_tuple_for;

template <typename F>
struct arg_specs_tuple_for<F, true> {
using type = arg_specs_tuple_for_async_procedure_t<F>;
};

template <typename F>
struct arg_specs_tuple_for<F, false> {
using type = arg_specs_tuple_for_sync_procedure_t<F>;
};

template <typename F>
using arg_specs_tuple_for_t =
typename arg_specs_tuple_for<F, is_async_procedure_v<F>>::type;

template <typename T>
struct args_specs_maker;

template <typename... Specs>
struct args_specs_maker<std::tuple<Specs...>> {
template <typename... Args>
static std::tuple<Specs...> make(Args&&... args)
{
if constexpr (sizeof...(Args) == 0) {
// default arg specs are arguments called "0", "1", ... and no default value
return iota(std::make_index_sequence<sizeof...(Specs)>());
}
else {
static_assert(
sizeof...(Args) == sizeof...(Specs),
"arguments specification must either match the number of "
"arguments or be empty");
return {Specs{std::forward<Args>(args)}...};
}
}

template <std::size_t... Idxs>
static std::tuple<Specs...> iota(std::index_sequence<Idxs...>)
{
return {Specs{std::to_string(Idxs)}...};
}
};

//! Options available for the argument specifications
//!
//! You are never expected to construct this structure yourself
//! but rather construct them by combining options such as
//! @ref packio::allow_extra_arguments "allow_extra_arguments"
//! with operator|
struct args_specs_options {
bool allow_extra_arguments = false;

constexpr args_specs_options operator|(const args_specs_options& other)
{
auto ret = *this;
ret.allow_extra_arguments |= other.allow_extra_arguments;
return ret;
}
};

template <typename SpecsTuple>
class args_specs {
public:
args_specs() : args_specs(args_specs_options{}){};

template <
typename T,
typename... Args,
typename = std::enable_if_t<!std::is_same_v<std::decay_t<T>, args_specs_options>>>
args_specs(T&& arg0, Args&&... args)
: args_specs(
args_specs_options{},
std::forward<T>(arg0),
std::forward<Args>(args)...)
{
}

template <typename... Args>
args_specs(args_specs_options opts, Args&&... args)
: specs_{args_specs_maker<SpecsTuple>::make(std::forward<Args>(args)...)},
opts_{std::move(opts)}
{
}

constexpr const args_specs_options& options() const { return opts_; }

template <std::size_t I>
constexpr decltype(auto) get() const
{
return std::get<I>(specs_);
}

static constexpr std::size_t size()
{
return std::tuple_size_v<SpecsTuple>;
}

private:
SpecsTuple specs_;
args_specs_options opts_{};
};
} // internal

//! Procedure arguments specifications
//! @tparam The procedure
//!
//! CLass that describes the arguments of the procedure
//! They each have a name and optionally a default value
//! This is a tuple-like class where each element can be
//! constructured from:
//! - a string, defining the name of the argument
//! - a @ref arg, defining the name of the argument
//! - a @ref arg::with_value, defining the name of the argyment
//! and its default value
//! Optionally, accepts
//! @ref packio::internal::args_specs_options "args_specs_options"
//! as first argument to customize the behavior of argument handling
template <typename Procedure>
class args_specs
// Using the real implementation as the base class reduces
// the number of templates instanciation
: public internal::args_specs<internal::arg_specs_tuple_for_t<Procedure>> {
public:
using base = internal::args_specs<internal::arg_specs_tuple_for_t<Procedure>>;
using base::base;
};

//! Option to allo extra arguments, ignoring them
constexpr auto allow_extra_arguments = internal::args_specs_options{true};

} // packio

#endif // PACKIO_ARGS_SPECS_H
7 changes: 6 additions & 1 deletion include/packio/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,12 @@ class client : public std::enable_shared_from_this<client<Rpc, Socket, Map>> {
PACKIO_TRACE("read: {}", length);
parser.buffer_consumed(length);

while (auto response = parser.get_response()) {
while (true) {
auto response = parser.get_response();
if (!response) {
PACKIO_INFO("stop reading: {}", response.error());
break;
}
self->async_call_handler(std::move(*response));
}

Expand Down
Loading

0 comments on commit a6f9d09

Please sign in to comment.