Skip to content

Commit e104e76

Browse files
committed
fable: Extract Number<T> implementation into number_impl.hpp
1 parent db4f50a commit e104e76

File tree

4 files changed

+344
-192
lines changed

4 files changed

+344
-192
lines changed

fable/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ add_library(${target}
2121
src/fable/conf.cpp
2222
src/fable/json.cpp
2323
src/fable/schema.cpp
24+
src/fable/schema/number.cpp
2425
src/fable/schema/struct.cpp
2526
src/fable/schema/string.cpp
2627
src/fable/schema/variant.cpp

fable/include/fable/schema/number.hpp

+24-192
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,9 @@
2626
#define FABLE_SCHEMA_NUMBER_HPP_
2727

2828
#include <initializer_list> // for initializer_list<>
29-
#include <limits> // for numeric_limits<>
3029
#include <set> // for set<>
3130
#include <string> // for string
3231
#include <type_traits> // for enable_if_t<>, is_arithmetic<>
33-
#include <utility> // for move
34-
#include <vector> // for vector<>
3532

3633
#include <fable/schema/interface.hpp> // for Base<>
3734

@@ -45,222 +42,57 @@ class Number : public Base<Number<T>> {
4542
public: // Types and Constructors
4643
using Type = T;
4744

48-
template <typename X = T,
49-
std::enable_if_t<std::is_integral<X>::value && std::is_unsigned<X>::value, int> = 0>
45+
template <typename X = T, std::enable_if_t<std::is_integral<X>::value && std::is_unsigned<X>::value, int> = 0>
5046
Number(Type* ptr, std::string&& desc)
5147
: Base<Number<T>>(JsonType::number_unsigned, std::move(desc)), ptr_(ptr) {}
5248

53-
template <typename X = T,
54-
std::enable_if_t<std::is_integral<X>::value && std::is_signed<X>::value, int> = 0>
49+
template <typename X = T, std::enable_if_t<std::is_integral<X>::value && std::is_signed<X>::value, int> = 0>
5550
Number(Type* ptr, std::string&& desc)
5651
: Base<Number<T>>(JsonType::number_integer, std::move(desc)), ptr_(ptr) {}
5752

5853
template <typename X = T, std::enable_if_t<std::is_floating_point<X>::value, int> = 0>
5954
Number(Type* ptr, std::string&& desc)
6055
: Base<Number<T>>(JsonType::number_float, std::move(desc)), ptr_(ptr) {}
6156

57+
6258
public: // Special
6359
T minimum() const { return value_min_; }
6460
bool exclusive_minimum() const { return exclusive_min_; }
65-
Number<T> minimum(T value) && {
66-
value_min_ = value;
67-
exclusive_min_ = false;
68-
return std::move(*this);
69-
}
70-
Number<T> exclusive_minimum(T value) && {
71-
value_min_ = value;
72-
exclusive_min_ = true;
73-
return std::move(*this);
74-
}
61+
Number<T> minimum(T value) &&;
62+
Number<T> exclusive_minimum(T value) &&;
7563

7664
T maximum() const { return value_max_; }
7765
bool exclusive_maximum() const { return exclusive_max_; }
78-
Number<T> maximum(T value) && {
79-
value_max_ = value;
80-
exclusive_max_ = false;
81-
return std::move(*this);
82-
}
83-
Number<T> exclusive_maximum(T value) && {
84-
value_max_ = value;
85-
exclusive_max_ = true;
86-
return std::move(*this);
87-
}
66+
Number<T> maximum(T value) &&;
67+
Number<T> exclusive_maximum(T value) &&;
8868

89-
std::pair<T, T> bounds() const { return std::make_pair(value_min_, value_max_); }
90-
Number<T> bounds(T min, T max) && {
91-
exclusive_min_ = false;
92-
value_min_ = min;
93-
exclusive_max_ = false;
94-
value_max_ = max;
95-
return std::move(*this);
96-
}
97-
Number<T> bounds_with(T min, T max, std::initializer_list<T> whitelisted) {
98-
exclusive_min_ = false;
99-
value_min_ = min;
100-
exclusive_max_ = false;
101-
value_max_ = max;
102-
for (auto x : whitelisted) {
103-
insert_whitelist(x);
104-
}
105-
return std::move(*this);
106-
}
69+
std::pair<T, T> bounds() const;
70+
Number<T> bounds(T min, T max) &&;
71+
Number<T> bounds_with(T min, T max, std::initializer_list<T> whitelisted) &&;
10772

10873
const std::set<T>& whitelist() const { return whitelist_; }
109-
Number<T> whitelist(T x) && {
110-
insert_whitelist(x);
111-
return std::move(*this);
112-
}
113-
Number<T> whitelist(std::initializer_list<T> xs) && {
114-
for (auto x : xs) {
115-
insert_whitelist(x);
116-
}
117-
return std::move(*this);
118-
}
119-
void insert_whitelist(T x) {
120-
if (std::is_floating_point<T>::value) {
121-
throw std::logic_error("cannot whitelist floating-point numbers");
122-
}
123-
if (blacklist_.count(x)) {
124-
throw std::logic_error("cannot add blacklisted value to whitelist: " + std::to_string(x));
125-
}
126-
whitelist_.insert(x);
127-
}
74+
Number<T> whitelist(T x) &&;
75+
Number<T> whitelist(std::initializer_list<T> xs) &&;
76+
void insert_whitelist(T x);
12877

12978
const std::set<T>& blacklist() const { return blacklist_; }
130-
Number<T> blacklist(T x) && {
131-
insert_blacklist(x);
132-
return std::move(*this);
133-
}
134-
Number<T> blacklist(std::initializer_list<T> xs) && {
135-
for (auto x : xs) {
136-
insert_blacklist(x);
137-
}
138-
return std::move(*this);
139-
}
140-
void insert_blacklist(T x) {
141-
if (std::is_floating_point<T>::value) {
142-
throw std::logic_error("cannot blacklist floating-point numbers");
143-
}
144-
if (blacklist_.count(x)) {
145-
throw std::logic_error("cannot add whitelisted value to blacklist: " + std::to_string(x));
146-
}
147-
blacklist_.insert(x);
148-
}
79+
Number<T> blacklist(T x) &&;
80+
Number<T> blacklist(std::initializer_list<T> xs) &&;
81+
void insert_blacklist(T x);
14982

15083
public: // Overrides
151-
Json json_schema() const override {
152-
Json j{
153-
{"type", this->type_string()},
154-
{exclusive_min_ ? "exclusiveMinimum" : "minimum", value_min_},
155-
{exclusive_max_ ? "exclusiveMaximum" : "maximum", value_max_},
156-
};
157-
158-
if (!std::is_floating_point<T>::value) {
159-
auto write_list = [&j](auto name, auto xlist) {
160-
if (!xlist.empty()) {
161-
std::vector<T> xs;
162-
for (auto x : xlist) {
163-
xs.emplace_back(x);
164-
}
165-
j[name] = xs;
166-
}
167-
};
168-
169-
write_list("whitelist", whitelist_);
170-
write_list("blacklist", blacklist_);
171-
}
172-
173-
this->augment_schema(j);
174-
return j;
175-
}
176-
177-
void validate(const Conf& c) const override {
178-
switch (c->type()) {
179-
case JsonType::number_unsigned: {
180-
check_bounds<uint64_t>(c);
181-
break;
182-
}
183-
case JsonType::number_integer: {
184-
check_bounds<int64_t>(c);
185-
break;
186-
}
187-
case JsonType::number_float: {
188-
if (this->type() != JsonType::number_float) {
189-
this->throw_wrong_type(c);
190-
}
191-
check_bounds<double>(c);
192-
break;
193-
}
194-
default:
195-
this->throw_wrong_type(c);
196-
}
197-
}
198-
84+
Json json_schema() const override;
85+
void validate(const Conf& c) const override;
19986
using Interface::to_json;
200-
void to_json(Json& j) const override {
201-
assert(ptr_ != nullptr);
202-
j = serialize(*ptr_);
203-
}
204-
205-
void from_conf(const Conf& c) override {
206-
assert(ptr_ != nullptr);
207-
*ptr_ = deserialize(c);
208-
}
209-
210-
Json serialize(const Type& x) const { return x; }
211-
212-
Type deserialize(const Conf& c) const { return c.get<Type>(); }
213-
214-
void reset_ptr() override { ptr_ = nullptr; }
87+
void to_json(Json& j) const override;
88+
void from_conf(const Conf& c) override;
89+
Json serialize(const Type& x) const;
90+
Type deserialize(const Conf& c) const;
91+
void reset_ptr() override;
21592

21693
private:
217-
/**
218-
* Check that the min and max bounds are held by c.
219-
*/
22094
template <typename B>
221-
void check_bounds(const Conf& c) const {
222-
auto v = c.get<B>();
223-
224-
// Check whitelist and blacklist first.
225-
if (!std::is_floating_point<T>::value) {
226-
if (whitelist_.count(v)) {
227-
return;
228-
}
229-
if (blacklist_.count(v)) {
230-
this->throw_error(c, "unexpected blacklisted value {}", v);
231-
}
232-
}
233-
234-
if (!std::numeric_limits<B>::is_signed && value_min_ < 0) {
235-
// If B is unsigned and value_min_ is less than 0, there is no way
236-
// that v cannot fulfill the minimum requirements. Trying to use the
237-
// other branches will "underflow" the value_min_ which will invalidate
238-
// any comparison.
239-
} else if (exclusive_min_) {
240-
if (v <= static_cast<B>(value_min_)) {
241-
this->throw_error(c, "expected exclusive minimum of {}, got {}", value_min_, v);
242-
}
243-
} else {
244-
if (v < static_cast<B>(value_min_)) {
245-
this->throw_error(c, "expected minimum of {}, got {}", value_min_, v);
246-
}
247-
}
248-
249-
if (!std::numeric_limits<B>::is_signed && value_max_ < 0) {
250-
// If B is unsigned, but our maximum value is somewhere below 0, then v
251-
// will by definition always be out-of-bounds.
252-
this->throw_error(c, "expected {}maximum of {}, got {}", (exclusive_max_ ? "exclusive " : ""),
253-
value_max_, v);
254-
} else if (exclusive_max_) {
255-
if (v >= static_cast<B>(value_max_)) {
256-
this->throw_error(c, "expected exclusive maximum of {}, got {}", value_max_, v);
257-
}
258-
} else {
259-
if (v > static_cast<B>(value_max_)) {
260-
this->throw_error(c, "expected maximum of {}, got {}", value_max_, v);
261-
}
262-
}
263-
}
95+
void check_bounds(const Conf& c) const;
26496

26597
private:
26698
bool exclusive_min_{false};

0 commit comments

Comments
 (0)