|
| 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 |
0 commit comments