scnlib  0.1.2
FormattedinputformodernC++
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups
visitor.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_VISITOR_H
19 #define SCN_DETAIL_VISITOR_H
20 
21 #include "context.h"
22 #include "stream.h"
23 
24 #include <string>
25 
26 namespace scn {
28 
29  enum class scan_status { keep, skip, end };
30 
31  namespace predicates {
32  template <typename Context>
33  using locale_ref = typename Context::locale_type&;
34 
35  template <typename Predicate>
36  struct predicate_skips : Predicate::does_skip {
37  };
38 
39  template <typename CharT>
40  struct propagate {
41  using does_skip = std::false_type;
43  {
44  return scan_status::keep;
45  }
46  };
47  template <typename CharT>
48  struct until {
49  using does_skip = std::false_type;
51  noexcept
52  {
54  }
55 
56  CharT until_ch;
57  };
58  template <typename CharT>
59  struct until_one_of {
60  using does_skip = std::false_type;
62  {
63  if (detail::find(until.begin(), until.end(), ch) !=
64  until.end()) {
65  return scan_status::end;
66  }
67  return scan_status::keep;
68  }
69 
71  };
72  template <typename CharT, typename Locale>
73  struct until_space {
74  using does_skip = std::false_type;
76  {
77  if (locale.is_space(ch)) {
78  return scan_status::end;
79  }
80  return scan_status::keep;
81  }
82 
83  Locale& locale;
84  };
85 
86  template <typename CharT>
88  using does_skip = std::true_type;
90  {
91  if (ch == until) {
92  return scan_status::end;
93  }
94  if (std::find(skip.begin(), skip.end(), ch) != skip.end()) {
95  return scan_status::skip;
96  }
97  return scan_status::keep;
98  }
99 
100  CharT until;
102  };
103  template <typename CharT>
105  using does_skip = std::true_type;
107  {
108  if (detail::find(until.begin(), until.end(), ch) !=
109  until.end()) {
110  return scan_status::end;
111  }
112  if (detail::find(skip.begin(), skip.end(), ch) != skip.end()) {
113  return scan_status::skip;
114  }
115  return scan_status::keep;
116  }
117 
120  };
121  template <typename CharT, typename Locale>
123  using does_skip = std::true_type;
125  {
126  if (locale.is_space(ch)) {
127  return scan_status::end;
128  }
129  if (detail::find(skip.begin(), skip.end(), ch) != skip.end()) {
130  return scan_status::skip;
131  }
132  return scan_status::keep;
133  }
134 
135  Locale& locale;
137  };
138  } // namespace predicates
139 
140  namespace pred = predicates;
141 
142  // read
143 
144  template <typename Stream,
145  typename CharT = typename Stream::char_type,
146  typename Span = span<CharT>,
147  typename std::enable_if<is_sized_stream<Stream>::value>::type* =
148  nullptr>
149  result<typename Span::iterator> read(Stream& stream, Span s)
150  {
151  auto n = detail::min(s.size(), stream.chars_to_read());
152  s = s.first(n);
153  stream.read_sized(s);
154  return {s.end()};
155  }
156  template <typename Stream,
157  typename CharT = typename Stream::char_type,
158  typename Span = span<CharT>,
159  typename std::enable_if<!is_sized_stream<Stream>::value>::type* =
160  nullptr>
161  result<typename Span::iterator> read(Stream& stream, Span s)
162  {
163  auto it = s.begin();
164  for (; it != s.end(); ++it) {
165  auto ret = stream.read_char();
166  if (!ret) {
167  if (ret.error() == error::end_of_stream) {
168  return {it};
169  }
170  return {it, ret.error()};
171  }
172  *it = ret.value();
173  }
174  return {it};
175  }
176 
177  // read_into_if
178 
179  template <typename Stream,
180  typename Iterator,
181  typename Predicate,
182  typename CharT = typename Stream::char_type,
183  typename std::enable_if<is_sized_stream<Stream>::value>::type* =
184  nullptr>
186  Iterator it,
187  Predicate&& p,
188  bool keep_final = false)
189  {
190  size_t n = 0, size = 0;
192  bool end = false;
193  while (!end) {
194  n = detail::min(s.chars_to_read(), std::size_t{64});
195  if (n == 0) {
196  break;
197  }
198 
199  auto ret = read(s, make_span(arr.data(), n));
200  if (!ret) {
201  return {size, ret.error()};
202  }
203 
204  const auto arr_end = arr.begin() + n;
205 
207  for (auto arr_it = arr.begin(); arr_it != arr_end; ++arr_it) {
208  auto r = p(*arr_it);
209  if (!r) {
210  return {size, r.error()};
211  }
212  if (r.value() == scan_status::skip) {
213  continue;
214  }
215  if (r.value() == scan_status::end) {
216  if (keep_final) {
217  ++arr_it;
218  }
219  if (arr_it != arr_end) {
220  s.putback_n(static_cast<size_t>(
221  std::distance(arr_it, arr_end)));
222  }
223  end = true;
224  break;
225  }
226  *it = *arr_it;
227  ++it;
228  ++size;
229  }
230  }
231  else {
232  auto arr_it = arr.begin();
233  for (; arr_it != arr_end; ++arr_it) {
234  auto r = p(*arr_it);
235  if (!r) {
236  return {size, r.error()};
237  }
238  if (r.value() == scan_status::end) {
239  if (keep_final) {
240  ++arr_it;
241  }
242  if (arr_it != arr_end) {
243  s.putback_n(static_cast<size_t>(
244  std::distance(arr_it, arr_end)));
245  }
246  if (keep_final) {
247  --arr_it;
248  }
249  end = true;
250  break;
251  }
252  }
253  const auto chars =
254  static_cast<size_t>(std::distance(arr.begin(), arr_it));
255  size += chars;
256  std::copy(arr.begin(), arr_it, it);
257  }
258  }
259  return {size};
260  }
261  template <typename Stream,
262  typename Iterator,
263  typename Predicate,
264  typename CharT = typename Stream::char_type,
265  typename std::enable_if<!is_sized_stream<Stream>::value>::type* =
266  nullptr>
267  result<size_t> read_into_if(Stream& s,
268  Iterator it,
269  Predicate&& p,
270  bool keep_final = false)
271  {
272  size_t n = 0;
273  while (true) {
274  auto ret = s.read_char();
275  if (!ret) {
276  if (ret.error() == error::end_of_stream) {
277  break;
278  }
279  return {n, ret.error()};
280  }
281 
282  auto r = p(ret.value());
283  if (!r) {
284  return {n, r.error()};
285  }
286  if (r.value() == scan_status::skip) {
287  continue;
288  }
289  if (r.value() == scan_status::end) {
290  if (keep_final) {
291  *it = ret.value();
292  ++it;
293  }
294  break;
295  }
296  *it = ret.value();
297  ++it;
298  ++n;
299  }
300  return {n};
301  }
302 
303  template <typename Stream,
304  typename Iterator,
305  typename Sentinel,
306  typename Predicate,
307  typename CharT = typename Stream::char_type,
308  typename std::enable_if<is_sized_stream<Stream>::value>::type* =
309  nullptr>
311  Iterator it,
312  Sentinel end,
313  Predicate&& p,
314  bool keep_final = false)
315  {
316  size_t n = 0;
318  bool done = false;
319  while (!done) {
320  n = detail::min({static_cast<size_t>(std::distance(it, end)),
321  s.chars_to_read(), size_t{64}});
322  if (n == 0) {
323  break;
324  }
325 
326  auto ret = read(s, make_span(arr.data(), n));
327  if (!ret) {
328  return {it, ret.error()};
329  }
330 
331  auto arr_it = arr.begin();
332  const auto arr_end = arr.begin() + n;
333  for (; arr_it != arr_end; ++arr_it) {
334  auto r = p(*arr_it);
335  if (!r) {
336  return {it, r.error()};
337  }
338  if (r.value() == scan_status::skip) {
339  continue;
340  }
341  if (r.value() == scan_status::end) {
342  if (keep_final) {
343  ++arr_it;
344  }
345  if (arr_it != arr_end) {
346  s.putback_n(static_cast<size_t>(
347  std::distance(arr_it, arr_end)));
348  }
349  done = true;
350  break;
351  }
352  *it = *arr_it;
353  ++it;
354  }
355  }
356  return {it};
357  }
358  template <typename Stream,
359  typename Iterator,
360  typename Sentinel,
361  typename Predicate,
362  typename CharT = typename Stream::char_type,
363  typename std::enable_if<!is_sized_stream<Stream>::value>::type* =
364  nullptr>
365  result<Iterator> read_into_if(Stream& s,
366  Iterator it,
367  Sentinel end,
368  Predicate&& p,
369  bool keep_final = false)
370  {
371  while (it != end) {
372  auto ret = s.read_char();
373  if (!ret) {
374  if (ret.error() == error::end_of_stream) {
375  break;
376  }
377  return {it, ret.error()};
378  }
379 
380  auto r = p(ret.value());
381  if (!r) {
382  return {it, r.error()};
383  }
384  if (r.value() == scan_status::skip) {
385  continue;
386  }
387  if (r.value() == scan_status::end) {
388  if (keep_final) {
389  *it = ret.value();
390  ++it;
391  }
392  break;
393  }
394  *it = ret.value();
395  ++it;
396  }
397  return {it};
398  }
399 
400  // read_into_until_space
401 
402  namespace detail {
403  template <typename Stream,
404  typename Locale,
405  typename Iterator,
406  typename = void>
408  using char_type = typename Stream::char_type;
409  static result<size_t> f(Stream& s,
410  Locale& l,
411  Iterator it,
412  bool keep_final)
413  {
414  return read_into_if(
415  s, it, pred::until_space<char_type, Locale>{l}, keep_final);
416  }
417  };
418 
419  template <typename Stream, typename Iterator>
421  Stream,
423  Iterator,
424  typename std::enable_if<
425  std::is_same<typename Stream::char_type, char>::value &&
426  is_sized_stream<Stream>::value>::type> {
427  using char_type = char;
429 
430  static result<size_t> f(Stream& s,
431  locale_type& loc,
432  Iterator it,
433  bool keep_final)
434  {
435  size_t i = 0;
436  char ch{};
437  for (; s.chars_to_read() != 0; ++i) {
438  s.read_sized(make_span(&ch, 1));
439  if (loc.is_space(ch)) {
440  if (keep_final) {
441  ++i;
442  *it = ch;
443  ++it;
444  }
445  else {
446  s.putback_n(1);
447  }
448  break;
449  }
450  *it = ch;
451  ++it;
452  }
453  return {i};
454  }
455  };
456  } // namespace detail
457 
458  template <typename Stream, typename Locale, typename Iterator>
460  Locale& l,
461  Iterator it,
462  bool keep_final = false)
463  {
465  s, l, it, keep_final);
466  }
467 
468  // putback_range
469 
470  template <typename Stream,
471  typename Iterator,
472  typename std::enable_if<!is_sized_stream<Stream>::value>::type* =
473  nullptr>
474  error putback_range(Stream& s, Iterator begin, Iterator end)
475  {
476  for (; begin != end; ++begin) {
477  auto ret = s.putback(*begin);
478  if (!ret) {
479  return ret;
480  }
481  }
482  return {};
483  }
484  template <typename Stream,
485  typename Iterator,
486  typename std::enable_if<is_sized_stream<Stream>::value>::type* =
487  nullptr>
488  error putback_range(Stream& s, Iterator begin, Iterator end)
489  {
490  s.putback_n(static_cast<size_t>(std::distance(begin, end)));
491  return {};
492  }
493 
494  template <typename CharT>
495  struct empty_parser {
496  template <typename Context>
497  error parse(Context& ctx)
498  {
499  auto& pctx = ctx.parse_context();
500  pctx.arg_begin();
501  if (SCN_UNLIKELY(!pctx)) {
503  "Unexpected format string end");
504  }
505  if (!pctx.check_arg_end(ctx.locale())) {
507  "Expected argument end");
508  }
509  pctx.arg_end();
510  return {};
511  }
512  };
513 
514  namespace detail {
515  template <typename CharT>
516  struct char_scanner {
517  template <typename Context>
518  error parse(Context& ctx)
519  {
520  auto& pctx = ctx.parse_context();
521  pctx.arg_begin();
522  if (SCN_UNLIKELY(!pctx)) {
524  "Unexpected format string end");
525  }
526  if (pctx.next() == detail::ascii_widen<CharT>('c')) {
527  pctx.advance();
528  }
529  if (!pctx.check_arg_end(ctx.locale())) {
531  "Expected argument end");
532  }
533  pctx.arg_end();
534  return {};
535  }
536 
537  template <typename Context>
538  error scan(CharT& val, Context& ctx)
539  {
540  auto ch = ctx.stream().read_char();
541  if (!ch) {
542  return ch.error();
543  }
544  val = ch.value();
545  return {};
546  }
547  };
548  template <typename CharT>
549  struct buffer_scanner : public empty_parser<CharT> {
550  template <typename Context>
551  error scan(span<CharT>& val, Context& ctx)
552  {
553  if (val.size() == 0) {
554  return {};
555  }
556 
558  static_cast<size_t>(val.size()));
559  auto span = scn::make_span(buf);
560  auto s = read(ctx.stream(), span);
561  if (!s) {
562  return s.error();
563  }
564  std::memcpy(val.begin(), buf.begin(), buf.size());
565 
566  return {};
567  }
568  };
569 
570  template <typename CharT>
571  struct bool_scanner {
572  template <typename Context>
573  error parse(Context& ctx)
574  {
575  // {}: no boolalpha, not localized
576  // l: localized
577  // a: boolalpha
578  auto& pctx = ctx.parse_context();
579  pctx.arg_begin();
580  if (SCN_UNLIKELY(!pctx)) {
582  "Unexpected format string end");
583  }
584 
585  bool a = false, n = false;
586  for (auto ch = pctx.next();
587  pctx && !pctx.check_arg_end(ctx.locale());
588  pctx.advance(), ch = pctx.next()) {
589  if (ch == detail::ascii_widen<CharT>('l')) {
590  localized = true;
591  }
592  else if (ch == detail::ascii_widen<CharT>('a')) {
593  a = true;
594  }
595  else if (ch == detail::ascii_widen<CharT>('n')) {
596  n = true;
597  }
598  else {
599  break;
600  }
601  }
602  if (a || n) {
603  allow_alpha = a;
604  allow_num = n;
605  }
606 
609  "boolalpha-mode cannot be disabled with 'l' "
610  "(localized) specifier with bool");
611  }
612 
613  if (pctx.next() == detail::ascii_widen<CharT>('b')) {
614  pctx.advance();
615  }
616  if (!pctx.check_arg_end(ctx.locale())) {
618  "Expected argument end");
619  }
620  pctx.arg_end();
621  return {};
622  }
623 
624  template <typename Context>
625  error scan(bool& val, Context& ctx)
626  {
627  if (allow_alpha) {
629  auto falsename =
631  if (localized) {
632  truename = ctx.locale().truename();
633  falsename = ctx.locale().falsename();
634  }
635  const auto max_len =
636  detail::max(truename.size(), falsename.size());
637  std::basic_string<CharT> buf;
638  buf.reserve(max_len);
639 
640  auto i = read_into_until_space(ctx.stream(), ctx.locale(),
641  std::back_inserter(buf));
642 #if 0
643  auto i = read_into_if(
644  ctx.stream(), std::back_inserter(buf),
646  typename Context::locale_type>{
647  ctx.locale()});
648 #endif
649  if (!i) {
650  return i.error();
651  }
652  buf.erase(i.value());
653 
654  bool found = false;
655  size_t chars = 0;
656 
657  if (buf.size() >= falsename.size()) {
658  if (std::equal(falsename.begin(), falsename.end(),
659  buf.begin())) {
660  val = false;
661  found = true;
662  chars = falsename.size();
663  }
664  }
665  if (!found && buf.size() >= truename.size()) {
666  if (std::equal(truename.begin(), truename.end(),
667  buf.begin())) {
668  val = true;
669  found = true;
670  chars = truename.size();
671  }
672  }
673  if (found) {
674  return putback_range(
675  ctx.stream(), buf.rbegin(),
676  buf.rend() - static_cast<std::ptrdiff_t>(chars));
677  }
678  else {
679  auto pb = putback_range(ctx.stream(), buf.rbegin(),
680  buf.rend());
681  if (!pb) {
682  return pb;
683  }
684  }
685  }
686 
687  if (allow_num) {
688  auto tmp = ctx.stream().read_char();
689  if (!tmp) {
690  return tmp.error();
691  }
692  if (tmp.value() == detail::ascii_widen<CharT>('0')) {
693  val = false;
694  return {};
695  }
696  if (tmp.value() == detail::ascii_widen<CharT>('1')) {
697  val = true;
698  return {};
699  }
700  auto pb = ctx.stream().putback(tmp.value());
701  if (!pb) {
702  return pb;
703  }
704  }
705 
707  "Couldn't scan bool");
708  }
709 
710  bool localized{false};
711  bool allow_alpha{true};
712  bool allow_num{true};
713  };
714 
715  namespace sto {
716  template <typename CharT, typename T>
717  struct str_to_int;
718  } // namespace sto
719 
720  namespace strto {
721  template <typename CharT, typename T>
722  struct str_to_int;
723  } // namespace strto
724 
725  template <typename CharT, typename T>
727 
729  SCN_CLANG_IGNORE("-Wpadded")
730 
731  template <typename CharT, typename T>
733  public:
734  template <typename Context>
735  error parse(Context& ctx)
736  {
737  // {}: base detect, not localized
738  // n: localized decimal/thousand separator
739  // l: n + localized digits
740  // d: decimal, o: octal, x: hex, b[1-36]: base
741  auto& pctx = ctx.parse_context();
742  pctx.arg_begin();
743  if (SCN_UNLIKELY(!pctx)) {
745  "Unexpected format string end");
746  }
747 
748  bool base_set = false;
749  bool loc_set = false;
750  for (auto ch = pctx.next();
751  pctx && !pctx.check_arg_end(ctx.locale());
752  pctx.advance(), ch = pctx.next()) {
753  if (!base_set) {
754  if (ch == detail::ascii_widen<CharT>('d')) {
755  base = 10;
756  base_set = true;
757  continue;
758  }
759  else if (ch == detail::ascii_widen<CharT>('x')) {
760  base = 16;
761  base_set = true;
762  continue;
763  }
764  else if (ch == detail::ascii_widen<CharT>('o')) {
765  base = 8;
766  base_set = true;
767  continue;
768  }
769  else if (ch == detail::ascii_widen<CharT>('i')) {
770  if (std::is_unsigned<T>::value) {
771  return error(
773  "'i' format specifier expects signed "
774  "integer argument");
775  }
776  base = 0;
777  base_set = true;
778  continue;
779  }
780  else if (ch == detail::ascii_widen<CharT>('u')) {
781  if (std::is_signed<T>::value) {
782  return error(
784  "'u' format specifier expects unsigned "
785  "integer argument");
786  }
787  base = 0;
788  base_set = true;
789  continue;
790  }
791  else if (ch == detail::ascii_widen<CharT>('b')) {
792  pctx.advance();
793  if (SCN_UNLIKELY(!pctx)) {
795  "Unexpected format string end");
796  }
797  if (SCN_UNLIKELY(
798  pctx.check_arg_end(ctx.locale()))) {
800  "Unexpected argument end");
801  }
802  ch = pctx.next();
803 
804  const auto zero = detail::ascii_widen<CharT>('0'),
805  nine = detail::ascii_widen<CharT>('9');
806  int tmp = 0;
807  if (ch < zero || ch > nine) {
809  "Invalid character after 'b', "
810  "expected digit");
811  }
812  tmp = pctx.next() - zero;
813  if (tmp < 1) {
814  return error(
816  "Invalid base, must be between 1 and 36");
817  }
818 
819  pctx.advance();
820  ch = pctx.next();
821 
822  if (pctx.check_arg_end(ctx.locale())) {
823  base = tmp;
824  break;
825  }
826  if (ch < zero || ch > nine) {
828  "Invalid character after 'b', "
829  "expected digit");
830  }
831  tmp *= 10;
832  tmp += ch - zero;
833  if (tmp < 1 || tmp > 36) {
834  return error(
836  "Invalid base, must be between 1 and 36");
837  }
838  base = tmp;
839  base_set = true;
840  continue;
841  }
842  }
843 
844  if (!loc_set) {
845  if (ch == detail::ascii_widen<CharT>('l')) {
846  localized = thousands_separator | digits;
847  loc_set = true;
848  continue;
849  }
850  else if (ch == detail::ascii_widen<CharT>('n')) {
851  localized = thousands_separator;
852  loc_set = true;
853  continue;
854  }
855  }
856 
857  if (!have_thsep) {
858  if (ch == detail::ascii_widen<CharT>('\'')) {
859  have_thsep = true;
860  continue;
861  }
862  }
863 
865  "Unexpected character in format string");
866  }
867 
868  if (localized && (base != 0 && base != 10)) {
869  return error(
871  "Localized integers can only be scanned in base 10");
872  }
873  if (have_thsep && ctx.int_method() != method::custom) {
875  "Thousand separator scanning is only "
876  "supported with custom parsing method");
877  }
878  if (!pctx.check_arg_end(ctx.locale())) {
880  "Expected argument end");
881  }
882  pctx.arg_end();
883  return {};
884  }
885 
886  template <typename Context>
887  error scan(T& val, Context& ctx)
888  {
890 
891  auto r = read_into_until_space(ctx.stream(), ctx.locale(),
892  std::back_inserter(buf));
893  if (!r) {
894  return r.error();
895  }
896  buf.erase(buf.begin() + r.value(), buf.end());
897 
898  T tmp = 0;
899  size_t chars = 0;
900 
901  if ((localized & digits) != 0) {
903  std::basic_string<CharT> str(buf.data(), buf.size());
904  auto ret = ctx.locale().read_num(tmp, str);
906 
907  if (!ret) {
908  return ret.error();
909  }
910 
911  auto pb = putback_range(
912  ctx.stream(), buf.rbegin(),
913  buf.rend() - static_cast<std::ptrdiff_t>(ret.value()));
914  if (!pb) {
915  return pb;
916  }
917  val = tmp;
918  return {};
919  }
920 
922  SCN_MSVC_IGNORE(4244)
923  SCN_MSVC_IGNORE(4127) // conditional expression is constant
924 
925  if (std::is_unsigned<T>::value) {
926  if (buf.front() == detail::ascii_widen<CharT>('-')) {
928  "Unexpected sign '-' when scanning an "
929  "unsigned integer");
930  }
931  }
932 
934 
935  auto do_read = [&]() {
937  SCN_GCC_IGNORE("-Wswitch-default")
939  switch (ctx.int_method()) {
940  case method::sto:
941  return &_read_sto;
942  case method::from_chars:
943  return &_read_from_chars;
944  case method::strto:
945  return &_read_strto;
946  case method::custom:
947  return &_read_custom;
948  }
952  };
953  auto e = do_read()(
954  tmp, make_span(buf).as_const(), base,
955  have_thsep ? ctx.locale().thousands_separator() : 0);
956  if (!e) {
957  return e.error();
958  }
959  chars = e.value();
960 
961  auto pb = putback_range(
962  ctx.stream(), buf.rbegin(),
963  buf.rend() - static_cast<std::ptrdiff_t>(chars));
964  if (!pb) {
965  return pb;
966  }
967  val = tmp;
968 
969  return {};
970  }
971 
972  enum localized_type : uint8_t {
973  thousands_separator = 1,
974  digits = 2
975  };
976 
977  int base{0};
978  uint8_t localized{0};
979  bool have_thsep{false};
980 
981  private:
982  static expected<size_t> _read_sto(T& val,
983  span<const CharT> buf,
984  int base,
985  CharT thsep);
986  static expected<size_t> _read_strto(T& val,
987  span<const CharT> buf,
988  int base,
989  CharT thsep);
990  static expected<size_t> _read_from_chars(T& val,
991  span<const CharT> buf,
992  int base,
993  CharT thsep);
994  static expected<size_t> _read_custom(T& val,
995  span<const CharT> buf,
996  int base,
997  CharT thsep);
998 
999  friend struct float_scanner<CharT, float>;
1000  friend struct float_scanner<CharT, double>;
1001  friend struct float_scanner<CharT, long double>;
1002  };
1003 
1005 
1006  namespace sto {
1007  template <typename CharT, typename T>
1009  } // namespace sto
1010 
1011  namespace strto {
1012  template <typename CharT, typename T>
1014  } // namespace strto
1015 
1016  template <typename CharT, typename T>
1017  struct float_scanner {
1018  template <typename Context>
1019  error parse(Context& ctx)
1020  {
1021  // {}: not localized
1022  // l: localized
1023  auto& pctx = ctx.parse_context();
1024  pctx.arg_begin();
1025  if (SCN_UNLIKELY(!pctx)) {
1027  "Unexpected format string end");
1028  }
1029 
1030  if (pctx.check_arg_end(ctx.locale())) {
1031  pctx.arg_end();
1032  return {};
1033  }
1034 
1035  if (pctx.next() == detail::ascii_widen<CharT>('l')) {
1036  localized = true;
1037  pctx.advance();
1038  }
1039 
1040  if (pctx.check_arg_end(ctx.locale())) {
1041  pctx.arg_end();
1042  return {};
1043  }
1044 
1045  if (pctx.next() == detail::ascii_widen<CharT>('a')) {
1046  pctx.advance();
1047  }
1048  else if (pctx.next() == detail::ascii_widen<CharT>('A')) {
1049  pctx.advance();
1050  }
1051  else if (pctx.next() == detail::ascii_widen<CharT>('e')) {
1052  pctx.advance();
1053  }
1054  else if (pctx.next() == detail::ascii_widen<CharT>('E')) {
1055  pctx.advance();
1056  }
1057  else if (pctx.next() == detail::ascii_widen<CharT>('f')) {
1058  pctx.advance();
1059  }
1060  else if (pctx.next() == detail::ascii_widen<CharT>('F')) {
1061  pctx.advance();
1062  }
1063  else if (pctx.next() == detail::ascii_widen<CharT>('g')) {
1064  pctx.advance();
1065  }
1066  else if (pctx.next() == detail::ascii_widen<CharT>('G')) {
1067  pctx.advance();
1068  }
1069 
1070  if (!pctx.check_arg_end(ctx.locale())) {
1072  "Expected argument end");
1073  }
1074  pctx.arg_end();
1075  return {};
1076  }
1077  template <typename Context>
1078  error scan(T& val, Context& ctx)
1079  {
1081 
1082  auto r = read_into_until_space(ctx.stream(), ctx.locale(),
1083  std::back_inserter(buf));
1084  if (!r) {
1085  return r.error();
1086  }
1087  buf.erase(buf.begin() + r.value(), buf.end());
1088 
1089  T tmp{};
1090  size_t chars = 0;
1091 
1092  if (localized) {
1094  auto str = std::basic_string<CharT>(buf.data(), buf.size());
1095  auto ret = ctx.locale().read_num(tmp, str);
1097 
1098  if (!ret) {
1099  return ret.error();
1100  }
1101  chars = ret.value();
1102 
1103  auto pb = putback_range(
1104  ctx.stream(), buf.rbegin(),
1105  buf.rend() - static_cast<std::ptrdiff_t>(chars));
1106  if (!pb) {
1107  return pb;
1108  }
1109  val = tmp;
1110  return {};
1111  }
1112 
1113  auto do_read = [&]() {
1114  SCN_GCC_PUSH
1115  SCN_GCC_IGNORE("-Wswitch-default")
1117  switch (ctx.float_method()) {
1118  case method::sto:
1119  return &_read_sto;
1120  case method::from_chars:
1121  return &_read_from_chars;
1122  case method::strto:
1123  return &_read_strto;
1124  case method::custom:
1125  return &_read_custom;
1126  }
1129  SCN_GCC_POP
1130  };
1131  auto e = do_read()(tmp, make_span(buf).as_const());
1132  if (!e) {
1133  return e.error();
1134  }
1135  chars = e.value();
1136 
1137  auto pb = putback_range(
1138  ctx.stream(), buf.rbegin(),
1139  buf.rend() - static_cast<std::ptrdiff_t>(chars));
1140  if (!pb) {
1141  return pb;
1142  }
1143  val = tmp;
1144 
1145  return {};
1146  }
1147 
1148  bool localized{false};
1149 
1150  private:
1151  static expected<size_t> _read_sto(T& val, span<const CharT> buf);
1152  static expected<size_t> _read_strto(T& val, span<const CharT> buf);
1153  static expected<size_t> _read_from_chars(T& val,
1154  span<const CharT> buf);
1155  static expected<size_t> _read_custom(T& val, span<const CharT> buf);
1156  };
1157 
1158  template <typename CharT>
1160  public:
1161  template <typename Context>
1162  error parse(Context& ctx)
1163  {
1164  auto& pctx = ctx.parse_context();
1165  pctx.arg_begin();
1166  if (SCN_UNLIKELY(!pctx)) {
1168  "Unexpected format string end");
1169  }
1170  if (pctx.next() == detail::ascii_widen<CharT>('s')) {
1171  pctx.advance();
1172  }
1173  if (!pctx.check_arg_end(ctx.locale())) {
1175  "Expected argument end");
1176  }
1177  pctx.arg_end();
1178  return {};
1179  }
1180 
1181  template <typename Context>
1182  error scan(std::basic_string<CharT>& val, Context& ctx)
1183  {
1184  std::basic_string<CharT> tmp;
1185  /* tmp.reserve(15); */
1186  auto s = read_into_until_space(ctx.stream(), ctx.locale(),
1187  std::back_inserter(tmp));
1188 #if 0
1189  auto s = read_into_if(
1190  ctx.stream(), std::back_inserter(tmp),
1192  typename Context::locale_type>{
1193  ctx.locale()});
1194 #endif
1195  if (SCN_UNLIKELY(!s)) {
1196  return s.error();
1197  }
1198  tmp.erase(s.value());
1199  if (SCN_UNLIKELY(tmp.empty())) {
1201  "Empty string parsed");
1202  }
1203  val = std::move(tmp);
1204 
1205  return {};
1206  }
1207  };
1208  } // namespace detail
1209 
1211  SCN_CLANG_IGNORE("-Wpadded")
1212  template <typename CharT>
1213  struct scanner<CharT, CharT> : public detail::char_scanner<CharT> {
1214  };
1215  template <typename CharT>
1216  struct scanner<CharT, span<CharT>> : public detail::buffer_scanner<CharT> {
1217  };
1218  template <typename CharT>
1219  struct scanner<CharT, bool> : public detail::bool_scanner<CharT> {
1220  };
1221  template <typename CharT>
1222  struct scanner<CharT, short>
1223  : public detail::integer_scanner<CharT, short> {
1224  };
1225  template <typename CharT>
1226  struct scanner<CharT, int> : public detail::integer_scanner<CharT, int> {
1227  };
1228  template <typename CharT>
1229  struct scanner<CharT, long> : public detail::integer_scanner<CharT, long> {
1230  };
1231  template <typename CharT>
1232  struct scanner<CharT, long long>
1233  : public detail::integer_scanner<CharT, long long> {
1234  };
1235  template <typename CharT>
1236  struct scanner<CharT, unsigned short>
1237  : public detail::integer_scanner<CharT, unsigned short> {
1238  };
1239  template <typename CharT>
1240  struct scanner<CharT, unsigned int>
1241  : public detail::integer_scanner<CharT, unsigned int> {
1242  };
1243  template <typename CharT>
1244  struct scanner<CharT, unsigned long>
1245  : public detail::integer_scanner<CharT, unsigned long> {
1246  };
1247  template <typename CharT>
1248  struct scanner<CharT, unsigned long long>
1249  : public detail::integer_scanner<CharT, unsigned long long> {
1250  };
1251  template <typename CharT>
1252  struct scanner<CharT, float> : public detail::float_scanner<CharT, float> {
1253  };
1254  template <typename CharT>
1255  struct scanner<CharT, double>
1256  : public detail::float_scanner<CharT, double> {
1257  };
1258  template <typename CharT>
1259  struct scanner<CharT, long double>
1260  : public detail::float_scanner<CharT, long double> {
1261  };
1262  template <typename CharT>
1263  struct scanner<CharT, std::basic_string<CharT>>
1264  : public detail::string_scanner<CharT> {
1265  };
1266  template <typename CharT>
1267  struct scanner<CharT, detail::monostate>;
1269 
1270  template <typename Context>
1271  error skip_stream_whitespace(Context& ctx) noexcept
1272  {
1273  while (true) {
1275 
1276  auto ch = ctx.stream().read_char();
1277  if (SCN_UNLIKELY(!ch)) {
1278  return ch.error();
1279  }
1280  if (!ctx.locale().is_space(ch.value())) {
1281  auto pb = ctx.stream().putback(ch.value());
1282  if (SCN_UNLIKELY(!pb)) {
1283  return pb;
1284  }
1285  break;
1286  }
1287 
1289  }
1290  return {};
1291  }
1292 #if 0
1293  template <typename Context,
1294  typename std::enable_if<is_sized_stream<
1295  typename Context::stream_type>::value>::type* = nullptr>
1296  error skip_stream_whitespace(Context& ctx) noexcept
1297  {
1298  using char_type = typename Context::char_type;
1299  detail::array<char_type, 8> buf;
1300  bool end = false;
1301  while (!end) {
1303 
1304  const auto n =
1305  detail::min(ctx.stream().chars_to_read(), buf.size());
1306  if (n == 0) {
1307  return error(error::end_of_stream, "EOF");
1308  }
1309  auto s = make_span(buf.data(), n);
1310  ctx.stream().read_sized(s);
1311 
1312  for (auto it = s.begin(); it != s.end(); ++it) {
1313  if (!ctx.locale().is_space(*it)) {
1314  ctx.stream().putback_n(
1315  static_cast<size_t>(std::distance(it, s.end())));
1316  end = true;
1317  break;
1318  }
1319  }
1320 
1322  }
1323  return {};
1324  }
1325 #endif
1326 
1327  template <typename Context>
1329  public:
1330  using context_type = Context;
1331  using char_type = typename Context::char_type;
1332 
1333  basic_visitor(Context& ctx) : m_ctx(std::addressof(ctx)) {}
1334 
1335  template <typename T>
1336  auto operator()(T&& val) -> error
1337  {
1338  return visit(std::forward<T>(val), detail::priority_tag<1>{});
1339  }
1340 
1341  private:
1342  auto visit(char_type& val, detail::priority_tag<1>) -> error
1343  {
1345  auto err = parse(s);
1346  if (!err) {
1347  return err;
1348  }
1349  return s.scan(val, *m_ctx);
1350  }
1351  auto visit(span<char_type>& val, detail::priority_tag<1>) -> error
1352  {
1353  detail::buffer_scanner<char_type> s;
1354  auto err = parse(s);
1355  if (!err) {
1356  return err;
1357  }
1358  return s.scan(val, *m_ctx);
1359  }
1360  auto visit(bool& val, detail::priority_tag<1>) -> error
1361  {
1362  detail::bool_scanner<char_type> s;
1363  auto err = parse(s);
1364  if (!err) {
1365  return err;
1366  }
1367  return s.scan(val, *m_ctx);
1368  }
1369 
1370 #define SCN_VISIT_INT(T) \
1371  error visit(T& val, detail::priority_tag<0>) \
1372  { \
1373  detail::integer_scanner<char_type, T> s; \
1374  auto err = parse(s); \
1375  if (!err) { \
1376  return err; \
1377  } \
1378  return s.scan(val, *m_ctx); \
1379  }
1380  SCN_VISIT_INT(short)
1381  SCN_VISIT_INT(int)
1382  SCN_VISIT_INT(long)
1383  SCN_VISIT_INT(long long)
1384  SCN_VISIT_INT(unsigned short)
1385  SCN_VISIT_INT(unsigned int)
1386  SCN_VISIT_INT(unsigned long)
1387  SCN_VISIT_INT(unsigned long long)
1388 #undef SCN_VISIT_INT
1389 
1390 #define SCN_VISIT_FLOAT(T) \
1391  error visit(T& val, detail::priority_tag<1>) \
1392  { \
1393  detail::float_scanner<char_type, T> s; \
1394  auto err = parse(s); \
1395  if (!err) { \
1396  return err; \
1397  } \
1398  return s.scan(val, *m_ctx); \
1399  }
1400  SCN_VISIT_FLOAT(float)
1401  SCN_VISIT_FLOAT(double)
1402  SCN_VISIT_FLOAT(long double)
1403 #undef SCN_VISIT_FLOAT
1404 
1405  auto visit(std::basic_string<char_type>& val, detail::priority_tag<1>)
1406  -> error
1407  {
1408  detail::string_scanner<char_type> s;
1409  auto err = parse(s);
1410  if (!err) {
1411  return err;
1412  }
1413  return s.scan(val, *m_ctx);
1414  }
1415  auto visit(typename Context::arg_type::handle val,
1416  detail::priority_tag<1>) -> error
1417  {
1418  return val.scan(*m_ctx);
1419  }
1420  auto visit(detail::monostate, detail::priority_tag<0>) -> error
1421  {
1422  return error(error::invalid_operation, "Cannot scan a monostate");
1423  }
1424 
1425  template <typename Scanner>
1426  error parse(Scanner& s)
1427  {
1428  return m_ctx->parse_context().parse(s, *m_ctx);
1429  }
1430 
1431  Context* m_ctx;
1432  };
1433 
1434  template <typename Context>
1435  scan_result visit(Context& ctx)
1436  {
1437  int args_read = 0;
1438 
1439  auto reterror = [&args_read](error e) -> scan_result {
1440  return scan_result(args_read, std::move(e));
1441  };
1442 
1443  auto& pctx = ctx.parse_context();
1444  auto arg = typename Context::arg_type();
1445 
1446  {
1447  auto ret = skip_stream_whitespace(ctx);
1448  if (!ret) {
1449  return reterror(ret);
1450  }
1451  }
1452 
1453  while (pctx) {
1454  if (pctx.should_skip_ws(ctx.locale())) {
1455  // Skip whitespace from format string and from stream
1456  // EOF is not an error
1457  auto ret = skip_stream_whitespace(ctx);
1458  if (SCN_UNLIKELY(!ret)) {
1459  if (ret == error::end_of_stream) {
1460  break;
1461  }
1463  auto rb = ctx.stream().roll_back();
1464  if (!rb) {
1465  return reterror(rb);
1466  }
1467  return reterror(ret);
1468  }
1469  // Don't advance pctx, should_skip_ws() does it for us
1470  continue;
1471  }
1472 
1473  // Non-brace character, or
1474  // Brace followed by another brace, meaning a literal '{'
1475  if (pctx.should_read_literal(ctx.locale())) {
1476  if (SCN_UNLIKELY(!pctx)) {
1477  return reterror(error(error::invalid_format_string,
1478  "Unexpected end of format string"));
1479  }
1480  // Check for any non-specifier {foo} characters
1481  auto ret = ctx.stream().read_char();
1483  if (!ret || !pctx.check_literal(ret.value())) {
1484  auto rb = ctx.stream().roll_back();
1485  if (!rb) {
1486  // Failed rollback
1487  return reterror(rb);
1488  }
1489  if (!ret) {
1490  // Failed read
1491  return reterror(ret.error());
1492  }
1493 
1494  // Mismatching characters in scan string and stream
1495  return reterror(
1497  "Expected character from format string not "
1498  "found in the stream"));
1499  }
1500  // Bump pctx to next char
1501  pctx.advance();
1502  }
1503  else {
1504  // Scan argument
1505  auto id_wrapped = pctx.parse_arg_id(ctx.locale());
1506  if (!id_wrapped) {
1507  return reterror(id_wrapped.error());
1508  }
1509  auto id = id_wrapped.value();
1510  auto arg_wrapped = [&]() -> expected<typename Context::arg_type>
1511  {
1512  if (id.empty()) {
1513  return ctx.next_arg();
1514  }
1515  if (ctx.locale().is_digit(id.front())) {
1516  size_t tmp = 0;
1517  for (auto ch : id) {
1518  tmp =
1519  tmp * 10 +
1520  static_cast<size_t>(
1521  ch - detail::ascii_widen<
1522  typename Context::char_type>('0'));
1523  }
1524  return ctx.arg(tmp);
1525  }
1526  return ctx.arg(id);
1527  }
1528  ();
1529  if (!arg_wrapped) {
1530  return reterror(arg_wrapped.error());
1531  }
1532  arg = arg_wrapped.value();
1533  if (!pctx) {
1534  return reterror(error(error::invalid_format_string,
1535  "Unexpected end of format argument"));
1536  }
1537  if (!arg) {
1538  // Mismatch between number of args and {}s
1539  return reterror(
1541  "Mismatch between number of arguments and "
1542  "'{}' in the format string"));
1543  }
1544  auto ret = visit_arg<Context>(basic_visitor<Context>(ctx), arg);
1545  if (!ret) {
1546  auto rb = ctx.stream().roll_back();
1547  if (!rb) {
1548  return reterror(rb);
1549  }
1550  return reterror(ret);
1551  }
1552  // Handle next arg and bump pctx
1553  ++args_read;
1554  pctx.arg_handled();
1555  if (pctx) {
1556  pctx.advance();
1557  }
1558  }
1559  }
1560  if (pctx) {
1561  // Format string not exhausted
1562  return reterror(error(error::invalid_format_string,
1563  "Format string not exhausted"));
1564  }
1565  auto srb = ctx.stream().set_roll_back();
1566  if (!srb) {
1567  return reterror(srb);
1568  }
1569  return {args_read};
1570  }
1571 
1573 } // namespace scn
1574 
1575 #if defined(SCN_HEADER_ONLY) && SCN_HEADER_ONLY && !defined(SCN_VISITOR_CPP)
1576 #include "visitor.cpp"
1577 #endif
1578 
1579 #endif // SCN_DETAIL_VISITOR_H
auto operator()(T &&val) -> error
Definition: visitor.h:1336
SCN_CONSTEXPR error() noexcept=default
size_type size() const noexcept
Definition: small_vector.h:502
Scanned value was invalid for given type.
Definition: result.h:44
error scan(T &val, Context &ctx)
Definition: visitor.h:887
SCN_CONSTEXPR T max(T a, T b) noexcept
Definition: util.h:121
#define SCN_END_NAMESPACE
Definition: config.h:376
expected< scan_status > operator()(CharT ch)
Definition: visitor.h:124
detail::named_arg< T, char > arg(string_view name, T &arg)
Definition: context.h:389
expected< scan_status > operator()(CharT ch)
Definition: visitor.h:89
#define SCN_UNLIKELY(x)
Definition: config.h:337
pointer data() noexcept
Definition: small_vector.h:494
error parse(Context &ctx)
Definition: visitor.h:1162
std::false_type does_skip
Definition: visitor.h:74
SCN_CONSTEXPR14 reverse_iterator rbegin() noexcept
Definition: small_vector.h:587
expected< scan_status > operator()(CharT ch)
Definition: visitor.h:106
std::false_type does_skip
Definition: visitor.h:60
result< typename Span::iterator > read(Stream &stream, Span s)
Definition: visitor.h:149
SCN_CONSTEXPR iterator begin() const noexcept
Definition: span.h:58
#define SCN_UNREACHABLE
Definition: config.h:322
Scanned value was out of range for the desired type.
Definition: result.h:49
error parse(Context &ctx)
Definition: visitor.h:497
#define SCN_MSVC_PUSH
Definition: config.h:139
#define SCN_CLANG_IGNORE(x)
Definition: config.h:128
SCN_CONSTEXPR14 iterator begin() noexcept
Definition: small_vector.h:561
#define SCN_GCC_PUSH
Definition: config.h:103
result< size_t > read_into_until_space(Stream &s, Locale &l, Iterator it, bool keep_final=false)
Definition: visitor.h:459
error putback_range(Stream &s, Iterator begin, Iterator end)
Definition: visitor.h:474
Format string was invalid.
Definition: result.h:41
error scan(CharT &val, Context &ctx)
Definition: visitor.h:538
SCN_CONSTEXPR span< T > make_span(T *ptr, std::size_t count) noexcept
Definition: span.h:145
iterator erase(iterator pos)
Definition: small_vector.h:663
expected-like type.
Definition: result.h:186
SCN_CONSTEXPR expected< scan_status > operator()(CharT ch) const noexcept
Definition: visitor.h:50
basic_visitor(Context &ctx)
Definition: visitor.h:1333
typename Context::char_type char_type
Definition: visitor.h:1331
error parse(Context &ctx)
Definition: visitor.h:573
SCN_CONSTEXPR14 pointer data() noexcept
Definition: util.h:320
#define SCN_CLANG_POP
Definition: config.h:127
#define SCN_CLANG_POP_IGNORE_UNDEFINED_TEMPLATE
Definition: config.h:130
SCN_CONSTEXPR14 InputIt find(InputIt first, InputIt last, const T &value)
Definition: util.h:173
SCN_CONSTEXPR expected< scan_status > operator()(CharT) const noexcept
Definition: visitor.h:42
#define SCN_VISIT_INT(T)
Definition: visitor.h:1370
static result< size_t > f(Stream &s, Locale &l, Iterator it, bool keep_final)
Definition: visitor.h:409
result< size_t > read_into_if(Stream &s, Iterator it, Predicate &&p, bool keep_final=false)
Definition: visitor.h:185
error scan(T &val, Context &ctx)
Definition: visitor.h:1078
std::false_type does_skip
Definition: visitor.h:49
Stream does not support the performed operation.
Definition: result.h:46
scan_result visit(Context &ctx)
Definition: visitor.h:1435
error parse(Context &ctx)
Definition: visitor.h:518
std::false_type does_skip
Definition: visitor.h:41
SCN_CONSTEXPR T min(T a, T b) noexcept
Definition: util.h:144
SCN_CONSTEXPR14 iterator begin() noexcept
Definition: util.h:294
#define SCN_MSVC_IGNORE(x)
Definition: config.h:141
SCN_CONSTEXPR bool is_space(char_type ch) const
Definition: locale.h:218
Error class.
Definition: result.h:32
#define SCN_BEGIN_NAMESPACE
Definition: config.h:375
expected< scan_status > operator()(CharT ch)
Definition: visitor.h:75
error parse(Context &ctx)
Definition: visitor.h:735
SCN_CONSTEXPR iterator end() const noexcept
Definition: span.h:62
error parse(Context &ctx)
Definition: visitor.h:1019
#define SCN_GCC_POP
Definition: config.h:104
SCN_CONSTEXPR14 iterator end() noexcept
Definition: small_vector.h:574
scan_status
Definition: visitor.h:29
CharT ascii_widen(char ch)
Definition: util.h:110
typename Context::locale_type & locale_ref
Definition: visitor.h:33
#define SCN_MSVC_POP
Definition: config.h:140
#define SCN_VISIT_FLOAT(T)
Definition: visitor.h:1390
#define SCN_CLANG_PUSH_IGNORE_UNDEFINED_TEMPLATE
Definition: config.h:129
result< int > scan_result
Definition: result.h:178
expected< scan_status > operator()(CharT ch)
Definition: visitor.h:61
error scan(bool &val, Context &ctx)
Definition: visitor.h:625
SCN_CONSTEXPR index_type size() const noexcept
Definition: span.h:102
SCN_CLANG_POP error skip_stream_whitespace(Context &ctx) noexcept
Definition: visitor.h:1271
#define SCN_CONSTEXPR
Definition: config.h:226
SCN_CONSTEXPR14 reference front()
Definition: small_vector.h:539
SCN_CONSTEXPR14 reverse_iterator rend() noexcept
Definition: small_vector.h:600
error scan(span< CharT > &val, Context &ctx)
Definition: visitor.h:551
Context context_type
Definition: visitor.h:1330
error scan(std::basic_string< CharT > &val, Context &ctx)
Definition: visitor.h:1182
#define SCN_GCC_IGNORE(x)
Definition: config.h:105
#define SCN_CLANG_PUSH
Definition: config.h:126
typename Stream::char_type char_type
Definition: visitor.h:408