scnlib  0.1.2
FormattedinputformodernC++
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups
args.h
Go to the documentation of this file.
1 // Copyright 2017-2019 Elias Kosunen
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 // This file is a part of scnlib:
16 // https://github.com/eliaskosunen/scnlib
17 
18 #ifndef SCN_DETAIL_ARGS_H
19 #define SCN_DETAIL_ARGS_H
20 
21 #include "parse_context.h"
22 #include "small_vector.h"
23 #include "util.h"
24 
25 #include <cstring>
26 
27 namespace scn {
29 
30  template <typename Context>
31  class basic_arg;
32  template <typename Context>
33  class basic_args;
34 
35  template <typename CharT, typename T, typename Enable = void>
36  struct scanner;
37 
38  template <typename T>
39  struct temporary {
40  temporary(T&& val) : value(std::move(val)) {}
41 
42  T& operator()() &&
43  {
44  return value;
45  }
46 
47  T value;
48  };
49  template <typename T>
50  temporary<T> temp(T&& val)
51  {
52  return {std::move(val)};
53  }
54 
55  namespace detail {
56  template <typename CharT>
58 
59  template <typename T, typename CharT>
60  struct named_arg;
61 
62  enum type {
63  none_type = 0,
65  // signed integer
70  // unsigned integer
75  // other integral types
79  // floats
84  // other
88  };
89 
90  SCN_CONSTEXPR bool is_integral(type t) noexcept
91  {
93  return t > none_type && t <= last_integer_type;
94  }
95  SCN_CONSTEXPR bool is_arithmetic(type t) noexcept
96  {
98  return t > none_type && t <= last_numeric_type;
99  }
100 
101  template <typename Context>
102  struct custom_value {
103  using fn_type = error (*)(void*, Context&);
104 
105  void* value;
107  };
108 
109  template <typename Context, typename T>
110  error scan_custom_arg(void* arg, Context& ctx)
111  {
112  SCN_EXPECT(arg != nullptr);
113 
114  typename Context::template scanner_type<T> s;
115  auto err = ctx.parse_context().parse(s, ctx);
116  if (!err) {
117  return err;
118  }
119  return s.scan(*static_cast<T*>(arg), ctx);
120  }
121 
122  struct monostate {
123  };
124 
125  template <typename Context>
126  class value {
127  public:
128  using char_type = typename Context::char_type;
129  using arg_type = typename Context::arg_type;
130 
131  union {
133 
134  short* short_value;
135  int* int_value;
136  long* long_value;
137  long long* long_long_value;
138 
139  unsigned short* ushort_value;
140  unsigned* uint_value;
141  unsigned long* ulong_value;
142  unsigned long long* ulong_long_value;
143 
144  bool* bool_value;
146 
147  float* float_value;
148  double* double_value;
149  long double* long_double_value;
150 
152  std::basic_string<char_type>* string_value;
154 
155  void* pointer;
156  };
157 
159 
160  value(short& val) : short_value(&val) {}
161  value(int& val) : int_value(&val) {}
162  value(long& val) : long_value(&val) {}
163  value(long long& val) : long_long_value(&val) {}
164 
165  value(unsigned short& val) : ushort_value(&val) {}
166  value(unsigned& val) : uint_value(&val) {}
167  value(unsigned long& val) : ulong_value(&val) {}
168  value(unsigned long long& val) : ulong_long_value(&val) {}
169 
170  value(bool& val) : bool_value(&val) {}
171  value(char_type& val) : char_value(&val) {}
172 
173  value(float& val) : float_value(&val) {}
174  value(double& val) : double_value(&val) {}
175  value(long double& val) : long_double_value(&val) {}
176 
178  value(std::basic_string<char_type>& val) : string_value(&val) {}
179 
180  value(void* val) : pointer(val) {}
181 
182  template <typename T>
183  value(T& val)
184  : custom(custom_value<Context>{std::addressof(val),
185  scan_custom_arg<Context, T>})
186  {
187  }
188 
189  named_arg_base<char_type>& as_named_arg()
190  {
191  SCN_EXPECT(pointer != nullptr);
192  return *static_cast<named_arg_base<char_type>*>(pointer);
193  }
194  };
195 
196  template <typename Context, typename T, type Type>
197  struct init {
198  T* val;
199  static const type type_tag = Type;
200 
201  SCN_CONSTEXPR init(T& v) : val(std::addressof(v)) {}
203  {
204  SCN_EXPECT(val != nullptr);
205  return value<Context>(*val);
206  }
207  };
208 
209  template <typename Context, typename T>
210  SCN_CONSTEXPR14 typename Context::arg_type make_arg(T& value);
211 
212 #define SCN_MAKE_VALUE(Tag, Type) \
213  template <typename C> \
214  SCN_CONSTEXPR init<C, Type, Tag> make_value(Type& val, priority_tag<1>) \
215  { \
216  return val; \
217  }
218 
219  SCN_MAKE_VALUE(short_type, short)
222  SCN_MAKE_VALUE(long_long_type, long long)
223 
224  SCN_MAKE_VALUE(ushort_type, unsigned short)
225  SCN_MAKE_VALUE(uint_type, unsigned)
226  SCN_MAKE_VALUE(ulong_type, unsigned long)
227  SCN_MAKE_VALUE(ulong_long_type, unsigned long long)
228 
230 
231  SCN_MAKE_VALUE(float_type, float)
232  SCN_MAKE_VALUE(double_type, double)
233  SCN_MAKE_VALUE(long_double_type, long double)
234 
235  SCN_MAKE_VALUE(buffer_type, span<typename C::char_type>)
236  SCN_MAKE_VALUE(string_type, std::basic_string<typename C::char_type>)
237 
238  template <typename C>
239  init<C, typename C::char_type, char_type> make_value(
240  typename C::char_type& val,
241  priority_tag<1>)
242  {
243  return val;
244  }
245 
246  template <typename C, typename T>
250  {
251  auto arg = make_arg<C>(val.value);
252  std::memcpy(val.data, &arg, sizeof(arg));
253  return static_cast<void*>(&val);
254  }
255 
256  template <typename T, typename Char, typename Enable = void>
258  : std::integral_constant<bool,
259  !std::is_arithmetic<T>::value &&
260  std::is_convertible<T, int>::value> {
261  };
262  template <typename C, typename T>
263  inline auto make_value(T& val, priority_tag<1>) ->
264  typename std::enable_if<
265  std::is_enum<T>::value &&
268  {
269  return static_cast<int>(val);
270  }
271 
272  template <typename C, typename T>
273  inline auto make_value(T& val, priority_tag<0>)
275  {
276  return val;
277  }
278 
279  enum : size_t { max_packed_args = sizeof(size_t) * 8 / 5 };
280  enum : size_t {
281  is_unpacked_bit = size_t{1} << (sizeof(size_t) * 8 - 1)
282  };
283 
284  template <typename Context>
285  class arg_map;
286  } // namespace detail
287 
289  SCN_CLANG_IGNORE("-Wpadded")
290 
291 
292  template <typename Context>
293  class SCN_TRIVIAL_ABI basic_arg {
294  public:
295  using char_type = typename Context::char_type;
296 
297  class handle {
298  public:
300  : m_custom(std::move(custom))
301  {
302  }
303 
304  error scan(Context& ctx)
305  {
306  return m_custom.scan(m_custom.value, ctx);
307  }
308 
309  private:
311  };
312 
313  SCN_CONSTEXPR basic_arg() = default;
314 
315  explicit operator bool() const noexcept
316  {
317  return m_type != detail::none_type;
318  }
319 
321  {
322  return type;
323  }
324  bool is_integral() const
325  {
326  return detail::is_integral(m_type);
327  }
328  bool is_arithmetic() const
329  {
330  return detail::is_arithmetic(m_type);
331  }
332 
333  private:
335  : m_value(v), m_type(t)
336  {
337  }
338 
339  template <typename ContextType, typename T>
340  friend SCN_CONSTEXPR14 typename ContextType::arg_type detail::make_arg(
341  T& value);
342 
343  template <typename Ctx, typename Visitor>
344  friend SCN_CONSTEXPR14 error visit_arg(Visitor&& vis,
345  typename Ctx::arg_type& arg);
346 
347  friend class basic_args<Context>;
348  friend class detail::arg_map<Context>;
349 
350  detail::value<Context> m_value{};
352  };
353 
355 
356  template <typename Context, typename Visitor>
358  typename Context::arg_type& arg)
359  {
360  SCN_EXPECT(arg.m_type != detail::named_arg_type);
361  switch (arg.m_type) {
362  case detail::none_type:
364  break;
365 
366  case detail::short_type:
367  return vis(*arg.m_value.short_value);
368  case detail::int_type:
369  return vis(*arg.m_value.int_value);
370  case detail::long_type:
371  return vis(*arg.m_value.long_value);
373  return vis(*arg.m_value.long_long_value);
374 
375  case detail::ushort_type:
376  return vis(*arg.m_value.ushort_value);
377  case detail::uint_type:
378  return vis(*arg.m_value.uint_value);
379  case detail::ulong_type:
380  return vis(*arg.m_value.ulong_value);
382  return vis(*arg.m_value.ulong_long_value);
383 
384  case detail::bool_type:
385  return vis(*arg.m_value.bool_value);
386  case detail::char_type:
387  return vis(*arg.m_value.char_value);
388 
389  case detail::float_type:
390  return vis(*arg.m_value.float_value);
391  case detail::double_type:
392  return vis(*arg.m_value.double_value);
394  return vis(*arg.m_value.long_double_value);
395 
396  case detail::buffer_type:
397  return vis(*arg.m_value.buffer_value);
398  case detail::string_type:
399  return vis(*arg.m_value.string_value);
400 
401  case detail::custom_type:
402  return vis(
403  typename Context::arg_type::handle(arg.m_value.custom));
404 
406  SCN_CLANG_IGNORE("-Wcovered-switch-default")
407  default:
408  return vis(detail::monostate{});
410  }
412  }
413 
414  namespace detail {
415  template <typename Context>
416  class arg_map {
417  public:
418  using char_type = typename Context::char_type;
419 
420  arg_map() = default;
421 
422  void init(const basic_args<Context>& args)
423  {
424  if (!m_args.empty()) {
425  return;
426  }
427 
428  m_args.resize(args.max_size());
429  if (args.is_packed()) {
430  for (size_t i = 0;; ++i) {
431  switch (args.type(i)) {
432  case none_type:
433  return;
434  case named_arg_type:
435  push_back(args.m_values[i]);
436  break;
437  }
438  }
439  }
440  for (size_t i = 0;; ++i) {
441  switch (args.m_args[i].m_type) {
442  case none_type:
443  return;
444  case named_arg_type:
445  push_back(args.m_args[i].m_value);
446  break;
447  }
448  }
449  }
450 
451  typename Context::arg_type find(
452  basic_string_view<char_type> name) const
453  {
454  SCN_EXPECT(!m_args.empty());
455  // Use for instead of find_if to avoid including <algorithm>
456  for (auto& e : m_args) {
457  if (e.name == name) {
458  return e.arg;
459  }
460  }
461  return {};
462  }
463 
464  private:
465  struct entry {
467  typename Context::arg_type arg;
468  };
469 
470  void push_back(value<Context> val)
471  {
472  const named_arg_base<char_type>& named = val.as_named_arg();
473  m_args.emplace_back(named.name,
474  named.template deserialize<Context>());
475  SCN_ENSURE(!m_args.empty());
476  }
477 
478  detail::small_vector<entry, 2> m_args;
479  };
480 
481  template <typename Context, typename T>
482  struct get_type {
483  using value_type = decltype(make_value<Context>(
484  std::declval<typename std::remove_reference<
485  typename std::remove_cv<T>::type>::type&>(),
486  std::declval<priority_tag<1>>()));
487  static const type value = value_type::type_tag;
488  };
489 
490  template <typename Context>
492  {
493  return 0;
494  }
495  template <typename Context, typename Arg, typename... Args>
497  {
498  return static_cast<size_t>(get_type<Context, Arg>::value) |
499  (get_types<Context, Args...>() << 5);
500  }
501 
502  template <typename Context, typename T>
503  SCN_CONSTEXPR14 typename Context::arg_type make_arg(T& value)
504  {
505  typename Context::arg_type arg;
506  arg.m_type = get_type<Context, T>::value;
507  arg.m_value = make_value<Context>(value, priority_tag<1>{});
508  return arg;
509  }
510 
511  template <bool Packed, typename Context, typename T>
512  inline auto make_arg(T& v) ->
513  typename std::enable_if<Packed, value<Context>>::type
514  {
515  return make_value<Context>(v, priority_tag<1>{});
516  }
517  template <bool Packed, typename Context, typename T>
518  inline auto make_arg(T& v) ->
520  {
521  return typename Context::arg_type(v);
522  }
523  } // namespace detail
524 
525  template <typename Context, typename... Args>
526  class arg_store {
527  static SCN_CONSTEXPR const size_t num_args = sizeof...(Args);
528  static const bool is_packed = num_args < detail::max_packed_args;
529 
530  friend class basic_args<Context>;
531 
532  static SCN_CONSTEXPR size_t get_types()
533  {
534  return is_packed ? detail::get_types<Context, Args...>()
535  : detail::is_unpacked_bit | num_args;
536  }
537 
538  public:
539  static SCN_CONSTEXPR size_t types = get_types();
540  using arg_type = typename Context::arg_type;
541 
542  using value_type = typename std::
543  conditional<is_packed, detail::value<Context>, arg_type>::type;
544  static SCN_CONSTEXPR size_t data_size =
545  num_args + (is_packed && num_args != 0 ? 0 : 1);
546 
547  arg_store(Args&... a)
548  : m_data{{detail::make_arg<is_packed, Context>(a)...}}
549  {
550  }
551 
552  span<value_type> data()
553  {
554  return make_span(m_data.data(),
555  static_cast<std::ptrdiff_t>(m_data.size()));
556  }
557 
558  private:
560  };
561 
562  template <typename Context, typename... Args>
563  typename Context::template arg_store_type<Args...> make_args(Args&... args)
564  {
565  return {args...};
566  }
567 
568  template <typename Context>
569  class basic_args {
570  public:
571  using arg_type = typename Context::arg_type;
572 
573  basic_args() = default;
574 
575  template <typename... Args>
576  basic_args(arg_store<Context, Args...>& store) : m_types(store.types)
577  {
578  set_data(store.m_data.data());
579  }
580 
582  : m_types(detail::is_unpacked_bit | args.size())
583  {
584  set_data(args.data());
585  }
586 
587  arg_type get(size_t i) const
588  {
589  auto arg = do_get(i);
590  if (arg.m_type == detail::named_arg_type) {
591  arg =
592  arg.m_value.as_named_arg().template deserialize<Context>();
593  }
594  return arg;
595  }
596 
597  bool check_id(size_t i) const
598  {
599  if (!is_packed()) {
600  return i < (m_types &
601  ~static_cast<size_t>(detail::is_unpacked_bit));
602  }
603  return type(i) != detail::none_type;
604  }
605 
606  size_t max_size() const
607  {
608  return is_packed()
609  ? static_cast<size_t>(detail::max_packed_args)
610  : m_types &
611  ~static_cast<size_t>(detail::is_unpacked_bit);
612  }
613 
614  private:
615  size_t m_types{0};
616  union {
619  };
620 
621  bool is_packed() const
622  {
623  return (m_types & detail::is_unpacked_bit) == 0;
624  }
625 
626  typename detail::type type(size_t i) const
627  {
628  size_t shift = i * 5;
629  return static_cast<typename detail::type>(
630  (m_types & (size_t{0x1f} << shift)) >> shift);
631  }
632 
633  friend class detail::arg_map<Context>;
634 
635  void set_data(detail::value<Context>* values)
636  {
637  m_values = values;
638  }
639  void set_data(arg_type* args)
640  {
641  m_args = args;
642  }
643 
644  arg_type do_get(size_t i) const
645  {
646  if (!is_packed()) {
647  auto num_args = max_size();
648  if (SCN_LIKELY(i < num_args)) {
649  return m_args[i];
650  }
651  return {};
652  }
653 
655  return {};
656 
657  arg_type arg;
658  arg.m_type = type(i);
659  if (arg.m_type == detail::none_type) {
660  return {};
661  }
662  arg.m_value = m_values[i];
663  return arg;
664  }
665  };
666 
668 } // namespace scn
669 
670 #endif // SCN_DETAIL_ARGS_H
error scan(Context &ctx)
Definition: args.h:304
span< char_type > * buffer_value
Definition: args.h:151
value(long double &val)
Definition: args.h:175
unsigned long * ulong_value
Definition: args.h:141
value(unsigned &val)
Definition: args.h:166
double * double_value
Definition: args.h:148
value(unsigned short &val)
Definition: args.h:165
decltype(make_value< Context >(std::declval< typename std::remove_reference< typename std::remove_cv< T >::type >::type & >(), std::declval< priority_tag< 1 >>())) value_type
Definition: args.h:486
int * int_value
Definition: args.h:135
basic_args(arg_store< Context, Args...> &store)
Definition: args.h:576
#define SCN_END_NAMESPACE
Definition: config.h:376
typename arg_context_base::arg_type arg_type
Definition: args.h:129
value(bool &val)
Definition: args.h:170
detail::named_arg< T, char > arg(string_view name, T &arg)
Definition: context.h:389
#define SCN_UNLIKELY(x)
Definition: config.h:337
error scan_custom_arg(void *arg, Context &ctx)
Definition: args.h:110
value(char_type &val)
Definition: args.h:171
value(std::basic_string< char_type > &val)
Definition: args.h:178
float * float_value
Definition: args.h:147
typename Context::char_type char_type
Definition: args.h:295
arg_store(Args &...a)
Definition: args.h:547
value(T &val)
Definition: args.h:183
init< C, typename C::char_type, char_type > make_value(typename C::char_type &val, priority_tag< 1 >)
Definition: args.h:239
typename std::conditional< is_packed, detail::value< Context >, arg_type >::type value_type
Definition: args.h:543
bool check_id(size_t i) const
Definition: args.h:597
value(double &val)
Definition: args.h:174
unsigned short * ushort_value
Definition: args.h:139
#define SCN_UNREACHABLE
Definition: config.h:322
SCN_CONSTEXPR bool is_integral(type t) noexcept
Definition: args.h:90
size_t max_size() const
Definition: args.h:606
value(long &val)
Definition: args.h:162
value(int &val)
Definition: args.h:161
void * pointer
Definition: args.h:155
#define SCN_CLANG_IGNORE(x)
Definition: config.h:128
basic_args(span< arg_type > args)
Definition: args.h:581
SCN_CONSTEXPR14 Context::arg_type make_arg(T &value)
Definition: args.h:503
value(unsigned long &val)
Definition: args.h:167
arg_type * m_args
Definition: args.h:618
value(span< char_type > &val)
Definition: args.h:177
T & operator()()&&
Definition: args.h:42
SCN_CONSTEXPR pointer data() const noexcept
Definition: span.h:98
static const type type_tag
Definition: args.h:199
void init(const basic_args< Context > &args)
Definition: args.h:422
SCN_CONSTEXPR size_t get_types()
Definition: args.h:491
long long * long_long_value
Definition: args.h:137
SCN_CONSTEXPR span< T > make_span(T *ptr, std::size_t count) noexcept
Definition: span.h:145
temporary< T > temp(T &&val)
Definition: args.h:50
bool is_arithmetic() const
Definition: args.h:328
long * long_value
Definition: args.h:136
typename Context::arg_type arg_type
Definition: args.h:540
SCN_CONSTEXPR value()
Definition: args.h:158
#define SCN_CLANG_POP
Definition: config.h:127
#define SCN_MAKE_VALUE(Tag, Type)
Definition: args.h:212
temporary(T &&val)
Definition: args.h:40
typename arg_context_base::arg_type arg_type
Definition: args.h:571
#define SCN_TRIVIAL_ABI
Definition: config.h:246
detail::value< Context > * m_values
Definition: args.h:617
typename Context::char_type char_type
Definition: args.h:128
bool * bool_value
Definition: args.h:144
bool is_integral() const
Definition: args.h:324
Error class.
Definition: result.h:32
Context::template arg_store_type< Args...> make_args(Args &...args)
Definition: args.h:563
#define SCN_BEGIN_NAMESPACE
Definition: config.h:375
#define SCN_ENSURE(cond)
Definition: config.h:372
value(float &val)
Definition: args.h:173
SCN_CONSTEXPR bool is_arithmetic(type t) noexcept
Definition: args.h:95
SCN_CLANG_POP SCN_CONSTEXPR14 error visit_arg(Visitor &&vis, typename Context::arg_type &arg)
Definition: args.h:357
value(long long &val)
Definition: args.h:163
typename Context::char_type char_type
Definition: args.h:418
char_type * char_value
Definition: args.h:145
custom_value< Context > custom
Definition: args.h:153
monostate empty_value
Definition: args.h:132
unsigned * uint_value
Definition: args.h:140
SCN_CONSTEXPR init(T &v)
Definition: args.h:201
error(*)(void *, arg_context_base &) fn_type
Definition: args.h:103
#define SCN_CONSTEXPR
Definition: config.h:226
unsigned long long * ulong_long_value
Definition: args.h:142
Context::arg_type find(basic_string_view< char_type > name) const
Definition: args.h:451
detail::type type() const
Definition: args.h:320
std::basic_string< char_type > * string_value
Definition: args.h:152
#define SCN_LIKELY(x)
Definition: config.h:336
Type-erased scanning argument.
Definition: args.h:31
value(unsigned long long &val)
Definition: args.h:168
handle(detail::custom_value< Context > custom)
Definition: args.h:299
value(void *val)
Definition: args.h:180
long double * long_double_value
Definition: args.h:149
value(short &val)
Definition: args.h:160
short * short_value
Definition: args.h:134
#define SCN_EXPECT(cond)
Definition: config.h:371
#define SCN_CLANG_PUSH
Definition: config.h:126
#define SCN_CONSTEXPR14
Definition: config.h:227