// 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 * auto scan(Range&& r, string_view f, Args&... a) { * auto range = scn::wrap(std::forward(r)); * auto args = scn::make_args_for(range, f, a...); * auto ret = scn::vscan(std::move(range), f, {args}); * return scn::make_scan_result(std::move(ret)); * } * \endcode */ template auto make_scan_result(vscan_result result) -> detail::scan_result_for_range { return detail::wrap_result(Error{result.err}, detail::range_tag{}, SCN_MOVE(result.range)); } namespace detail { template auto scan_boilerplate(Range&& r, const Format& f, Args&... a) -> detail::scan_result_for_range { static_assert(sizeof...(Args) > 0, "Have to scan at least a single argument"); static_assert(SCN_CHECK_CONCEPT(ranges::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(SCN_MOVE(ret)); } template auto scan_boilerplate_default(Range&& r, Args&... a) -> detail::scan_result_for_range { static_assert(sizeof...(Args) > 0, "Have to scan at least a single argument"); static_assert(SCN_CHECK_CONCEPT(ranges::range), "Input needs to be a Range"); auto range = wrap(SCN_FWD(r)); auto format = static_cast(sizeof...(Args)); auto args = make_args_for(range, format, a...); auto ret = vscan_default(SCN_MOVE(range), format, {args}); return make_scan_result(SCN_MOVE(ret)); } template auto scan_boilerplate_localized(const Locale& loc, Range&& r, const Format& f, Args&... a) -> detail::scan_result_for_range { static_assert(sizeof...(Args) > 0, "Have to scan at least a single argument"); static_assert(SCN_CHECK_CONCEPT(ranges::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(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(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 auto scan(Range&& r, const Format& f, Args&... a) -> detail::scan_result_for_range; #else template SCN_NODISCARD auto scan(Range&& r, const Format& f, Args&... a) -> detail::scan_result_for_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 auto scan_default(Range&& r, Args&... a) -> detail::scan_result_for_range; #else template SCN_NODISCARD auto scan_default(Range&& r, Args&... a) -> detail::scan_result_for_range { return detail::scan_boilerplate_default(std::forward(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 ``. * * 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 auto scan_localized(const Locale& loc, Range&& r, const Format& f, Args&... a) -> detail::scan_result_for_range; #else template SCN_NODISCARD auto scan_localized(const Locale& loc, Range&& r, const Format& f, Args&... a) -> detail::scan_result_for_range { return detail::scan_boilerplate_localized(loc, std::forward(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("42"); * if (ret) { * // ret.value() == 42 * } * \endcode */ #if SCN_DOXYGEN template auto scan_value(Range&& r) -> detail::generic_scan_result_for_range, Range>; #else template SCN_NODISCARD auto scan_value(Range&& r) -> detail::generic_scan_result_for_range, 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{value}, detail::range_tag{}, SCN_MOVE(ret.range)); } return detail::wrap_result(expected{ret.err}, detail::range_tag{}, 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 ``. */ template > #if SCN_DOXYGEN auto input(const Format& f, Args&... a) -> detail::scan_result_for_range&>; #else SCN_NODISCARD auto input(const Format& f, Args&... a) -> detail::scan_result_for_range&> { auto& range = stdin_range(); 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 auto prompt(const CharT* p, const Format& f, Args&... a) -> detail::scan_result_for_range&>; #else template SCN_NODISCARD auto prompt(const CharT* p, const Format& f, Args&... a) -> detail::scan_result_for_range&> { 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 expected parse_integer(basic_string_view str, T& val, int base = 10); #else template SCN_NODISCARD expected parse_integer(basic_string_view str, T& val, int base = 10) { SCN_EXPECT(!str.empty()); auto s = detail::simple_integer_scanner{}; SCN_CLANG_PUSH_IGNORE_UNDEFINED_TEMPLATE auto ret = s.scan_lower(span(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 expected parse_float(basic_string_view str, T& val); #else template SCN_NODISCARD expected parse_float( basic_string_view str, T& val) { SCN_EXPECT(!str.empty()); auto s = detail::float_scanner_access{}; auto ret = s._read_float(val, make_span(str.data(), str.size()), detail::ascii_widen('.')); 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 : public scn::empty_parser { * template * 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 error scan_usertype(basic_context& ctx, const Format& f, Args&... a); #else template SCN_NODISCARD error scan_usertype(basic_context& 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_parse_context>(a...); return vscan_usertype(ctx, basic_string_view(f), {args}); } #endif SCN_END_NAMESPACE } // namespace scn #endif // SCN_DETAIL_SCAN_H