diff options
Diffstat (limited to 'src/third-party/scnlib/include/scn/scan')
-rw-r--r-- | src/third-party/scnlib/include/scn/scan/common.h | 131 | ||||
-rw-r--r-- | src/third-party/scnlib/include/scn/scan/getline.h | 186 | ||||
-rw-r--r-- | src/third-party/scnlib/include/scn/scan/ignore.h | 189 | ||||
-rw-r--r-- | src/third-party/scnlib/include/scn/scan/istream.h | 147 | ||||
-rw-r--r-- | src/third-party/scnlib/include/scn/scan/list.h | 450 | ||||
-rw-r--r-- | src/third-party/scnlib/include/scn/scan/scan.h | 444 | ||||
-rw-r--r-- | src/third-party/scnlib/include/scn/scan/vscan.h | 208 |
7 files changed, 1755 insertions, 0 deletions
diff --git a/src/third-party/scnlib/include/scn/scan/common.h b/src/third-party/scnlib/include/scn/scan/common.h new file mode 100644 index 0000000..ccdd825 --- /dev/null +++ b/src/third-party/scnlib/include/scn/scan/common.h @@ -0,0 +1,131 @@ +// Copyright 2017 Elias Kosunen +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file is a part of scnlib: +// https://github.com/eliaskosunen/scnlib + +#ifndef SCN_SCAN_COMMON_H +#define SCN_SCAN_COMMON_H + +#include "../detail/locale.h" +#include "../detail/result.h" +#include "../unicode/common.h" + +namespace scn { + SCN_BEGIN_NAMESPACE + + namespace detail { + template <typename CharT> + constexpr int to_format(int i) + { + return i; + } + template <typename T> + constexpr auto to_format(T&& f) -> decltype(string_view{SCN_FWD(f)}) + { + return {SCN_FWD(f)}; + } + template <typename T> + constexpr auto to_format(T&& f) -> decltype(wstring_view{SCN_FWD(f)}) + { + return {SCN_FWD(f)}; + } + template <typename CharT> + basic_string_view<CharT> to_format(const std::basic_string<CharT>& str) + { + return {str.data(), str.size()}; + } + + template <typename CharT> + struct until_pred { + array<CharT, 4> until; + size_t size; + + constexpr until_pred(CharT ch) : until({{ch}}), size(1) {} + until_pred(code_point cp) + { + auto ret = encode_code_point(until.begin(), until.end(), cp); + SCN_ENSURE(ret); + size = ret.value() - until.begin(); + } + + SCN_CONSTEXPR14 bool operator()(span<const CharT> ch) const + { + if (ch.size() != size) { + return false; + } + for (size_t i = 0; i < ch.size(); ++i) { + if (ch[i] != until[i]) { + return false; + } + } + return true; + } + static constexpr bool is_localized() + { + return false; + } + constexpr bool is_multibyte() const + { + return size != 1; + } + }; + + template <typename Error, typename Range> + using generic_scan_result_for_range = decltype(detail::wrap_result( + SCN_DECLVAL(Error), + SCN_DECLVAL(detail::range_tag<Range>), + SCN_DECLVAL(range_wrapper_for_t<Range>))); + template <typename Range> + using scan_result_for_range = + generic_scan_result_for_range<wrapped_error, Range>; + } // namespace detail + + template <typename T> + struct discard_type { + discard_type() = default; + }; + + /** + * Scans an instance of `T`, but doesn't store it anywhere. + * Uses `scn::temp` internally, so the user doesn't have to bother. + * + * \code{.cpp} + * int i{}; + * // 123 is discarded, 456 is read into `i` + * auto result = scn::scan("123 456", "{} {}", scn::discard<T>(), i); + * // result == true + * // i == 456 + * \endcode + */ + template <typename T> + discard_type<T>& discard() + { + return temp(discard_type<T>{})(); + } + + template <typename T> + struct scanner<discard_type<T>> : public scanner<T> { + template <typename Context> + error scan(discard_type<T>&, Context& ctx) + { + T tmp; + return scanner<T>::scan(tmp, ctx); + } + }; + + SCN_END_NAMESPACE +} // namespace scn + +#endif diff --git a/src/third-party/scnlib/include/scn/scan/getline.h b/src/third-party/scnlib/include/scn/scan/getline.h new file mode 100644 index 0000000..c330969 --- /dev/null +++ b/src/third-party/scnlib/include/scn/scan/getline.h @@ -0,0 +1,186 @@ +// Copyright 2017 Elias Kosunen +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file is a part of scnlib: +// https://github.com/eliaskosunen/scnlib + +#ifndef SCN_SCAN_GETLINE_H +#define SCN_SCAN_GETLINE_H + +#include "common.h" + +namespace scn { + SCN_BEGIN_NAMESPACE + + namespace detail { + template <typename WrappedRange, + typename String, + typename Until, + typename CharT = typename WrappedRange::char_type> + error getline_impl(WrappedRange& r, String& str, Until until) + { + auto pred = until_pred<CharT>{until}; + auto s = read_until_space_zero_copy(r, pred, true); + if (!s) { + return s.error(); + } + if (s.value().size() != 0) { + auto size = s.value().size(); + if (pred(s.value().last(1))) { + --size; + } + str.clear(); + str.resize(size); + std::copy(s.value().begin(), s.value().begin() + size, + str.begin()); + return {}; + } + + String tmp; + auto out = std::back_inserter(tmp); + auto e = read_until_space(r, out, pred, true); + if (!e) { + return e; + } + if (pred(span<const CharT>(&*(tmp.end() - 1), 1))) { + tmp.pop_back(); + } + str = SCN_MOVE(tmp); + return {}; + } + template <typename WrappedRange, + typename Until, + typename CharT = typename WrappedRange::char_type> + error getline_impl(WrappedRange& r, + basic_string_view<CharT>& str, + Until until) + { + static_assert( + WrappedRange::is_contiguous, + "Cannot getline a string_view from a non-contiguous range"); + auto pred = until_pred<CharT>{until}; + auto s = read_until_space_zero_copy(r, pred, true); + if (!s) { + return s.error(); + } + SCN_ASSERT(s.value().size(), ""); + auto size = s.value().size(); + if (pred(s.value().last(1))) { + --size; + } + str = basic_string_view<CharT>{s.value().data(), size}; + return {}; + } +#if SCN_HAS_STRING_VIEW + template <typename WrappedRange, + typename Until, + typename CharT = typename WrappedRange::char_type> + auto getline_impl(WrappedRange& r, + std::basic_string_view<CharT>& str, + Until until) -> error + { + auto sv = ::scn::basic_string_view<CharT>{}; + auto ret = getline_impl(r, sv, until); + str = ::std::basic_string_view<CharT>{sv.data(), sv.size()}; + return ret; + } +#endif + } // namespace detail + + /** + * Read the range in \c r into \c str until \c until is found. + * \c until will be skipped in parsing: it will not be pushed into \c + * str, and the returned range will go past it. + * + * If `str` is convertible to a `basic_string_view`: + * - And if `r` is a `contiguous_range`: + * - `str` is set to point inside `r` with the appropriate length + * - if not, returns an error + * + * Otherwise, clears `str` by calling `str.clear()`, and then reads the + * range into `str` as if by repeatedly calling \c str.push_back. + * `str.reserve()` is also required to be present. + * + * `Until` can either be the same as `r` character type (`char` or + * `wchar_t`), or `code_point`. + * + * \code{.cpp} + * auto source = "hello\nworld" + * std::string line; + * auto result = scn::getline(source, line, '\n'); + * // line == "hello" + * // result.range() == "world" + * + * // Using the other overload + * result = scn::getline(result.range(), line); + * // line == "world" + * // result.empty() == true + * \endcode + */ +#if SCN_DOXYGEN + template <typename Range, typename String, typename Until> + auto getline(Range&& r, String& str, Until until) + -> detail::scan_result_for_range<Range>; +#else + template <typename Range, typename String, typename Until> + SCN_NODISCARD auto getline(Range&& r, String& str, Until until) + -> detail::scan_result_for_range<Range> + { + auto wrapped = wrap(SCN_FWD(r)); + auto err = getline_impl(wrapped, str, until); + if (!err) { + auto e = wrapped.reset_to_rollback_point(); + if (!e) { + err = e; + } + } + else { + wrapped.set_rollback_point(); + } + return detail::wrap_result( + wrapped_error{err}, detail::range_tag<Range>{}, SCN_MOVE(wrapped)); + } +#endif + + /** + * Equivalent to \ref getline with the last parameter set to + * <tt>'\\n'</tt> with the appropriate character type. + * + * In other words, reads `r` into `str` until <tt>'\\n'</tt> is found. + * + * The character type is determined by `r`. + */ +#if SCN_DOXYGEN + template <typename Range, + typename String, + typename CharT = typename detail::extract_char_type< + ranges::iterator_t<range_wrapper_for_t<Range>>>::type> + auto getline(Range&& r, String& str) + -> detail::scan_result_for_range<Range>; +#else + template <typename Range, + typename String, + typename CharT = typename detail::extract_char_type< + ranges::iterator_t<range_wrapper_for_t<Range>>>::type> + SCN_NODISCARD auto getline(Range&& r, String& str) + -> detail::scan_result_for_range<Range> + { + return getline(SCN_FWD(r), str, detail::ascii_widen<CharT>('\n')); + } +#endif + + SCN_END_NAMESPACE +} // namespace scn + +#endif diff --git a/src/third-party/scnlib/include/scn/scan/ignore.h b/src/third-party/scnlib/include/scn/scan/ignore.h new file mode 100644 index 0000000..415a877 --- /dev/null +++ b/src/third-party/scnlib/include/scn/scan/ignore.h @@ -0,0 +1,189 @@ +// Copyright 2017 Elias Kosunen +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file is a part of scnlib: +// https://github.com/eliaskosunen/scnlib + +#ifndef SCN_SCAN_IGNORE_H +#define SCN_SCAN_IGNORE_H + +#include "common.h" + +namespace scn { + SCN_BEGIN_NAMESPACE + + namespace detail { + template <typename CharT> + struct ignore_iterator { + using value_type = CharT; + using pointer = value_type*; + using reference = value_type&; + using difference_type = std::ptrdiff_t; + using iterator_category = std::output_iterator_tag; + + constexpr ignore_iterator() = default; + + SCN_CONSTEXPR14 ignore_iterator& operator=(CharT) noexcept + { + return *this; + } + constexpr const ignore_iterator& operator=(CharT) const noexcept + { + return *this; + } + + SCN_CONSTEXPR14 ignore_iterator& operator*() noexcept + { + return *this; + } + constexpr const ignore_iterator& operator*() const noexcept + { + return *this; + } + + SCN_CONSTEXPR14 ignore_iterator& operator++() noexcept + { + return *this; + } + constexpr const ignore_iterator& operator++() const noexcept + { + return *this; + } + }; + + template <typename CharT> + struct ignore_iterator_n { + using value_type = CharT; + using pointer = value_type*; + using reference = value_type&; + using difference_type = std::ptrdiff_t; + using iterator_category = std::output_iterator_tag; + + ignore_iterator_n() = default; + ignore_iterator_n(difference_type n) : i(n) {} + + constexpr const ignore_iterator_n& operator=(CharT) const noexcept + { + return *this; + } + + constexpr const ignore_iterator_n& operator*() const noexcept + { + return *this; + } + + SCN_CONSTEXPR14 ignore_iterator_n& operator++() noexcept + { + ++i; + return *this; + } + + constexpr bool operator==(const ignore_iterator_n& o) const noexcept + { + return i == o.i; + } + constexpr bool operator!=(const ignore_iterator_n& o) const noexcept + { + return !(*this == o); + } + + difference_type i{0}; + }; + + template <typename WrappedRange, + typename Until, + typename CharT = typename WrappedRange::char_type> + error ignore_until_impl(WrappedRange& r, Until until) + { + ignore_iterator<CharT> it{}; + return read_until_space(r, it, until_pred<CharT>{until}, false); + } + + template <typename WrappedRange, + typename Until, + typename CharT = typename WrappedRange::char_type> + error ignore_until_n_impl(WrappedRange& r, + ranges::range_difference_t<WrappedRange> n, + Until until) + { + ignore_iterator_n<CharT> begin{}, end{n}; + return read_until_space_ranged(r, begin, end, + until_pred<CharT>{until}, false); + } + } // namespace detail + + /** + * Advances the beginning of \c r until \c until is found. + */ +#if SCN_DOXYGEN + template <typename Range, typename Until> + auto ignore_until(Range&& r, Until until) + -> detail::scan_result_for_range<Range>; +#else + template <typename Range, typename Until> + SCN_NODISCARD auto ignore_until(Range&& r, Until until) + -> detail::scan_result_for_range<Range> + { + auto wrapped = wrap(SCN_FWD(r)); + auto err = detail::ignore_until_impl(wrapped, until); + if (!err) { + auto e = wrapped.reset_to_rollback_point(); + if (!e) { + err = e; + } + } + else { + wrapped.set_rollback_point(); + } + return detail::wrap_result( + wrapped_error{err}, detail::range_tag<Range>{}, SCN_MOVE(wrapped)); + } +#endif + + /** + * Advances the beginning of \c r until \c until is found, or the + * beginning has been advanced \c n times. + * + * `Until` can be the `r` character type (`char` or `wchar_t`), or + * `code_point`. + */ +#if SCN_DOXYGEN + template <typename Range, typename Until> + auto ignore_until_n(Range&& r, + ranges::range_difference_t<Range> n, + Until until) -> detail::scan_result_for_range<Range>; +#else + template <typename Range, typename Until> + SCN_NODISCARD auto ignore_until_n(Range&& r, + ranges::range_difference_t<Range> n, + Until until) + -> detail::scan_result_for_range<Range> + { + auto wrapped = wrap(SCN_FWD(r)); + auto err = detail::ignore_until_n_impl(wrapped, n, until); + if (!err) { + auto e = wrapped.reset_to_rollback_point(); + if (!e) { + err = e; + } + } + return detail::wrap_result( + wrapped_error{err}, detail::range_tag<Range>{}, SCN_MOVE(wrapped)); + } +#endif + + SCN_END_NAMESPACE +} // namespace scn + +#endif diff --git a/src/third-party/scnlib/include/scn/scan/istream.h b/src/third-party/scnlib/include/scn/scan/istream.h new file mode 100644 index 0000000..a9aef86 --- /dev/null +++ b/src/third-party/scnlib/include/scn/scan/istream.h @@ -0,0 +1,147 @@ +// Copyright 2017 Elias Kosunen +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file is a part of scnlib: +// https://github.com/eliaskosunen/scnlib + +#ifndef SCN_SCAN_ISTREAM_H +#define SCN_SCAN_ISTREAM_H + +#include "../reader/common.h" +#include "../detail/result.h" + +#include <istream> + +namespace scn { + SCN_BEGIN_NAMESPACE + + namespace detail { + template <typename WrappedRange> + class range_streambuf + : public std::basic_streambuf<typename WrappedRange::char_type> { + using base = std::basic_streambuf<typename WrappedRange::char_type>; + + public: + using range_type = WrappedRange; + using char_type = typename WrappedRange::char_type; + using traits_type = typename base::traits_type; + using int_type = typename base::int_type; + + explicit range_streambuf(range_type& r) : m_range(std::addressof(r)) + { + } + + private: + int_type underflow() override + { + // already read + if (!traits_type::eq_int_type(m_ch, traits_type::eof())) { + return m_ch; + } + + auto ret = read_code_unit(*m_range); + if (!ret) { + // error + // m_ch is already eof + return traits_type::eof(); + } + m_ch = traits_type::to_int_type(ret.value()); + return m_ch; + } + int_type uflow() override + { + auto ret = underflow(); + if (ret != traits_type::eof()) { + m_ch = traits_type::eof(); + } + return ret; + } + std::streamsize showmanyc() override + { + return traits_type::eq_int_type(m_ch, traits_type::eof()) ? 0 + : 1; + } + int_type pbackfail(int_type) override + { + auto e = putback_n(*m_range, 1); + if (!e) { + return traits_type::eof(); + } + return traits_type::to_int_type(0); + } + + range_type* m_range; + int_type m_ch{traits_type::eof()}; + }; + + // Trick stolen from {fmt} + template <typename CharT> + struct test_std_stream : std::basic_istream<CharT> { + private: + struct null; + // Hide all operator>> from std::basic_istream<CharT> + void operator>>(null); + }; + + // Check for user-defined operator>> + template <typename CharT, typename T, typename = void> + struct is_std_streamable : std::false_type { + }; + + template <typename CharT, typename T> + struct is_std_streamable< + CharT, + T, + void_t<decltype(SCN_DECLVAL(test_std_stream<CharT>&) >> + SCN_DECLVAL(T&))>> : std::true_type { + }; + } // namespace detail + + template <typename T> + struct scanner<T, + typename std::enable_if< + detail::is_std_streamable<char, T>::value || + detail::is_std_streamable<wchar_t, T>::value>::type> + : public empty_parser { + template <typename Context> + error scan(T& val, Context& ctx) + { + static_assert(detail::is_std_streamable<typename Context::char_type, + T>::value, + "Type can not be read from a basic_istream of this " + "character type"); + detail::range_streambuf<typename Context::range_type> streambuf( + ctx.range()); + std::basic_istream<typename Context::char_type> stream( + std::addressof(streambuf)); + + if (!(stream >> val)) { + if (stream.eof()) { + return {error::end_of_range, "EOF"}; + } + if (stream.bad()) { + return {error::unrecoverable_source_error, + "Bad std::istream after reading"}; + } + return {error::invalid_scanned_value, + "Failed to read with std::istream"}; + } + return {}; + } + }; + + SCN_END_NAMESPACE +} // namespace scn + +#endif // SCN_DETAIL_ISTREAM_H diff --git a/src/third-party/scnlib/include/scn/scan/list.h b/src/third-party/scnlib/include/scn/scan/list.h new file mode 100644 index 0000000..a5eeaf5 --- /dev/null +++ b/src/third-party/scnlib/include/scn/scan/list.h @@ -0,0 +1,450 @@ +// Copyright 2017 Elias Kosunen +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file is a part of scnlib: +// https://github.com/eliaskosunen/scnlib + +#ifndef SCN_SCAN_LIST_H +#define SCN_SCAN_LIST_H + +#include "common.h" + +namespace scn { + SCN_BEGIN_NAMESPACE + + /** + * Adapts a `span` into a type that can be read into using \ref + * scan_list. This way, potentially unnecessary dynamic memory + * allocations can be avoided. To use as a parameter to \ref scan_list, + * use \ref make_span_list_wrapper. + * + * \code{.cpp} + * std::vector<int> buffer(8, 0); + * scn::span<int> s = scn::make_span(buffer); + * + * auto wrapper = scn::span_list_wrapper<int>(s); + * scn::scan_list("123 456", wrapper); + * // s[0] == buffer[0] == 123 + * // s[1] == buffer[1] == 456 + * \endcode + * + * \see scan_list + * \see make_span_list_wrapper + */ + template <typename T> + struct span_list_wrapper { + using value_type = T; + + span_list_wrapper(span<T> s) : m_span(s) {} + + void push_back(T val) + { + SCN_EXPECT(n < max_size()); + m_span[n] = SCN_MOVE(val); + ++n; + } + + SCN_NODISCARD constexpr std::size_t size() const noexcept + { + return n; + } + SCN_NODISCARD constexpr std::size_t max_size() const noexcept + { + return m_span.size(); + } + + span<T> m_span; + std::size_t n{0}; + }; + + namespace detail { + template <typename T> + using span_list_wrapper_for = + span_list_wrapper<typename decltype(make_span( + SCN_DECLVAL(T&)))::value_type>; + } + + /** + * Adapts a contiguous buffer into a type containing a `span` that can + * be read into using \ref scn::scan_list. + * + * Example adapted from \ref span_list_wrapper: + * \code{.cpp} + * std::vector<int> buffer(8, 0); + * scn::scan_list("123 456", scn::make_span_list_wrapper(buffer)); + * // s[0] == buffer[0] == 123 + * // s[1] == buffer[1] == 456 + * \endcode + * + * \see scan_list + * \see span_list_wrapper + */ + template <typename T> + auto make_span_list_wrapper(T& s) + -> temporary<detail::span_list_wrapper_for<T>> + { + auto _s = make_span(s); + return temp(span_list_wrapper<typename decltype(_s)::value_type>(_s)); + } + + /** + * Used to customize `scan_list_ex()`. + * + * \tparam CharT Can be a code unit type (`char` or `wchar_t`, depending on + * the source range), or `code_point`. + * + * `list_separator`, `list_until` and `list_separator_and_until` can be used + * to create a value of this type, taking advantage of template argument + * deduction (no need to hand-specify `CharT`). + */ + template <typename CharT> + struct scan_list_options { + /** + * If set, up to one separator character can be accepted between values, + * which may be surrounded by whitespace. + */ + optional<CharT> separator{}; + /** + * If set, reading the list is stopped if this character is found + * between values. + * + * In that case, it is advanced over, and no error is returned. + */ + optional<CharT> until{}; + + scan_list_options() = default; + scan_list_options(optional<CharT> s, optional<CharT> u) + : separator(SCN_MOVE(s)), until(SCN_MOVE(u)) + { + } + }; + + /** + * Create a `scan_list_options` for `scan_list_ex`, by using `ch` as the + * separator character. + */ + template <typename CharT> + scan_list_options<CharT> list_separator(CharT ch) + { + return {optional<CharT>{ch}, nullopt}; + } + /** + * Create a `scan_list_options` for `scan_list_ex`, by using `ch` as the + * until-character. + */ + template <typename CharT> + scan_list_options<CharT> list_until(CharT ch) + { + return {nullopt, optional<CharT>{ch}}; + } + /** + * Create a `scan_list_options` for `scan_list_ex`, by using `sep` as the + * separator, and `until` as the until-character. + */ + template <typename CharT> + scan_list_options<CharT> list_separator_and_until(CharT sep, CharT until) + { + return {optional<CharT>{sep}, optional<CharT>{until}}; + } + + namespace detail { + template <typename WrappedRange, typename CharT> + expected<CharT> check_separator(WrappedRange& r, size_t& n, CharT) + { + auto ret = read_code_unit(r); + if (!ret) { + return ret.error(); + } + n = 1; + return ret.value(); + } + template <typename WrappedRange> + expected<code_point> check_separator(WrappedRange& r, + size_t& n, + code_point) + { + unsigned char buf[4] = {0}; + auto ret = read_code_point(r, make_span(buf, 4)); + if (!ret) { + return ret.error(); + } + n = ret.value().chars.size(); + return ret.value().cp; + } + + template <typename Context, typename Container, typename Separator> + auto scan_list_impl(Context& ctx, + bool localized, + Container& c, + scan_list_options<Separator> options) -> error + { + using char_type = typename Context::char_type; + using value_type = typename Container::value_type; + value_type value; + + auto args = make_args_for(ctx.range(), 1, value); + + bool scanning = true; + while (scanning) { + if (c.size() == c.max_size()) { + break; + } + + // read value + auto pctx = make_parse_context(1, ctx.locale(), localized); + auto err = visit(ctx, pctx, basic_args<char_type>{args}); + if (!err) { + if (err == error::end_of_range) { + break; + } + return err; + } + c.push_back(SCN_MOVE(value)); + + auto next = static_cast<Separator>(0); + size_t n{0}; + + auto read_next = [&]() -> error { + auto ret = check_separator(ctx.range(), n, + static_cast<Separator>(0)); + if (!ret) { + if (ret.error() == error::end_of_range) { + scanning = false; + return {}; + } + return ret.error(); + } + next = ret.value(); + + err = + putback_n(ctx.range(), static_cast<std::ptrdiff_t>(n)); + if (!err) { + return err; + } + + return {}; + }; + + bool sep_found = false; + while (true) { + // read until + if (options.until) { + err = read_next(); + if (!err) { + return err; + } + if (!scanning) { + break; + } + + if (next == options.until.get()) { + scanning = false; + break; + } + } + + // read sep + if (options.separator && !sep_found) { + err = read_next(); + if (!err) { + return err; + } + if (!scanning) { + break; + } + + if (next == options.separator.get()) { + // skip to next char + ctx.range().advance(static_cast<std::ptrdiff_t>(n)); + continue; + } + } + + err = read_next(); + if (!err) { + return err; + } + if (!scanning) { + break; + } + + if (ctx.locale().get_static().is_space(next)) { + // skip ws + ctx.range().advance(static_cast<std::ptrdiff_t>(n)); + } + else { + break; + } + } + } + + return {}; + } + } // namespace detail + + /** + * Reads values repeatedly from `r` and writes them into `c`. + * + * The values read are of type `Container::value_type`, and they are + * written into `c` using `c.push_back`. + * The values are separated by whitespace. + * + * The range is read, until: + * - `c.max_size()` is reached, or + * - range `EOF` is reached + * + * In these cases, an error will not be returned, and the beginning + * of the returned range will point to the first character after the + * scanned list. + * + * If an invalid value is scanned, `error::invalid_scanned_value` is + * returned, but the values already in `vec` will remain there. The range is + * put back to the state it was before reading the invalid value. + * + * To scan into `span`, use \ref span_list_wrapper. + * \ref make_span_list_wrapper + * + * \code{.cpp} + * std::vector<int> vec{}; + * auto result = scn::scan_list("123 456", vec); + * // vec == [123, 456] + * // result.empty() == true + * + * vec.clear(); + * result = scn::scan_list("123 456 abc", vec); + * // vec == [123, 456] + * // result.error() == invalid_scanned_value + * // result.range() == " abc" + * \endcode + * + * \param r Range to read from + * \param c Container to write values to, using `c.push_back()`. + * `Container::value_type` will be used to determine the type of the values + * to read. + */ +#if SCN_DOXYGEN + template <typename Range, typename Container> + auto scan_list(Range&& r, Container& c) + -> detail::scan_result_for_range<Range>; +#else + template <typename Range, typename Container> + SCN_NODISCARD auto scan_list(Range&& r, Container& c) + -> detail::scan_result_for_range<Range> + { + auto range = wrap(SCN_FWD(r)); + auto ctx = make_context(SCN_MOVE(range)); + using char_type = typename decltype(ctx)::char_type; + + auto err = detail::scan_list_impl(ctx, false, c, + scan_list_options<char_type>{}); + + return detail::wrap_result(wrapped_error{err}, + detail::range_tag<Range>{}, + SCN_MOVE(ctx.range())); + } +#endif + + /** + * Otherwise equivalent to `scan_list()`, except can react to additional + * characters, based on `options`. + * + * See `scan_list_options` for more information. + * + * \param r Range to scan from + * \param c Container to write read values into + * \param options Options to use + * + * \code{.cpp} + * std::vector<int> vec{}; + * auto result = scn::scan_list_ex("123, 456", vec, + * scn::list_separator(',')); + * // vec == [123, 456] + * // result.empty() == true + * \endcode + * + * \see scan_list + * \see scan_list_options + */ +#if SCN_DOXYGEN + template <typename Range, typename Container, typename CharT> + auto scan_list_ex(Range&& r, Container& c, scan_list_options<CharT> options) + -> detail::scan_result_for_range<Range>; +#else + template <typename Range, typename Container, typename CharT> + SCN_NODISCARD auto scan_list_ex(Range&& r, + Container& c, + scan_list_options<CharT> options) + -> detail::scan_result_for_range<Range> + { + auto range = wrap(SCN_FWD(r)); + auto ctx = make_context(SCN_MOVE(range)); + + auto err = detail::scan_list_impl(ctx, false, c, options); + + return detail::wrap_result(wrapped_error{err}, + detail::range_tag<Range>{}, + SCN_MOVE(ctx.range())); + } +#endif + + /** + * Otherwise equivalent to `scan_list_ex()`, except uses `loc` to scan the + * values. + * + * \param loc Locale to use for scanning. Must be a `std::locale`. + * \param r Range to scan from + * \param c Container to write read values into + * \param options Options to use + * + * \see scan_list_ex() + * \see scan_localized() + */ +#if SCN_DOXYGEN + template <typename Locale, + typename Range, + typename Container, + typename CharT> + auto scan_list_localized(const Locale& loc, + Range&& r, + Container& c, + scan_list_options<CharT> options) + -> detail::scan_result_for_range<Range>; +#else + template <typename Locale, + typename Range, + typename Container, + typename CharT> + SCN_NODISCARD auto scan_list_localized(const Locale& loc, + Range&& r, + Container& c, + scan_list_options<CharT> options) + -> detail::scan_result_for_range<Range> + { + auto range = wrap(SCN_FWD(r)); + using char_type = typename decltype(range)::char_type; + auto locale = make_locale_ref<char_type>(loc); + auto ctx = make_context(SCN_MOVE(range), SCN_MOVE(locale)); + + auto err = detail::scan_list_impl(ctx, true, c, options); + + return detail::wrap_result(wrapped_error{err}, + detail::range_tag<Range>{}, + SCN_MOVE(ctx.range())); + } +#endif + + SCN_END_NAMESPACE +} // namespace scn + +#endif diff --git a/src/third-party/scnlib/include/scn/scan/scan.h b/src/third-party/scnlib/include/scn/scan/scan.h new file mode 100644 index 0000000..20f4cd1 --- /dev/null +++ b/src/third-party/scnlib/include/scn/scan/scan.h @@ -0,0 +1,444 @@ +// Copyright 2017 Elias Kosunen +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file is a part of scnlib: +// https://github.com/eliaskosunen/scnlib + +#ifndef SCN_SCAN_SCAN_H +#define SCN_SCAN_SCAN_H + +#include "../util/optional.h" +#include "common.h" +#include "vscan.h" + +namespace scn { + SCN_BEGIN_NAMESPACE + + namespace dummy { + } + + /** + * \tparam OriginalRange The type of the range passed to the scanning + * function \param result Return value of `vscan` \return Result object + * + * \code{.cpp} + * template <typename Range, typename... Args> + * auto scan(Range&& r, string_view f, Args&... a) { + * auto range = scn::wrap(std::forward<Range>(r)); + * auto args = scn::make_args_for(range, f, a...); + * auto ret = scn::vscan(std::move(range), f, {args}); + * return scn::make_scan_result<Range>(std::move(ret)); + * } + * \endcode + */ + template <typename OriginalRange, + typename Error = wrapped_error, + typename WrappedRange> + auto make_scan_result(vscan_result<WrappedRange> result) + -> detail::scan_result_for_range<OriginalRange> + { + return detail::wrap_result(Error{result.err}, + detail::range_tag<OriginalRange>{}, + SCN_MOVE(result.range)); + } + + namespace detail { + template <typename Range, typename Format, typename... Args> + auto scan_boilerplate(Range&& r, const Format& f, Args&... a) + -> detail::scan_result_for_range<Range> + { + static_assert(sizeof...(Args) > 0, + "Have to scan at least a single argument"); + static_assert(SCN_CHECK_CONCEPT(ranges::range<Range>), + "Input needs to be a Range"); + + auto range = wrap(SCN_FWD(r)); + auto format = detail::to_format(f); + auto args = make_args_for(range, format, a...); + auto ret = vscan(SCN_MOVE(range), format, {args}); + return make_scan_result<Range>(SCN_MOVE(ret)); + } + + template <typename Range, typename... Args> + auto scan_boilerplate_default(Range&& r, Args&... a) + -> detail::scan_result_for_range<Range> + { + static_assert(sizeof...(Args) > 0, + "Have to scan at least a single argument"); + static_assert(SCN_CHECK_CONCEPT(ranges::range<Range>), + "Input needs to be a Range"); + + auto range = wrap(SCN_FWD(r)); + auto format = static_cast<int>(sizeof...(Args)); + auto args = make_args_for(range, format, a...); + auto ret = vscan_default(SCN_MOVE(range), format, {args}); + return make_scan_result<Range>(SCN_MOVE(ret)); + } + + template <typename Locale, + typename Range, + typename Format, + typename... Args> + auto scan_boilerplate_localized(const Locale& loc, + Range&& r, + const Format& f, + Args&... a) + -> detail::scan_result_for_range<Range> + { + static_assert(sizeof...(Args) > 0, + "Have to scan at least a single argument"); + static_assert(SCN_CHECK_CONCEPT(ranges::range<Range>), + "Input needs to be a Range"); + + auto range = wrap(SCN_FWD(r)); + auto format = detail::to_format(f); + SCN_CLANG_PUSH_IGNORE_UNDEFINED_TEMPLATE + auto locale = + make_locale_ref<typename decltype(range)::char_type>(loc); + SCN_CLANG_POP_IGNORE_UNDEFINED_TEMPLATE + + auto args = make_args_for(range, format, a...); + auto ret = vscan_localized(SCN_MOVE(range), SCN_MOVE(locale), + format, {args}); + return make_scan_result<Range>(SCN_MOVE(ret)); + } + + } // namespace detail + + // scan + + // For some reason, Doxygen dislikes SCN_NODISCARD + + /** + * The most fundamental part of the scanning API. + * Reads from the range in \c r according to the format string \c f. + * + * \code{.cpp} + * int i; + * scn::scan("123", "{}", i); + * // i == 123 + * \endcode + */ +#if SCN_DOXYGEN + template <typename Range, typename Format, typename... Args> + auto scan(Range&& r, const Format& f, Args&... a) + -> detail::scan_result_for_range<Range>; +#else + template <typename Range, typename Format, typename... Args> + SCN_NODISCARD auto scan(Range&& r, const Format& f, Args&... a) + -> detail::scan_result_for_range<Range> + { + return detail::scan_boilerplate(SCN_FWD(r), f, a...); + } +#endif + + // default format + + /** + * Equivalent to \ref scan, but with a + * format string with the appropriate amount of space-separated `"{}"`s for + * the number of arguments. Because this function doesn't have to parse the + * format string, performance is improved. + * + * Adapted from the example for \ref scan + * \code{.cpp} + * int i; + * scn::scan_default("123", i); + * // i == 123 + * \endcode + * + * \see scan + */ +#if SCN_DOXYGEN + template <typename Range, typename... Args> + auto scan_default(Range&& r, Args&... a) + -> detail::scan_result_for_range<Range>; +#else + template <typename Range, typename... Args> + SCN_NODISCARD auto scan_default(Range&& r, Args&... a) + -> detail::scan_result_for_range<Range> + { + return detail::scan_boilerplate_default(std::forward<Range>(r), a...); + } +#endif + + // scan localized + + /** + * Read from the range in \c r using the locale in \c loc. + * \c loc must be a \c std::locale. The parameter is a template to avoid + * inclusion of `<locale>`. + * + * Use of this function is discouraged, due to the overhead involved + * with locales. Note, that the other functions are completely + * locale-agnostic, and aren't affected by changes to the global C + * locale. + * + * \code{.cpp} + * double d; + * scn::scan_localized(std::locale{"fi_FI"}, "3,14", "{}", d); + * // d == 3.14 + * \endcode + * + * \see scan + */ +#if SCN_DOXYGEN + template <typename Locale, + typename Range, + typename Format, + typename... Args> + auto scan_localized(const Locale& loc, + Range&& r, + const Format& f, + Args&... a) -> detail::scan_result_for_range<Range>; +#else + template <typename Locale, + typename Range, + typename Format, + typename... Args> + SCN_NODISCARD auto scan_localized(const Locale& loc, + Range&& r, + const Format& f, + Args&... a) + -> detail::scan_result_for_range<Range> + { + return detail::scan_boilerplate_localized(loc, std::forward<Range>(r), + f, a...); + } +#endif + + // value + + /** + * Scans a single value with the default options, returning it instead of + * using an output parameter. + * + * The parsed value is in `ret.value()`, if `ret == true`. + * The return type of this function is otherwise similar to other scanning + * functions. + * + * \code{.cpp} + * auto ret = scn::scan_value<int>("42"); + * if (ret) { + * // ret.value() == 42 + * } + * \endcode + */ +#if SCN_DOXYGEN + template <typename T, typename Range> + auto scan_value(Range&& r) + -> detail::generic_scan_result_for_range<expected<T>, Range>; +#else + template <typename T, typename Range> + SCN_NODISCARD auto scan_value(Range&& r) + -> detail::generic_scan_result_for_range<expected<T>, Range> + { + T value; + auto range = wrap(SCN_FWD(r)); + auto args = make_args_for(range, 1, value); + auto ret = vscan_default(SCN_MOVE(range), 1, {args}); + if (ret.err) { + return detail::wrap_result(expected<T>{value}, + detail::range_tag<Range>{}, + SCN_MOVE(ret.range)); + } + return detail::wrap_result(expected<T>{ret.err}, + detail::range_tag<Range>{}, + SCN_MOVE(ret.range)); + } +#endif + + // input + + /** + * Otherwise equivalent to \ref scan, expect reads from `stdin`. + * Character type is determined by the format string. + * Syncs with `<cstdio>`. + */ + template <typename Format, + typename... Args, + typename CharT = ranges::range_value_t<Format>> +#if SCN_DOXYGEN + auto input(const Format& f, Args&... a) + -> detail::scan_result_for_range<basic_file<CharT>&>; +#else + SCN_NODISCARD auto input(const Format& f, Args&... a) + -> detail::scan_result_for_range<basic_file<CharT>&> + { + auto& range = stdin_range<CharT>(); + auto ret = detail::scan_boilerplate(range, f, a...); + range.sync(); + ret.range().reset_begin_iterator(); + return ret; + } +#endif + + // prompt + + namespace detail { + inline void put_stdout(const char* str) + { + std::fputs(str, stdout); + } + inline void put_stdout(const wchar_t* str) + { + std::fputws(str, stdout); + } + } // namespace detail + + /** + * Equivalent to \ref input, except writes what's in `p` to `stdout`. + * + * \code{.cpp} + * int i{}; + * scn::prompt("What's your favorite number? ", "{}", i); + * // Equivalent to: + * // std::fputs("What's your favorite number? ", stdout); + * // scn::input("{}", i); + * \endcode + */ +#if SCN_DOXYGEN + template <typename CharT, typename Format, typename... Args> + auto prompt(const CharT* p, const Format& f, Args&... a) + -> detail::scan_result_for_range<basic_file<CharT>&>; +#else + template <typename CharT, typename Format, typename... Args> + SCN_NODISCARD auto prompt(const CharT* p, const Format& f, Args&... a) + -> detail::scan_result_for_range<basic_file<CharT>&> + { + SCN_EXPECT(p != nullptr); + detail::put_stdout(p); + + return input(f, a...); + } +#endif + + // parse_integer + + /** + * Parses an integer into \c val in base \c base from \c str. + * Returns a pointer past the last character read, or an error. + * + * @param str source, can't be empty, cannot have: + * - preceding whitespace + * - preceding \c "0x" or \c "0" (base is determined by the \c base + * parameter) + * - \c '+' sign (\c '-' is fine) + * @param val parsed integer, must be value-constructed + * @param base between [2,36] + */ +#if SCN_DOXYGEN + template <typename T, typename CharT> + expected<const CharT*> parse_integer(basic_string_view<CharT> str, + T& val, + int base = 10); +#else + template <typename T, typename CharT> + SCN_NODISCARD expected<const CharT*> + parse_integer(basic_string_view<CharT> str, T& val, int base = 10) + { + SCN_EXPECT(!str.empty()); + auto s = detail::simple_integer_scanner<T>{}; + SCN_CLANG_PUSH_IGNORE_UNDEFINED_TEMPLATE + auto ret = + s.scan_lower(span<const CharT>(str.data(), str.size()), val, base); + SCN_CLANG_POP_IGNORE_UNDEFINED_TEMPLATE + if (!ret) { + return ret.error(); + } + return {ret.value()}; + } +#endif + + /** + * Parses float into \c val from \c str. + * Returns a pointer past the last character read, or an error. + * + * @param str source, can't be empty + * @param val parsed float, must be value-constructed + */ +#if SCN_DOXYGEN + template <typename T, typename CharT> + expected<const CharT*> parse_float(basic_string_view<CharT> str, T& val); +#else + template <typename T, typename CharT> + SCN_NODISCARD expected<const CharT*> parse_float( + basic_string_view<CharT> str, + T& val) + { + SCN_EXPECT(!str.empty()); + auto s = detail::float_scanner_access<T>{}; + auto ret = s._read_float(val, make_span(str.data(), str.size()), + detail::ascii_widen<CharT>('.')); + if (!ret) { + return ret.error(); + } + return {str.data() + ret.value()}; + } +#endif + + /** + * A convenience function for creating scanners for user-provided types. + * + * Wraps \ref vscan_usertype + * + * Example use: + * + * \code{.cpp} + * // Type has two integers, and its textual representation is + * // "[val1, val2]" + * struct user_type { + * int val1; + * int val2; + * }; + * + * template <> + * struct scn::scanner<user_type> : public scn::empty_parser { + * template <typename Context> + * error scan(user_type& val, Context& ctx) + * { + * return scan_usertype(ctx, "[{}, {}]", val.val1, val.val2); + * } + * }; + * \endcode + * + * \param ctx Context given to the scanning function + * \param f Format string to parse + * \param a Member types (etc) to parse + */ +#if SCN_DOXYGEN + template <typename WrappedRange, typename Format, typename... Args> + error scan_usertype(basic_context<WrappedRange>& ctx, + const Format& f, + Args&... a); +#else + template <typename WrappedRange, typename Format, typename... Args> + SCN_NODISCARD error scan_usertype(basic_context<WrappedRange>& ctx, + const Format& f, + Args&... a) + { + static_assert(sizeof...(Args) > 0, + "Have to scan at least a single argument"); + + using char_type = typename WrappedRange::char_type; + auto args = make_args<basic_context<WrappedRange>, + basic_parse_context<char_type>>(a...); + return vscan_usertype(ctx, basic_string_view<char_type>(f), {args}); + } +#endif + + SCN_END_NAMESPACE +} // namespace scn + +#endif // SCN_DETAIL_SCAN_H diff --git a/src/third-party/scnlib/include/scn/scan/vscan.h b/src/third-party/scnlib/include/scn/scan/vscan.h new file mode 100644 index 0000000..86bf23e --- /dev/null +++ b/src/third-party/scnlib/include/scn/scan/vscan.h @@ -0,0 +1,208 @@ +// Copyright 2017 Elias Kosunen +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file is a part of scnlib: +// https://github.com/eliaskosunen/scnlib + +#ifndef SCN_SCAN_VSCAN_H +#define SCN_SCAN_VSCAN_H + +#include "../detail/context.h" +#include "../detail/file.h" +#include "../detail/parse_context.h" +#include "../detail/visitor.h" +#include "common.h" + +namespace scn { + SCN_BEGIN_NAMESPACE + + // Avoid documentation issues: without this, Doxygen will think + // SCN_BEGIN_NAMESPACE is a part of the vscan declaration + namespace dummy { + } + + /** + * Type returned by `vscan` and others + */ + template <typename WrappedRange> + struct vscan_result { + error err; + WrappedRange range; + }; + + namespace detail { + template <typename WrappedRange, + typename CharT = typename WrappedRange::char_type> + vscan_result<WrappedRange> vscan_boilerplate( + WrappedRange&& r, + basic_string_view<CharT> fmt, + basic_args<CharT> args) + { + auto ctx = make_context(SCN_MOVE(r)); + auto pctx = make_parse_context(fmt, ctx.locale()); + auto err = visit(ctx, pctx, SCN_MOVE(args)); + return {err, SCN_MOVE(ctx.range())}; + } + + template <typename WrappedRange, + typename CharT = typename WrappedRange::char_type> + vscan_result<WrappedRange> vscan_boilerplate_default( + WrappedRange&& r, + int n_args, + basic_args<CharT> args) + { + auto ctx = make_context(SCN_MOVE(r)); + auto pctx = make_parse_context(n_args, ctx.locale()); + auto err = visit(ctx, pctx, SCN_MOVE(args)); + return {err, SCN_MOVE(ctx.range())}; + } + + template <typename WrappedRange, + typename Format, + typename CharT = typename WrappedRange::char_type> + vscan_result<WrappedRange> vscan_boilerplate_localized( + WrappedRange&& r, + basic_locale_ref<CharT>&& loc, + const Format& fmt, + basic_args<CharT> args) + { + auto ctx = make_context(SCN_MOVE(r), SCN_MOVE(loc)); + auto pctx = make_parse_context(fmt, ctx.locale()); + auto err = visit(ctx, pctx, SCN_MOVE(args)); + return {err, SCN_MOVE(ctx.range())}; + } + } // namespace detail + + /** + * In the spirit of {fmt}/`std::format` and `vformat`, `vscan` behaves + * similarly to \ref scan, except instead of taking a variadic argument + * pack, it takes an object of type `basic_args`, which type-erases the + * arguments to scan. This, in effect, will decrease generated code size and + * compile times dramatically. + * + * \param range Source range that has been wrapped with `detail::wrap`, and + * passed in as an rvalue. + * \param fmt Format string to use + * \param args Type-erased values to read + */ + template <typename WrappedRange, + typename CharT = typename WrappedRange::char_type> + vscan_result<WrappedRange> vscan(WrappedRange range, + basic_string_view<CharT> fmt, + basic_args<CharT>&& args) + { + return detail::vscan_boilerplate(SCN_MOVE(range), fmt, SCN_MOVE(args)); + } + + /** + * To be used with `scan_default` + * + * \param range Source range that has been wrapped with `detail::wrap`, and + * passed in as an rvalue. + * \param n_args Number of arguments in args + * \param args Type-erased values to read + * + * \see vscan + */ + template <typename WrappedRange, + typename CharT = typename WrappedRange::char_type> + vscan_result<WrappedRange> vscan_default(WrappedRange range, + int n_args, + basic_args<CharT>&& args) + { + return detail::vscan_boilerplate_default(SCN_MOVE(range), n_args, + SCN_MOVE(args)); + } + + /** + * To be used with `scan_localized` + * + * \param loc Locale to use + * \param range Source range that has been wrapped with `detail::wrap`, and + * passed in as an rvalue. + * \param fmt Format string to use + * \param args Type-erased values to read + * + * \see vscan + */ + template <typename WrappedRange, + typename CharT = typename WrappedRange::char_type> + vscan_result<WrappedRange> vscan_localized(WrappedRange range, + basic_locale_ref<CharT>&& loc, + basic_string_view<CharT> fmt, + basic_args<CharT>&& args) + { + return detail::vscan_boilerplate_localized( + SCN_MOVE(range), SCN_MOVE(loc), fmt, SCN_MOVE(args)); + } + + /** + * \see scan_usertype + * \see vscan + */ + template <typename WrappedRange, + typename CharT = typename WrappedRange::char_type> + error vscan_usertype(basic_context<WrappedRange>& ctx, + basic_string_view<CharT> f, + basic_args<CharT>&& args) + { + auto pctx = make_parse_context(f, ctx.locale()); + return visit(ctx, pctx, SCN_MOVE(args)); + } + +#if !defined(SCN_HEADER_ONLY) || !SCN_HEADER_ONLY + +#define SCN_VSCAN_DECLARE(Range, WrappedAlias, CharAlias) \ + namespace detail { \ + namespace vscan_macro { \ + using WrappedAlias = range_wrapper_for_t<Range>; \ + using CharAlias = typename WrappedAlias::char_type; \ + } \ + } \ + vscan_result<detail::vscan_macro::WrappedAlias> vscan( \ + detail::vscan_macro::WrappedAlias&&, \ + basic_string_view<detail::vscan_macro::CharAlias>, \ + basic_args<detail::vscan_macro::CharAlias>&&); \ + \ + vscan_result<detail::vscan_macro::WrappedAlias> vscan_default( \ + detail::vscan_macro::WrappedAlias&&, int, \ + basic_args<detail::vscan_macro::CharAlias>&&); \ + \ + vscan_result<detail::vscan_macro::WrappedAlias> vscan_localized( \ + detail::vscan_macro::WrappedAlias&&, \ + basic_locale_ref<detail::vscan_macro::CharAlias>&&, \ + basic_string_view<detail::vscan_macro::CharAlias>, \ + basic_args<detail::vscan_macro::CharAlias>&&); \ + \ + error vscan_usertype(basic_context<detail::vscan_macro::WrappedAlias>&, \ + basic_string_view<detail::vscan_macro::CharAlias>, \ + basic_args<detail::vscan_macro::CharAlias>&&) + + SCN_VSCAN_DECLARE(string_view, string_view_wrapped, string_view_char); + SCN_VSCAN_DECLARE(wstring_view, wstring_view_wrapped, wstring_view_char); + SCN_VSCAN_DECLARE(std::string, string_wrapped, string_char); + SCN_VSCAN_DECLARE(std::wstring, wstring_wrapped, wstring_char); + SCN_VSCAN_DECLARE(file&, file_ref_wrapped, file_ref_char); + SCN_VSCAN_DECLARE(wfile&, wfile_ref_wrapped, wfile_ref_char); + +#endif // !SCN_HEADER_ONLY + + SCN_END_NAMESPACE +} // namespace scn + +#if defined(SCN_HEADER_ONLY) && SCN_HEADER_ONLY && !defined(SCN_VSCAN_CPP) +#include "vscan.cpp" +#endif + +#endif // SCN_SCAN_VSCAN_H |