$darkmode
number_impl.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2020 Robert Bosch GmbH
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  * SPDX-License-Identifier: Apache-2.0
17  */
27 #pragma once
28 #ifndef FABLE_SCHEMA_NUMBER_IMPL_HPP_
29 #define FABLE_SCHEMA_NUMBER_IMPL_HPP_
30 
31 #include <initializer_list> // for initializer_list<>
32 #include <limits> // for numeric_limits<>
33 #include <set> // for set<>
34 #include <string> // for string
35 #include <type_traits> // for enable_if_t<>, is_arithmetic<>
36 #include <utility> // for move
37 #include <vector> // for vector<>
38 
39 #include <fable/schema/number.hpp> // for Number<>
40 
41 namespace fable {
42 namespace schema {
43 
44 template <typename T>
45 Number<T> Number<T>::minimum(T value) && {
46  value_min_ = value;
47  exclusive_min_ = false;
48  return std::move(*this);
49 }
50 
51 template <typename T>
52 Number<T> Number<T>::exclusive_minimum(T value) && {
53  value_min_ = value;
54  exclusive_min_ = true;
55  return std::move(*this);
56 }
57 
58 template <typename T>
59 Number<T> Number<T>::maximum(T value) && {
60  value_max_ = value;
61  exclusive_max_ = false;
62  return std::move(*this);
63 }
64 
65 template <typename T>
66 Number<T> Number<T>::exclusive_maximum(T value) && {
67  value_max_ = value;
68  exclusive_max_ = true;
69  return std::move(*this);
70 }
71 
72 template <typename T>
73 std::pair<T, T> Number<T>::bounds() const { return std::make_pair(value_min_, value_max_); }
74 
75 template <typename T>
76 Number<T> Number<T>::bounds(T min, T max) && {
77  exclusive_min_ = false;
78  value_min_ = min;
79  exclusive_max_ = false;
80  value_max_ = max;
81  return std::move(*this);
82 }
83 
84 template <typename T>
85 Number<T> Number<T>::bounds_with(T min, T max, std::initializer_list<T> whitelisted) && {
86  exclusive_min_ = false;
87  value_min_ = min;
88  exclusive_max_ = false;
89  value_max_ = max;
90  for (auto x : whitelisted) {
91  insert_whitelist(x);
92  }
93  return std::move(*this);
94 }
95 
96 template <typename T>
97 Number<T> Number<T>::whitelist(T x) && {
98  insert_whitelist(x);
99  return std::move(*this);
100 }
101 
102 template <typename T>
103 Number<T> Number<T>::whitelist(std::initializer_list<T> xs) && {
104  for (auto x : xs) {
105  insert_whitelist(x);
106  }
107  return std::move(*this);
108 }
109 
110 template <typename T>
111 void Number<T>::insert_whitelist(T x) {
112  if (std::is_floating_point<T>::value) {
113  throw std::logic_error("cannot whitelist floating-point numbers");
114  }
115  if (blacklist_.count(x)) {
116  throw std::logic_error("cannot add blacklisted value to whitelist: " + std::to_string(x));
117  }
118  whitelist_.insert(x);
119 }
120 
121 template <typename T>
122 Number<T> Number<T>::blacklist(T x) && {
123  insert_blacklist(x);
124  return std::move(*this);
125 }
126 
127 template <typename T>
128 Number<T> Number<T>::blacklist(std::initializer_list<T> xs) && {
129  for (auto x : xs) {
130  insert_blacklist(x);
131  }
132  return std::move(*this);
133 }
134 
135 template <typename T>
136 void Number<T>::insert_blacklist(T x) {
137  if (std::is_floating_point<T>::value) {
138  throw std::logic_error("cannot blacklist floating-point numbers");
139  }
140  if (blacklist_.count(x)) {
141  throw std::logic_error("cannot add whitelisted value to blacklist: " + std::to_string(x));
142  }
143  blacklist_.insert(x);
144 }
145 
146 template <typename T>
148  Json j{
149  {"type", this->type_string()},
150  {exclusive_min_ ? "exclusiveMinimum" : "minimum", value_min_},
151  {exclusive_max_ ? "exclusiveMaximum" : "maximum", value_max_},
152  };
153 
154  if (!std::is_floating_point<T>::value) {
155  auto write_list = [&j](auto name, auto xlist) {
156  if (!xlist.empty()) {
157  std::vector<T> xs;
158  for (auto x : xlist) {
159  xs.emplace_back(x);
160  }
161  j[name] = xs;
162  }
163  };
164 
165  write_list("whitelist", whitelist_);
166  write_list("blacklist", blacklist_);
167  }
168 
169  this->augment_schema(j);
170  return j;
171 }
172 
173 template <typename T>
174 void Number<T>::validate(const Conf& c) const {
175  switch (c->type()) {
176  case JsonType::number_unsigned: {
177  check_bounds<uint64_t>(c);
178  break;
179  }
180  case JsonType::number_integer: {
181  check_bounds<int64_t>(c);
182  break;
183  }
184  case JsonType::number_float: {
185  if (this->type() != JsonType::number_float) {
186  this->throw_wrong_type(c);
187  }
188  check_bounds<double>(c);
189  break;
190  }
191  default:
192  this->throw_wrong_type(c);
193  }
194 }
195 
196 template <typename T>
197 void Number<T>::to_json(Json& j) const {
198  assert(ptr_ != nullptr);
199  j = serialize(*ptr_);
200 }
201 
202 template <typename T>
203 void Number<T>::from_conf(const Conf& c) {
204  assert(ptr_ != nullptr);
205  *ptr_ = deserialize(c);
206 }
207 
208 template <typename T>
209 Json Number<T>::serialize(const T& x) const { return x; }
210 
211 template <typename T>
212 T Number<T>::deserialize(const Conf& c) const { return c.get<T>(); }
213 
214 template <typename T>
215 void Number<T>::reset_ptr() { ptr_ = nullptr; }
216 
220 template <typename T>
221 template <typename B>
222 void Number<T>::check_bounds(const Conf& c) const {
223  auto v = c.get<B>();
224 
225  // Check whitelist and blacklist first.
226  if (!std::is_floating_point<T>::value) {
227  if (whitelist_.count(v)) {
228  return;
229  }
230  if (blacklist_.count(v)) {
231  this->throw_error(c, "unexpected blacklisted value {}", v);
232  }
233  }
234 
235  if (!std::numeric_limits<B>::is_signed && value_min_ < 0) {
236  // If B is unsigned and value_min_ is less than 0, there is no way
237  // that v cannot fulfill the minimum requirements. Trying to use the
238  // other branches will "underflow" the value_min_ which will invalidate
239  // any comparison.
240  } else if (exclusive_min_) {
241  if (v <= static_cast<B>(value_min_)) {
242  this->throw_error(c, "expected exclusive minimum of {}, got {}", value_min_, v);
243  }
244  } else {
245  if (v < static_cast<B>(value_min_)) {
246  this->throw_error(c, "expected minimum of {}, got {}", value_min_, v);
247  }
248  }
249 
250  if (!std::numeric_limits<B>::is_signed && value_max_ < 0) {
251  // If B is unsigned, but our maximum value is somewhere below 0, then v
252  // will by definition always be out-of-bounds.
253  this->throw_error(c, "expected {}maximum of {}, got {}", (exclusive_max_ ? "exclusive " : ""),
254  value_max_, v);
255  } else if (exclusive_max_) {
256  if (v >= static_cast<B>(value_max_)) {
257  this->throw_error(c, "expected exclusive maximum of {}, got {}", value_max_, v);
258  }
259  } else {
260  if (v > static_cast<B>(value_max_)) {
261  this->throw_error(c, "expected maximum of {}, got {}", value_max_, v);
262  }
263  }
264 }
265 
266 } // namespace schema
267 } // namespace fable
268 
269 #endif // FABLE_SCHEMA_NUMBER_IMPL_HPP_
Definition: conf.hpp:74
void from_conf(const Conf &c) override
Definition: number_impl.hpp:203
Definition: number.hpp:39
T get() const
Definition: conf.hpp:164
Json json_schema() const override
Definition: number_impl.hpp:147
nlohmann::json Json
Definition: json.hpp:62
Definition: conf.hpp:70
virtual Json to_json() const
Definition: interface.hpp:220
void reset_ptr() override
Definition: number_impl.hpp:215
void validate(const Conf &c) const override
Definition: number_impl.hpp:174