scnlib  0.2.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
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 "util.h"
23 
24 #include <cstring>
25 
26 namespace scn {
28 
29  template <typename Context>
30  class basic_arg;
31  template <typename Context>
32  class basic_args;
33 
34  template <typename CharT, typename T, typename Enable = void>
35  struct scanner;
36 
44  template <typename T>
45  struct temporary {
46  temporary(T&& val) : value(std::move(val)) {}
47 
48  T& operator()() && noexcept
49  {
50  return value;
51  }
52 
53  T value;
54  };
60  template <typename T>
61  temporary<T> temp(T&& val)
62  {
63  return {std::move(val)};
64  }
65 
66  namespace detail {
67  enum type {
68  none_type = 0,
69  // signed integer
74  // unsigned integer
79  // other integral types
83  // floats
88  // other
92 
94  };
95 
96  constexpr bool is_integral(type t) noexcept
97  {
98  return t > none_type && t <= last_integer_type;
99  }
100  constexpr bool is_arithmetic(type t) noexcept
101  {
102  return t > none_type && t <= last_numeric_type;
103  }
104 
105  struct custom_value {
106  // using scan_type = error (*)(void*, Context&, ParseCtx&);
107 
108  void* value;
109  void (*scan)();
110  };
111 
112  template <typename Context, typename ParseCtx, typename T>
113  error scan_custom_arg(void* arg, Context& ctx, ParseCtx& pctx) noexcept
114  {
115  SCN_EXPECT(arg != nullptr);
116 
117  typename Context::template scanner_type<T> s;
118  auto err = pctx.parse(s);
119  if (!err) {
120  return err;
121  }
122  return s.scan(*static_cast<T*>(arg), ctx);
123  }
124 
125  struct monostate {
126  };
127 
128  template <typename ParseCtx>
129  struct parse_ctx_tag {
130  };
131 
132  template <typename Context>
133  class value {
134  public:
135  using char_type = typename Context::char_type;
136  using arg_type = typename Context::arg_type;
137 
138  constexpr value() noexcept : m_empty{} {}
139 
140  template <typename T>
141  SCN_CONSTEXPR14 value(T& val) noexcept
142  : m_value(std::addressof(val))
143  {
144  }
145 
146  template <typename ParseCtx, typename T>
147  value(parse_ctx_tag<ParseCtx>, T& val) noexcept
148  : m_custom(
149  custom_value{std::addressof(val),
150  reinterpret_cast<void (*)()>(
151  &scan_custom_arg<Context, ParseCtx, T>)})
152  {
153  }
154 
155  template <typename T>
156  SCN_CONSTEXPR14 T& get_as() noexcept
157  {
158  return *static_cast<T*>(m_value);
159  }
160  template <typename T>
161  constexpr const T& get_as() const noexcept
162  {
163  return *static_cast<const T*>(m_value);
164  }
165 
167  {
168  return m_custom;
169  }
170  constexpr const custom_value& get_custom() const noexcept
171  {
172  return m_custom;
173  }
174 
175  private:
176  union {
178  void* m_value;
180  };
181  };
182 
183  template <typename Context, typename T, type Type>
184  struct init {
185  T* val;
186  static const type type_tag = Type;
187 
188  constexpr init(T& v) : val(std::addressof(v)) {}
189  template <typename ParseCtx>
191  {
192  SCN_EXPECT(val != nullptr);
193  return value<Context>(*val);
194  }
195  };
196  template <typename Context, typename T>
197  struct init<Context, T, custom_type> {
198  T* val;
199  static const type type_tag = custom_type;
200 
201  constexpr init(T& v) : val(std::addressof(v)) {}
202  template <typename ParseCtx>
204  {
205  SCN_EXPECT(val != nullptr);
206  return value<Context>(parse_ctx_tag<ParseCtx>(), *val);
207  }
208  };
209 
210  template <typename Context, typename ParseCtx, typename T>
211  SCN_CONSTEXPR14 typename Context::arg_type make_arg(T& value) noexcept;
212 
213 #define SCN_MAKE_VALUE(Tag, Type) \
214  template <typename C> \
215  constexpr init<C, Type, Tag> make_value(Type& val, \
216  priority_tag<1>) noexcept \
217  { \
218  return val; \
219  }
220 
221  SCN_MAKE_VALUE(short_type, short)
224  SCN_MAKE_VALUE(long_long_type, long long)
225 
226  SCN_MAKE_VALUE(ushort_type, unsigned short)
227  SCN_MAKE_VALUE(uint_type, unsigned)
228  SCN_MAKE_VALUE(ulong_type, unsigned long)
229  SCN_MAKE_VALUE(ulong_long_type, unsigned long long)
230 
232 
233  SCN_MAKE_VALUE(float_type, float)
234  SCN_MAKE_VALUE(double_type, double)
235  SCN_MAKE_VALUE(long_double_type, long double)
236 
237  SCN_MAKE_VALUE(buffer_type, span<typename C::char_type>)
238  SCN_MAKE_VALUE(string_type, std::basic_string<typename C::char_type>)
240  basic_string_view<typename C::char_type>)
241 
242  template <typename C>
243  constexpr init<C, typename C::char_type, char_type> make_value(
244  typename C::char_type& val,
245  priority_tag<1>) noexcept
246  {
247  return val;
248  }
249 
250  template <typename T, typename Char, typename Enable = void>
252  : std::integral_constant<bool,
253  !std::is_arithmetic<T>::value &&
254  std::is_convertible<T, int>::value> {
255  };
256  template <typename C, typename T>
257  constexpr inline auto make_value(T& val, priority_tag<1>) noexcept ->
258  typename std::enable_if<
259  std::is_enum<T>::value &&
260  convert_to_int<T, typename C::char_type>::value,
261  init<C, int, int_type>>::type
262  {
263  return static_cast<int>(val);
264  }
265 
266  template <typename C, typename T>
267  constexpr inline auto make_value(T& val, priority_tag<0>) noexcept
268  -> init<C, T, custom_type>
269  {
270  return val;
271  }
272 
273  enum : std::ptrdiff_t {
276  max_packed_args = (sizeof(size_t) * 8 - 1) / packed_arg_bitsize
277  };
278  enum : size_t {
279  is_unpacked_bit = size_t{1} << (sizeof(size_t) * 8 - 1)
280  };
281  } // namespace detail
282 
284  SCN_CLANG_IGNORE("-Wpadded")
285 
286 
287  template <typename Context>
288  class SCN_TRIVIAL_ABI basic_arg {
289  public:
290  using char_type = typename Context::char_type;
291 
292  class handle {
293  public:
294  explicit handle(detail::custom_value custom)
295  : m_custom(std::move(custom))
296  {
297  }
298 
299  template <typename ParseCtx>
300  error scan(Context& ctx, ParseCtx& pctx)
301  {
302  return reinterpret_cast<error (*)(void*, Context&, ParseCtx&)>(
303  m_custom.scan)(m_custom.value, ctx, pctx);
304  }
305 
306  private:
308  };
309 
310  constexpr basic_arg() = default;
311 
312  constexpr explicit operator bool() const noexcept
313  {
314  return m_type != detail::none_type;
315  }
316 
317  constexpr detail::type type() const noexcept
318  {
319  return type;
320  }
321  constexpr bool is_integral() const noexcept
322  {
323  return detail::is_integral(m_type);
324  }
325  constexpr bool is_arithmetic() const noexcept
326  {
327  return detail::is_arithmetic(m_type);
328  }
329 
330  private:
331  constexpr basic_arg(detail::value<Context> v, detail::type t) noexcept
332  : m_value(v), m_type(t)
333  {
334  }
335 
336  template <typename ContextType, typename ParseCtx, typename T>
337  friend SCN_CONSTEXPR14 typename ContextType::arg_type detail::make_arg(
338  T& value) noexcept;
339 
340  template <typename Ctx, typename Visitor>
341  friend SCN_CONSTEXPR14 error visit_arg(Visitor&& vis,
342  typename Ctx::arg_type& arg);
343 
344  friend class basic_args<Context>;
345 
348  };
349 
351 
352  template <typename Context, typename Visitor>
354  typename Context::arg_type& arg)
355  {
356  using char_type = typename Context::char_type;
357  switch (arg.m_type) {
358  case detail::none_type:
359  break;
360 
361  case detail::short_type:
362  return vis(arg.m_value.template get_as<short>());
363  case detail::int_type:
364  return vis(arg.m_value.template get_as<int>());
365  case detail::long_type:
366  return vis(arg.m_value.template get_as<long>());
368  return vis(arg.m_value.template get_as<long long>());
369 
370  case detail::ushort_type:
371  return vis(arg.m_value.template get_as<unsigned short>());
372  case detail::uint_type:
373  return vis(arg.m_value.template get_as<unsigned int>());
374  case detail::ulong_type:
375  return vis(arg.m_value.template get_as<unsigned long>());
377  return vis(arg.m_value.template get_as<unsigned long long>());
378 
379  case detail::bool_type:
380  return vis(arg.m_value.template get_as<bool>());
381  case detail::char_type:
382  return vis(arg.m_value.template get_as<char_type>());
383 
384  case detail::float_type:
385  return vis(arg.m_value.template get_as<float>());
386  case detail::double_type:
387  return vis(arg.m_value.template get_as<double>());
389  return vis(arg.m_value.template get_as<long double>());
390 
391  case detail::buffer_type:
392  return vis(arg.m_value.template get_as<span<char_type>>());
393  case detail::string_type:
394  return vis(
395  arg.m_value
396  .template get_as<std::basic_string<char_type>>());
398  return vis(
399  arg.m_value
400  .template get_as<basic_string_view<char_type>>());
401 
402  case detail::custom_type:
403  return vis(typename Context::arg_type::handle(
404  arg.m_value.get_custom()));
405 
407  SCN_CLANG_IGNORE("-Wcovered-switch-default")
408  default:
409  return vis(detail::monostate{});
411  }
413  }
414 
415  namespace detail {
416  template <typename Context, typename T>
417  struct get_type {
418  using value_type = decltype(make_value<Context>(
419  std::declval<typename std::remove_reference<
420  typename std::remove_cv<T>::type>::type&>(),
421  std::declval<priority_tag<1>>()));
422  static const type value = value_type::type_tag;
423  };
424 
425  template <typename Context>
426  constexpr size_t get_types()
427  {
428  return 0;
429  }
430  template <typename Context, typename Arg, typename... Args>
431  constexpr size_t get_types()
432  {
433  return static_cast<size_t>(get_type<Context, Arg>::value) |
434  (get_types<Context, Args...>() << 5);
435  }
436 
437  template <typename Context, typename ParseCtx, typename T>
438  SCN_CONSTEXPR14 typename Context::arg_type make_arg(T& value) noexcept
439  {
440  typename Context::arg_type arg;
441  arg.m_type = get_type<Context, T>::value;
442  arg.m_value = make_value<Context>(value, priority_tag<1>{})
443  .template get<ParseCtx>();
444  return arg;
445  }
446 
447  template <bool Packed, typename Context, typename ParseCtx, typename T>
448  inline auto make_arg(T& v) ->
449  typename std::enable_if<Packed, value<Context>>::type
450  {
451  return make_value<Context>(v, priority_tag<1>{})
452  .template get<ParseCtx>();
453  }
454  template <bool Packed, typename Context, typename ParseCtx, typename T>
455  inline auto make_arg(T& v) ->
457  {
458  return make_arg<Context, ParseCtx>(v);
459  }
460  } // namespace detail
461 
462  template <typename Context, typename... Args>
463  class arg_store {
464  static constexpr const size_t num_args = sizeof...(Args);
465  static const bool is_packed = num_args < detail::max_packed_args;
466 
467  friend class basic_args<Context>;
468 
469  static constexpr size_t get_types()
470  {
471  return is_packed ? detail::get_types<Context, Args...>()
472  : detail::is_unpacked_bit | num_args;
473  }
474 
475  public:
476  static constexpr size_t types = get_types();
477  using arg_type = typename Context::arg_type;
478 
479  using value_type = typename std::
480  conditional<is_packed, detail::value<Context>, arg_type>::type;
481  static constexpr size_t data_size =
482  num_args + (is_packed && num_args != 0 ? 0 : 1);
483 
484  template <typename ParseCtx>
486  Args&... a) noexcept
487  : m_data{{detail::make_arg<is_packed, Context, ParseCtx>(a)...}}
488  {
489  }
490 
491  SCN_CONSTEXPR14 span<value_type> data() noexcept
492  {
493  return make_span(m_data.data(),
494  static_cast<std::ptrdiff_t>(m_data.size()));
495  }
496 
497  private:
499  };
500 
501  template <typename Context, typename ParseCtx, typename... Args>
502  typename Context::template arg_store_type<Args...> make_args(Args&... args)
503  {
504  return {detail::parse_ctx_tag<ParseCtx>(), args...};
505  }
506 
507  template <typename Context>
508  class basic_args {
509  public:
510  using arg_type = typename Context::arg_type;
511 
512  constexpr basic_args() noexcept = default;
513 
514  template <typename... Args>
515  SCN_CONSTEXPR14 basic_args(arg_store<Context, Args...>& store) noexcept
516  : m_types(store.types)
517  {
518  set_data(store.m_data.data());
519  }
520 
522  : m_types(detail::is_unpacked_bit | args.size())
523  {
524  set_data(args.data());
525  }
526 
527  SCN_CONSTEXPR14 arg_type get(std::ptrdiff_t i) const noexcept
528  {
529  return do_get(i);
530  }
531 
532  SCN_CONSTEXPR14 bool check_id(std::ptrdiff_t i) const noexcept
533  {
534  if (!is_packed()) {
535  return static_cast<size_t>(i) <
536  (m_types &
537  ~static_cast<size_t>(detail::is_unpacked_bit));
538  }
539  return type(i) != detail::none_type;
540  }
541 
542  constexpr size_t max_size() const noexcept
543  {
544  return is_packed()
545  ? static_cast<size_t>(detail::max_packed_args)
546  : m_types &
547  ~static_cast<size_t>(detail::is_unpacked_bit);
548  }
549 
550  private:
551  size_t m_types{0};
552  union {
555  };
556 
557  constexpr bool is_packed() const noexcept
558  {
559  return (m_types & detail::is_unpacked_bit) == 0;
560  }
561 
562  SCN_CONSTEXPR14 typename detail::type type(std::ptrdiff_t i) const
563  noexcept
564  {
565  size_t shift = static_cast<size_t>(i) * detail::packed_arg_bitsize;
566  return static_cast<typename detail::type>((m_types >> shift) &
568  }
569 
570  SCN_CONSTEXPR14 void set_data(detail::value<Context>* values) noexcept
571  {
572  m_values = values;
573  }
574  SCN_CONSTEXPR14 void set_data(arg_type* args) noexcept
575  {
576  m_args = args;
577  }
578 
579  SCN_CONSTEXPR14 arg_type do_get(std::ptrdiff_t i) const noexcept
580  {
581  SCN_EXPECT(i >= 0);
582 
583  arg_type arg;
584  if (!is_packed()) {
585  auto num_args = static_cast<std::ptrdiff_t>(max_size());
586  if (SCN_LIKELY(i < num_args)) {
587  arg = m_args[i];
588  }
589  return arg;
590  }
591 
592  SCN_EXPECT(m_values);
594  return arg;
595  }
596 
597  arg.m_type = type(i);
598  if (arg.m_type == detail::none_type) {
599  return arg;
600  }
601  arg.m_value = m_values[i];
602  return arg;
603  }
604  };
605 
607 } // namespace scn
608 
609 #endif // SCN_DETAIL_ARGS_H
SCN_CONSTEXPR14 custom_value & get_custom() noexcept
Definition: args.h:166
monostate m_empty
Definition: args.h:177
constexpr init(T &v)
Definition: args.h:188
Context::template arg_store_type< Args...> make_args(Args &...args)
Definition: args.h:502
SCN_CONSTEXPR14 basic_args(span< arg_type > args) noexcept
Definition: args.h:521
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:421
constexpr bool is_integral(type t) noexcept
Definition: args.h:96
SCN_CONSTEXPR14 T & get_as() noexcept
Definition: args.h:156
constexpr size_t max_size() const noexcept
Definition: args.h:542
#define SCN_END_NAMESPACE
Definition: config.h:406
typename Context::arg_type arg_type
Definition: args.h:136
#define SCN_UNLIKELY(x)
Definition: config.h:367
constexpr detail::type type() const noexcept
Definition: args.h:317
custom_value m_custom
Definition: args.h:179
error scan_custom_arg(void *arg, Context &ctx, ParseCtx &pctx) noexcept
Definition: args.h:113
typename Context::char_type char_type
Definition: args.h:290
typename std::conditional< is_packed, detail::value< Context >, arg_type >::type value_type
Definition: args.h:480
constexpr value() noexcept
Definition: args.h:138
temporary< T > temp(T &&val)
Factory function for temporary.
Definition: args.h:61
#define SCN_UNREACHABLE
Definition: config.h:352
#define SCN_CLANG_IGNORE(x)
Definition: config.h:150
arg_type * m_args
Definition: args.h:554
typename Context::arg_type arg_type
Definition: args.h:477
SCN_CONSTEXPR14 value(T &val) noexcept
Definition: args.h:141
#define SCN_CLANG_POP
Definition: config.h:149
handle(detail::custom_value custom)
Definition: args.h:294
temporary(T &&val)
Definition: args.h:46
typename Context::arg_type arg_type
Definition: args.h:510
#define SCN_TRIVIAL_ABI
Definition: config.h:277
detail::value< Context > * m_values
Definition: args.h:553
Allows reading an rvalue.
Definition: args.h:45
typename Context::char_type char_type
Definition: args.h:135
SCN_CONSTEXPR14 Context::arg_type make_arg(T &value) noexcept
Definition: args.h:438
Error class.
Definition: result.h:32
#define SCN_BEGIN_NAMESPACE
Definition: config.h:405
constexpr bool is_arithmetic() const noexcept
Definition: args.h:325
constexpr size_t get_types()
Definition: args.h:426
constexpr const custom_value & get_custom() const noexcept
Definition: args.h:170
SCN_CLANG_POP SCN_CONSTEXPR14 error visit_arg(Visitor &&vis, typename Context::arg_type &arg)
Definition: args.h:353
error scan(Context &ctx, ParseCtx &pctx)
Definition: args.h:300
constexpr const T & get_as() const noexcept
Definition: args.h:161
SCN_CONSTEXPR14 arg_store(detail::parse_ctx_tag< ParseCtx >, Args &...a) noexcept
Definition: args.h:485
T & operator()()&&noexcept
Definition: args.h:48
constexpr span< T > make_span(T *ptr, std::size_t count) noexcept
Definition: span.h:383
void * m_value
Definition: args.h:178
constexpr bool is_arithmetic(type t) noexcept
Definition: args.h:100
#define SCN_LIKELY(x)
Definition: config.h:366
Type-erased scanning argument.
Definition: args.h:30
char_type make_value(typename C::char_type &val, priority_tag< 1 >) noexcept
Definition: args.h:243
constexpr bool is_integral() const noexcept
Definition: args.h:321
SCN_MAKE_VALUE(string_view_type, basic_string_view< typename C::char_type >) template< typename C > const expr init<C
value(parse_ctx_tag< ParseCtx >, T &val) noexcept
Definition: args.h:147
SCN_CONSTEXPR14 bool check_id(std::ptrdiff_t i) const noexcept
Definition: args.h:532
#define SCN_EXPECT(cond)
Definition: config.h:401
#define SCN_CLANG_PUSH
Definition: config.h:148
A view over a contiguous range.
Definition: span.h:29
#define SCN_CONSTEXPR14
Definition: config.h:246