API Documentation

Scanning functions

Main part of the public API.

Generally, the functions in this group take a range, a format string, and a list of arguments. The arguments are parsed from the range based on the information given in the format string.

If the function takes a format string and a range, they must share character types. Also, the format string must be convertible to basic_string_view<CharT>, where CharT is that aforementioned character type.

template<typename Range, typename Format, typename ...Args>
auto scn::scan(Range &&r, const Format &f, Args&... a) -> detail::scan_result_for_range<Range>

The most fundamental part of the scanning API.

Reads from the range in r according to the format string f.

int i;
scn::scan("123", "{}", i);
// i == 123

template<typename Range, typename ...Args>
auto scn::scan_default(Range &&r, Args&... a) -> detail::scan_result_for_range<Range>

Equivalent to 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 scan

int i;
scn::scan_default("123", i);
// i == 123

See also

scan

template<typename Locale, typename Range, typename Format, typename ...Args>
auto scn::scan_localized(const Locale &loc, Range &&r, const Format &f, Args&... a) -> detail::scan_result_for_range<Range>

Read from the range in r using the locale in loc.

loc must be a 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.

double d;
scn::scan_localized(std::locale{"fi_FI"}, "3,14", "{}", d);
// d == 3.14

See also

scan

template<typename T, typename Range>
auto scn::scan_value(Range &&r) -> detail::generic_scan_result_for_range<expected<T>, Range>

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.

auto ret = scn::scan_value<int>("42");
if (ret) {
  // ret.value() == 42
}

template<typename Format, typename ...Args, typename CharT = ranges::range_value_t<Format>>
auto scn::input(const Format &f, Args&... a) -> detail::scan_result_for_range<basic_file<CharT>&>

Otherwise equivalent to scan, expect reads from stdin.

Character type is determined by the format string. Syncs with <cstdio>.

template<typename CharT, typename Format, typename ...Args>
auto scn::prompt(const CharT *p, const Format &f, Args&... a) -> detail::scan_result_for_range<basic_file<CharT>&>

Equivalent to input, except writes what’s in p to stdout.

int i{};
scn::prompt("What's your favorite number? ", "{}", i);
// Equivalent to:
//   std::fputs("What's your favorite number? ", stdout);
//   scn::input("{}", i);

template<typename Range, typename String, typename Until>
auto scn::getline(Range &&r, String &str, Until until) -> detail::scan_result_for_range<Range>

Read the range in r into str until until is found.

until will be skipped in parsing: it will not be pushed into 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 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.

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

template<typename Range, typename String, typename CharT = typename detail::extract_char_type<ranges::iterator_t<range_wrapper_for_t<Range>>>::type>
auto scn::getline(Range &&r, String &str) -> detail::scan_result_for_range<Range>

Equivalent to getline with the last parameter set to '\n' with the appropriate character type.

In other words, reads r into str until '\n' is found.

The character type is determined by r.

template<typename Range, typename Until>
auto scn::ignore_until(Range &&r, Until until) -> detail::scan_result_for_range<Range>

Advances the beginning of r until until is found.

template<typename Range, typename Until>
auto scn::ignore_until_n(Range &&r, ranges::range_difference_t<Range> n, Until until) -> detail::scan_result_for_range<Range>

Advances the beginning of r until until is found, or the beginning has been advanced n times.

Until can be the r character type (char or wchar_t), or code_point.

Source range

Various kinds of ranges can be passed to scanning functions.

Fundamentally, a range is something that has a beginning and an end. Examples of ranges are a string literal, a C-style array, and a std::vector. All of these can be passed to std::begin and std::end, which then return an iterator to the range. This notion of ranges was standardized in C++20 with the Ranges TS. This library provides barebone support of this functionality.

Source range requirements

Ranges passed to scanning functions must be:
  • bidirectional

  • default and move constructible

Using C++20 concepts:

template <typename Range>
concept scannable_range =
    std::ranges::bidirectional_range<Range> &&
    std::default_constructible<Range> &&
    std::move_constructible<Range>;

A bidirectional range is a range, the iterator type of which is bidirectional: http://eel.is/c++draft/iterator.concepts#iterator.concept.bidir. Bidirectionality means, that the iterator can be moved both forwards: ++it and backwards --it.

Note, that both random-access and contiguous ranges are refinements of bidirectional ranges, and can be passed to the library. In fact, the library implements various optimizations for contiguous ranges.

Character type

The range has an associated character type. This character type can be either char or wchar_t. The character type is determined by the result of operator* of the range iterator. If dereferencing the iterator returns

  • char or wchar_t: the character type is char or wchar_t, respectively

  • expected<char> or expected<wchar_t>: the character type is char or wchar_t, respectively

Note on string literals

Please note, that only string literals are ranges (const char(&)[N]), not pointers to a constant character (const char*). This is because:

  • It’s impossible to differentiate if a const char* is a null-terminated string, a pointer to a single char, or a pointer to an array of char. For safety reasons, const char* is thus not an allowed source range type.

  • It’s how ranges in the standard are defined: a const char* cannot be passed to std::ranges::begin or std::ranges::end (it doesn’t have a clear beginning or an end, for the reason explained above), so it’s not even a range to begin with.

Therefore, this code is allowed, as it uses a string literal (const char(&)[N]) as the source range type:

int i;
scn::scan_default("123", i);

But this code isn’t, as the source range type used is not a range, but a pointer to constant character (const char*):

const char* source = "123";
int i;
scn::scan_default(source, i); // compiler error

This issue can be avoided by using a string_view:

const char* source = "123";
int i;
scn::scan_default(scn::string_view{source}, i);
// std::string_view would also work

Range wrapper

template<typename Range>
class scn::detail::range_wrapper

Wraps a source range for more consistent behavior.

Unnamed Group

template<typename R = range_nocvref_type, typename std::enable_if<SCN_CHECK_CONCEPT(ranges::sized_range<R>)>::type* = nullptr>
inline void advance_to(iterator it) noexcept

Advance the begin iterator, until it’s equal to it.

Assumes that it is reachable by repeatedly incrementing begin, will hang otherwise.

Public Functions

inline bool empty() const

Returns true if begin() == end().

inline iterator advance(difference_type n = 1) noexcept

Advance the begin iterator by n characters.

inline iterator begin_underlying () const noexcept(noexcept(ranges::cbegin(SCN_DECLVAL(const range_nocvref_type &))))

Returns the begin iterator of the underlying source range, is not necessarily equal to begin().

inline const range_type &range_underlying() const noexcept

Returns the underlying source range.

Note that range_underlying().begin() may not be equal to begin().

template<typename R = range_nocvref_type, typename std::enable_if<SCN_CHECK_CONCEPT(ranges::contiguous_range<R>)>::type* = nullptr>
inline auto data() const noexcept(noexcept(*SCN_DECLVAL(ranges::iterator_t<const R>))) -> decltype(std::addressof(*SCN_DECLVAL(ranges::iterator_t<const R>)))

Returns a pointer to the beginning of the range.

*this must be contiguous.

template<typename R = range_nocvref_type, typename std::enable_if<SCN_CHECK_CONCEPT(ranges::sized_range<R>)>::type* = nullptr>
inline auto size() const noexcept(noexcept(ranges::distance(SCN_DECLVAL(ranges::iterator_t<const R>), SCN_DECLVAL(ranges::sentinel_t<const R>)))) -> decltype(ranges::distance(SCN_DECLVAL(ranges::iterator_t<const R>), SCN_DECLVAL(ranges::sentinel_t<const R>)))

Returns end() - begin().

*this must be sized.

inline error reset_to_rollback_point()

Reset begin() to the rollback point, as if by repeatedly calling operator--() on the begin iterator.

Returns error::unrecoverable_source_error on failure.

inline void set_rollback_point()

Sets the rollback point equal to the current begin() iterator.

template<typename R>
inline auto reconstruct_and_rewrap() && -> range_wrapper<R>

Construct a new source range from begin() and end(), and wrap it in a new range_wrapper.

Public Static Attributes

static constexpr bool is_direct = is_direct_impl<range_nocvref_type>::value

true if value_type is a character type (char or wchar_t) false if it’s an expected containing a character

static constexpr bool is_contiguous = SCN_CHECK_CONCEPT(ranges::contiguous_range<range_nocvref_type>)

true if this->data() can be called, and memcpy can be performed on it.

static constexpr bool provides_buffer_access = provides_buffer_access_impl<range_nocvref_type>::value

true if the range provides a way to access a contiguous buffer on it, which may not provide the entire source data, e.g.

a span of spans (vectored I/O).

Unimplemented, TODO

struct dummy

Return type

The return type of the scanning functions is based on the type of the given range. It contains an object of that range type, representing what was left over of the range after scanning. The type is designed in such a way as to minimize copying and dynamic memory allocations. The type also contains an error value.

struct scn::wrapped_error

Base class for the result type returned by most scanning functions (except for scan_value).

scn::detail::scan_result_base inherits either from this class or expected.

Public Functions

inline SCN_NODISCARD::scn::error error() const

Get underlying error.

inline explicit operator bool() const

Did the operation succeed &#8212; true means success.

template<typename WrappedRange, typename Base>
class scn::detail::scan_result_base : public scn::detail::scan_result_base_wrapper<Base>

Type returned by scanning functions.

Contains an error (inherits from it: for error, that’s wrapped_error; with scan_value, inherits from expected), and the leftover range after scanning.

The leftover range may reference the range given to the scanning function. Please take the necessary measures to make sure that the original range outlives the leftover range. Alternatively, if possible for your specific range type, call the reconstruct() member function to get a new, independent range.

Subclassed by scn::detail::intermediary_scan_result< WrappedRange, Base >

Public Functions

inline iterator begin() const noexcept

Beginning of the leftover range.

inline sentinel end() const noexcept(noexcept(SCN_DECLVAL(wrapped_range_type).end()))

End of the leftover range.

inline bool empty() const noexcept(noexcept(SCN_DECLVAL(wrapped_range_type).end()))

Whether the leftover range is empty.

inline ranges::subrange<iterator, sentinel> subrange() const

A subrange pointing to the leftover range.

inline wrapped_range_type &range() &

Leftover range.

If the leftover range is used to scan a new value, this member function should be used.

See also

range_wrapper

inline const wrapped_range_type &range() const &

Leftover range.

If the leftover range is used to scan a new value, this member function should be used.

See also

range_wrapper

inline wrapped_range_type range() &&

Leftover range.

If the leftover range is used to scan a new value, this member function should be used.

See also

range_wrapper

template<typename R = wrapped_range_type, typename = typename std::enable_if<R::is_contiguous>::type>
inline basic_string_view<char_type> range_as_string_view() const

Return a view into the leftover range as a string_view.

Operations done to the leftover range after a call to this may cause issues with iterator invalidation. The returned range will reference to the leftover range, so be wary of use-after-free-problems.

template<typename R = wrapped_range_type, typename = typename std::enable_if<R::is_contiguous>::type>
inline span<const char_type> range_as_span() const

Return a view into the leftover range as a span.

Operations done to the leftover range after a call to this may cause issues with iterator invalidation. The returned range will reference to the leftover range, so be wary of use-after-free-problems.

template<typename R = wrapped_range_type, typename = typename std::enable_if<R::is_contiguous>::type>
inline std::basic_string<char_type> range_as_string() const

Return the leftover range as a string.

The contents are copied into the string, so using this will not lead to lifetime issues.

template<typename R = typename WrappedRange::range_type>
R reconstruct() const

Reconstructs a range of the original type, independent of the leftover range, beginning from begin and ending in end.

Compiles only if range is reconstructible.

Note, that the values scanned are only touched iff the scanning succeeded, i.e. operator bool() returns true. This means, that reading from a default-constructed value of a built-in type on error will cause UB:

int i;
auto ret = scn::scan("foo", "{}", i);
// ret == false
// i is still default-constructed -- reading from it is UB

Error types

class scn::error

Error class.

Used as a return value for functions without a success value.

Public Types

enum code

Error code.

Values:

enumerator good

No error.

enumerator end_of_range

EOF.

enumerator invalid_format_string

Format string was invalid.

enumerator invalid_scanned_value

Scanned value was invalid for given type.

e.g. a period ‘.’ when scanning for an int

enumerator invalid_operation

Stream does not support the performed operation.

enumerator value_out_of_range

Scanned value was out of range for the desired type.

(e.g. >2^32 for an uint32_t)

enumerator invalid_argument

Invalid argument given to operation.

enumerator invalid_encoding

Source range has invalid (utf-8 or utf-16) encoding.

enumerator exceptions_required

This operation is only possible with exceptions enabled.

enumerator source_error

The source range emitted an error.

enumerator unrecoverable_source_error

The source range emitted an error that cannot be recovered from.

The stream is now unusable.

enumerator unrecoverable_internal_error
enumerator max_error

Public Functions

inline explicit constexpr operator bool() const noexcept

Evaluated to true if there was no error.

inline SCN_NODISCARD constexpr enum code code () const noexcept

Get error code.

inline SCN_NODISCARD constexpr bool is_recoverable () const noexcept

Returns true if, after this error, the state of the given input range is consistent, and thus, the range can be used for new scanning operations.

struct success_tag_t
template<typename T, typename Error = ::scn::error, typename Enable = void>
class expected

expected-like type.

For situations where there can be a value in case of success or an error code.

Lists

template<typename Range, typename Container>
auto scn::scan_list(Range &&r, Container &c) -> detail::scan_result_for_range<Range>

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 span_list_wrapper. make_span_list_wrapper

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"

Parameters
  • r – Range to read from

  • 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.

template<typename Range, typename Container, typename CharT>
auto scn::scan_list_ex(Range &&r, Container &c, scan_list_options<CharT> options) -> detail::scan_result_for_range<Range>

Otherwise equivalent to scan_list(), except can react to additional characters, based on options.

See scan_list_options for more information.

std::vector<int> vec{};
auto result = scn::scan_list_ex("123, 456", vec,
                                scn::list_separator(','));
// vec == [123, 456]
// result.empty() == true

See also

scan_list

Parameters
  • r – Range to scan from

  • c – Container to write read values into

  • options – Options to use

template<typename Locale, typename Range, typename Container, typename CharT>
auto scn::scan_list_localized(const Locale &loc, Range &&r, Container &c, scan_list_options<CharT> options) -> detail::scan_result_for_range<Range>

Otherwise equivalent to scan_list_ex(), except uses loc to scan the values.

See also

scan_list_ex()

See also

scan_localized()

Parameters
  • loc – Locale to use for scanning. Must be a std::locale.

  • r – Range to scan from

  • c – Container to write read values into

  • options – Options to use

template<typename CharT>
struct scn::scan_list_options

Used to customize scan_list_ex().

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 Parameters

CharT – Can be a code unit type (char or wchar_t, depending on the source range), or code_point.

Public Members

optional<CharT> separator = {}

If set, up to one separator character can be accepted between values, which may be surrounded by whitespace.

optional<CharT> until = {}

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.

template<typename CharT>
scan_list_options<CharT> scn::list_separator(CharT ch)

Create a scan_list_options for scan_list_ex, by using ch as the separator character.

template<typename CharT>
scan_list_options<CharT> scn::list_until(CharT ch)

Create a scan_list_options for scan_list_ex, by using ch as the until-character.

template<typename CharT>
scan_list_options<CharT> scn::list_separator_and_until(CharT sep, CharT until)

Create a scan_list_options for scan_list_ex, by using sep as the separator, and until as the until-character.

Convenience scan types

These types can be passed to scanning functions (scn::scan and alike) as arguments, providing useful functionality.

template<typename T>
struct temporary

Allows reading an rvalue.

Stores an rvalue and returns an lvalue reference to it via operator(). Create one with temp.

template<typename T, typename std::enable_if<!std::is_lvalue_reference<T>::value>::type* = nullptr>
temporary<T> scn::temp(T &&val)

Factory function for temporary.

Canonical use case is with scn::span:

std::vector<char> buffer(32, '\0');
auto result = scn::scan("123", "{}", scn::temp(scn::make_span(buffer)));
// buffer == "123"

template<typename T>
discard_type<T> &scn::discard()

Scans an instance of T, but doesn’t store it anywhere.

Uses scn::temp internally, so the user doesn’t have to bother.

int i{};
// 123 is discarded, 456 is read into `i`
auto result = scn::scan("123 456", "{} {}", scn::discard<T>(), i);
// result == true
// i == 456

template<typename T>
struct span_list_wrapper

Adapts a span into a type that can be read into using scan_list.

This way, potentially unnecessary dynamic memory allocations can be avoided. To use as a parameter to scan_list, use make_span_list_wrapper.

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

See also

scan_list

template<typename T>
auto scn::make_span_list_wrapper(T &s) -> temporary<detail::span_list_wrapper_for<T>>

Adapts a contiguous buffer into a type containing a span that can be read into using scn::scan_list.

Example adapted from span_list_wrapper:

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

See also

scan_list

Format string

Every value to be scanned from the source range is marked with a pair of curly braces "{}" in the format string. Inside these braces, additional options can be specified. The syntax is not dissimilar from the one found in fmtlib.

The information inside the braces consist of two parts: the index and the scanning options, separated by a colon ':'.

The index part can either be empty, or be an integer. If the index is specified for one of the arguments, it must be set for all of them. The index tells the library which argument the braces correspond to.

int i;
std::string str;
scn::scan(range, "{1} {0}", i, str);
// Reads from the range in the order of:
//   string, whitespace, integer
// That's because the first format string braces have index '1', pointing to
// the second passed argument (indices start from 0), which is a string

After the index comes a colon and the scanning options. The colon only has to be there if any scanning options are specified.

The format of the format specifiers are as follows. [foo] means an optional flag.

[[fill]align][width][L][type]

Fill and align

Values to be parsed can be aligned in the source, using fill characters. For example, an integer could be aligned in a field using asterisks, like "****42****". This can be parsed without extra trickery, by specifying the fill character *, and center alignment ^:

int i;
auto ret = scn::scan("*****42*****", "{:*^}", i);
// i == 42
// ret.empty() == true

The fill character can be any character other than }. If no fill character is given, a space is used.

Supported alignment options are:

  • <: for left-alignment (value is in the left of the field, fill characters come after it)

  • >: for right-alignment

  • ^: for center-alignment – fill characters both before and after the value.

Width

Width specifies the maximum number of characters (= code units) that can be read from the source stream. Width can be any unsigned integer.

std::string str{};
auto ret = scn::scan("abcde", "{:3}", s);
// str == "123"
// ret.range() == "45"

Localized

Specifying the L-flag will cause the library to use localized scanning for this value. If a locale was passed to the scanning function (for example, with scn::scan_localized), it will be used. Otherwise, the global C++ locale will be used (std::locale{}, set with std::locale::global()).

double d{};

// uses global locale ("C", because std::locale::global() hasn't been called)
auto ret = scn::scan("3.14", "{:L}", d);
// ret.empty() == true
// d == 3.14

// uses the passed-in locale
ret = scn::scan_localized(std::locale{"fi_FI.UTF-8"}, "3,14", "{:L}", d);
// ret.empty() == true
// d == 3.14

Itself, the L flag has an effect with floats, where it affects the accepted decimal separator. In conjunction with other flags (n and ') it can have additional effects.

Type

The type flag can be used to further specify how the value is to be parsed, what values are accepted, and how they are interpreted. The accepted flags and their meanings depend on the actual type to be scanned.

Type: int

For integral types (short, int, long, long long + unsigned versions), the flags are organized in three categories. Up to one from each category can be present in the format string.

First category:

  • b: Binary, optional prefix 0b or 0B accepted

  • Bnn: (B followed by two digits): custom base, n in range 2-36 (inclusive), no base prefix accepted

  • d: Decimal, no base prefix accepted

  • i: Integer, detect base by prefix: 0b/0B for binary, 0o/0O/0 for octal, 0x/0X for hex, decimal otherwise

  • u: Unsigned decimal, no sign or base prefix accepted (even for signed types)

  • o: Octal, optional prefix 0o, 0O or 0 accepted

  • x: Hex, optional prefix 0x or 0X accepted

  • (default): d

Second category:

  • n: Accept digits as specified by the supplied locale, implies L

  • (default): Only digits [0-9] are accepted, no custom digits

Third category:

  • ': Accept thousands separators: default to ,, use locale if L set

  • (default): Only digits [0-9] are accepted, no thousands separator

Type: float

For floats (float, double and long double), there are also three categories, where up to one from each category can be present in the format string.

First category:

  • a and A: Hex float accepted (e.g. 0x1f.0p2)

  • e and E: Scientific format accepted (123.456e2)

  • f and F: Fixed-precision format accepted (123.456)

  • g and G: General format (implies e AND f)

  • (default): Accept all (implies e AND f AND a)

Second category:

  • n: Accept digits as specified by the supplied locale, implies L

  • (default): Only digits [0-9] are accepted, no custom digits

Third category:

  • ': Accept thousands separators: default to ,, use locale if L set

  • (default): Only digits [0-9] are accepted, no thousands separator

Type: string

For strings (std::basic_string, scn::/std::basic_string_view, scn::span), the supported options are as follows:

  • s: Accept any non-whitespace characters (if L is set, use the supplied locale, otherwise use std::isspace with "C" locale). Skips leading whitespace.

  • [ set ]: Accept any characters as specified by _set_, further information below. Does not skip leading whitespace.

  • (default): s

set can consist of literal characters ([abc] only accepts a, b, and c), ranges of literal characters ([a-z] only accepts characters from a to z), or specifiers, that are detailed in the table below.

Literals can also be specified as hex values:

  • \xnn: \x followed by two hexadecimal digits -> hex value

  • \unnnn: \u followed by four hexadecimal digits -> Unicode code point

  • \Unnnnnnnn: \U followed by eight hexadecimal digits -> Unicode code point (max value 0x10FFFF)

Specifiers

Specifier

Description

Accepted characters

:all:

All characters

true

:alnum:

Alphanumeric characters

std::isalnum

:alpha:

Letters

std::isalpha

:blank:

Blank characters

std::isblank (space and \t)

:cntrl:

Control characters

std::iscntrl (0x0 to 0x1f, and 0x7f)

:digit:

Digits

std::isdigit

:graph:

“Graphic” characters

std::isgraph

:lower:

Lowercase letters

std::islower

:print:

Printable characters

std::isprint (std::isgraph + space)

:punct:

Punctuation characters

std::ispunct

:space:

Whitespace characters

std::isspace

:upper:

Uppercase letters

std::isupper

:xdigit:

Hexadecimal digits

std::isxdigit

\l

Letters

std::isalpha (= :alpha:)

\w

Letters, numbers, and underscore

std::isalnum + _ (= :alnum: + _)

\s

Whitespace

std::isspace (= :space:)

\d

Digits

std::isdigit (= :digit:)

\l, \w, \s and \d can be inverted with capitalization: \L, \W, \S and \D, respectively.

If the first character in the set is ^, all options are inverted.

\:, \], \\^, \\ specify literal :, ], ^, and \, respectively.

- ranges accept any value numerically between the two ends, e.g. [A-z] accepts every ascii value between 0x41 and 0x7a, including characters like [, \, and ]. [a-Z] is an error, because the range end must be greater or equal to its beginning.

If the L flag is used, the supplied locale is used. If not, the <cctype> detailed in the above table is used, with the "C" locale.

Example format strings

Format string

Accepted characters

"{:[abc]}"

a, b and c

"{:[a-z]}"

std::islower

"{:[a-z-]}"

std::islower + -

"{:[a-z\n]}" (1)

std::islower + \n

"{:[a-z\\\\]}" (2)

std::islower + \

"{:[a-zA-Z0-9]}"

std::isalnum

"{:[^a-zA-Z0-9]}"

!std::isalnum

"{:[:alnum:]}"

std::isalnum

"{:[^:alnum:]}"

!std::isalnum

"{:[\\w]}" (3)

std::isalnum + _

"{:[^\\w]}"

NOT std::isalnum) + _

"{:[\\W]}"

NOT std::isalnum + _

"{:[^\\W]}"

std::isalnum + _

(1): \n means literal line break 0x0a

(2): Note the quadruple backslash: \\\\ is turned into [0x5c 0x5c] (two backslash characters) in the actual string by C++, which, in turn, is interpreted as an escaped backslash by scnlib. Just a double backslash \\] would lead to the closing square parenthesis being escaped, and the format string being invalid.

(3): Same as above: \\w means the format string specifier \w, literal \w would be an invalid C++ escape sequence.

Type: bool

Any number of flags accepted

  • s: Accept string values (true and false AND possible locale values, if using L)

  • i: Accept int values (0 and 1)

  • n: i, except accepts localized digits, implies L

  • (default): s + i: Accept 0, 1, true, false, and possible locale string values if using L

Type: char and code_point

Only flag c accepted, does not affect behavior.

Note, that with chars and code_point, leading whitespace is not skipped.

Whitespace

Any amount of whitespace in the format string tells the library to skip until the next non-whitespace character is found from the range. Not finding any whitespace from the range is not an error.

Literal characters

To scan literal characters and immediately discard them, just write the characters in the format string. To read literal { or }, write {{ or }}, respectively.

std::string bar;
scn::scan("foobar", "foo{}", bar);
// bar == "bar"

Semantics of scanning a value

In the beginning, with every scn::scan (or similar) call, the library wraps the given range in a scn::detail::range_wrapper, using scn::wrap. This wrapper provides an uniform interface and lifetime semantics over all possible ranges. The arguments to scan are wrapped in a scn::arg_store. These are then passed, alongside the format string, to scn::vscan (or similar).

The appropriate context and parse context types are then constructed based on these values, the format string, and the requested locale, and scn::visit is called. There, the library calls begin() on the range, getting an iterator. This iterator is advanced until a non-whitespace character is found.

After that, the format string is scanned character-by-character, until an unescaped '{' is found, after which the part after the '{' is parsed, until a ':' or '}' is found. If the parser finds an argument id, the argument with that id is fetched from the argument list, otherwise the next argument is used.

The parse() member function of the appropriate scn::scanner specialization is called, which parses the parsing options-part of the format string argument, setting the member variables of the scn::scanner specialization to their appropriate values.

After that, the scan() member function is called. It reads the range, starting from the aforementioned iterator, into a buffer until the next whitespace character is found (except for char/wchar_t: just a single character is read; and for span: span.size() characters are read). That buffer is then parsed with the appropriate algorithm.

If some of the characters in the buffer were not used, these characters are put back to the range, meaning that operator-- is called on the iterator.

Because how the range is read until a whitespace character, and how the unused part of the buffer is simply put back to the range, some interesting situations may arise. Please note, that the following behavior is consistent with both scanf and <iostream>.

// chars do not skip leading whitespace by default
// strings do
char c;
std::string str;

// No whitespace character after first {}, no range whitespace is skipped
scn::scan("abc", "{}{}", c, str);
// c == 'a'
// str == "bc"

// Not finding whitespace to skip from the range when whitespace is found in
// the format string isn't an error
scn::scan("abc", "{} {}", c, str);
// c == 'a'
// str == "bc"

// string scanners skip leading whitespace
scn::scan("a bc", "{}{}", c, str);
// c == 'a'
// str == "bc"

// char scanners do not
scn::scan("ab c", "{}{}", str, c);
// str == "ab"
// c == ' '

// Nothing surprising
scn::scan("a bc", "{} {}", c, str);
// c == 'a'
// str == "bc"

Using scn::scan_default is equivalent to using "{}" in the format string as many times as there are arguments, separated by whitespace.

scn::scan_default(range, a, b);
// Equivalent to:
// scn::scan(range, "{} {}", a, b);

Files

template<typename CharT>
class scn::basic_file

Range mapping to a C FILE*.

Not copyable or reconstructible.

Subclassed by scn::basic_owning_file< CharT >

Public Functions

basic_file() = default

Construct an empty file.

Reading not possible: valid() is false

inline basic_file(FILE *f)

Construct from a FILE*.

Must be a valid handle that can be read from.

inline FILE *handle() const

Get the FILE* for this range.

Only use this handle for reading sync() has been called and no reading operations have taken place after that.

See also

sync

inline FILE *set_handle(FILE *f, bool allow_sync = true) noexcept

Reset the file handle.

Calls sync(), if necessary, before resetting.

Returns

The old handle

inline constexpr bool valid() const noexcept

Whether the file has been opened.

inline void sync() noexcept

Synchronizes this file with the underlying FILE*.

Invalidates all non-end iterators. File must be open.

Necessary for mixing-and-matching scnlib and <cstdio>:

scn::scan(file, ...);
file.sync();
std::fscanf(file.handle(), ...);

Necessary for synchronizing result objects:

auto result = scn::scan(file, ...);
// only result.range() can now be used for scanning
result = scn::scan(result.range(), ...);
// .sync() allows the original file to also be used
file.sync();
result = scn::scan(file, ...);

class iterator
template<typename CharT>
class scn::basic_owning_file : public scn::basic_file<CharT>

A child class for basic_file, handling fopen, fclose, and lifetimes with RAII.

Public Functions

basic_owning_file() = default

Open an empty file.

inline basic_owning_file(const char *f, const char *mode)

Open a file, with fopen arguments.

inline explicit basic_owning_file(FILE *f)

Steal ownership of a FILE*.

inline bool open(const char *f, const char *mode)

fopen

inline bool open(FILE *f)

Steal ownership.

inline void close()

Close file.

inline SCN_NODISCARD bool is_open () const

Is the file open.

template<typename CharT>
class scn::basic_mapped_file : public scn::detail::byte_mapped_file

Memory-mapped file range.

Manages the lifetime of the mapping itself.

Public Functions

basic_mapped_file() = default

Constructs an empty mapping.

inline explicit basic_mapped_file(const char *f)

Constructs a mapping to a filename.

inline span<const CharT> buffer() const

Mapping data.

using scn::file = basic_file<char>
using scn::wfile = basic_file<wchar_t>
using scn::owning_file = basic_owning_file<char>
using scn::owning_wfile = basic_owning_file<wchar_t>
using scn::mapped_file = basic_mapped_file<char>
using scn::mapped_wfile = basic_mapped_file<wchar_t>
template<typename CharT>
basic_file<CharT> &scn::stdin_range()

Get a reference to the global stdin range.

inline file &scn::cstdin()

Get a reference to the global char-oriented stdin range.

inline wfile &scn::wcstdin()

Get a reference to the global wchar_t-oriented stdin range.

Lower level parsing and scanning operations

template<typename OriginalRange, typename Error = wrapped_error, typename WrappedRange>
auto scn::make_scan_result(vscan_result<WrappedRange> result) -> detail::scan_result_for_range<OriginalRange>

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));
}

Template Parameters

OriginalRange – The type of the range passed to the scanning function

Parameters

result – Return value of vscan

Returns

Result object

template<typename WrappedRange, typename Format, typename ...Args, typename CharT = typename WrappedRange::char_type>
arg_store<CharT, Args...> scn::make_args_for(WrappedRange&, Format, Args&... args)

vscan

template<typename WrappedRange, typename CharT = typename WrappedRange::char_type>
vscan_result<WrappedRange> scn::vscan(WrappedRange range, basic_string_view<CharT> fmt, basic_args<CharT> &&args)

In the spirit of {fmt}/std::format and vformat, vscan behaves similarly to 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.

Parameters
  • range – Source range that has been wrapped with detail::wrap, and passed in as an rvalue.

  • fmt – Format string to use

  • args – Type-erased values to read

template<typename WrappedRange, typename CharT = typename WrappedRange::char_type>
vscan_result<WrappedRange> scn::vscan_default(WrappedRange range, int n_args, basic_args<CharT> &&args)

To be used with scan_default

See also

vscan

Parameters
  • range – Source range that has been wrapped with detail::wrap, and passed in as an rvalue.

  • n_args – Number of arguments in args

  • args – Type-erased values to read

template<typename WrappedRange, typename CharT = typename WrappedRange::char_type>
vscan_result<WrappedRange> scn::vscan_localized(WrappedRange range, basic_locale_ref<CharT> &&loc, basic_string_view<CharT> fmt, basic_args<CharT> &&args)

To be used with scan_localized

See also

vscan

Parameters
  • loc – Locale to use

  • range – Source range that has been wrapped with detail::wrap, and passed in as an rvalue.

  • fmt – Format string to use

  • args – Type-erased values to read

Warning

doxygenstruct: Cannot find class “vscan_result” in doxygen xml output for project “scnlib” from directory: build/xml

template<typename Context, typename ParseCtx>
error scn::visit(Context &ctx, ParseCtx &pctx, basic_args<typename Context::char_type> args)

Low-level parsing

parse_integer and parse_float will provide super-fast parsing from a string, at the expense of some safety and usability guarantees. Using these functions can easily lead to unexpected behavior or UB if not used correctly and proper precautions are not taken.

template<typename T, typename CharT>
expected<const CharT*> scn::parse_integer(basic_string_view<CharT> str, T &val, int base = 10)

Parses an integer into val in base base from str.

Returns a pointer past the last character read, or an error.

Parameters
  • str – source, can’t be empty, cannot have:

    • preceding whitespace

    • preceding "0x" or "0" (base is determined by the base parameter)

    • '+' sign ('-' is fine)

  • val – parsed integer, must be value-constructed

  • base – between [2,36]

template<typename T, typename CharT>
expected<const CharT*> scn::parse_float(basic_string_view<CharT> str, T &val)

Parses float into val from str.

Returns a pointer past the last character read, or an error.

Parameters
  • str – source, can’t be empty

  • val – parsed float, must be value-constructed

Scanner

Values are eventually scanned using a scn::scanner.

struct scn::parser_base

Base class for all scanners.

User-defined scanner must derive from this type.

Subclassed by scn::common_parser, scn::empty_parser

Public Static Functions

static inline constexpr bool skip_preceding_whitespace()

Returns true if skip_range_whitespace() is to be called before scanning this value.

Defaults to true. Is false for chars, code points and strings when using set scanning.

static inline constexpr bool support_align_and_fill()

Returns true if this scanner supports parsing align and fill specifiers from the format string, and then scanning them.

Defaults to false, true for all scnlib-defined scanners.

struct empty_parser : public scn::parser_base

A very simple parser base class, which only accepts empty format string specifiers, e.g.

{}, {:} or {1:}.

Subclassed by scn::scanner< T, typename std::enable_if< detail::is_std_streamable< char, T >::value||detail::is_std_streamable< wchar_t, T >::value >::type >

struct scn::common_parser : public scn::parser_base

Provides a framework for building a format string parser.

Does not provide a parse() member function, so not a parser on to its own.

Subclassed by scn::detail::float_scanner< double >, scn::detail::float_scanner< float >, scn::detail::float_scanner< long double >, scn::detail::integer_scanner< int >, scn::detail::integer_scanner< long >, scn::detail::integer_scanner< long long >, scn::detail::integer_scanner< short >, scn::detail::integer_scanner< unsigned int >, scn::detail::integer_scanner< unsigned long >, scn::detail::integer_scanner< unsigned long long >, scn::detail::integer_scanner< unsigned short >, scn::common_parser_default, scn::detail::bool_scanner, scn::detail::char_scanner, scn::detail::float_scanner< T >, scn::detail::integer_scanner< T >, scn::detail::string_scanner

Public Functions

template<typename ParseCtx, typename F, typename CharT = typename ParseCtx::char_type>
inline error parse_common(ParseCtx &pctx, span<const CharT> type_options, span<bool> type_flags, F &&type_cb)

Parse a format string argument, using parse_common_begin, parse_common_flags, parse_common_end, and the supplied type flags.

type_options.size() == type_flags.size() must be true. pctx must be valid, and must start at the format string argument specifiers, e.g. in the case of "{1:foo}" -> pctx == "foo}"

Parameters
  • pctx – Format string to parse

  • type_options – A span of characters, where each character corresponds to a valid type flag. For example, for characters, this span would be [‘c’]

  • type_flags – A span of bools, where the values will be set to true, if a corresponding type flag from type_options was found. Should be initialized to all-false, as a false value will not be written.

  • type_cb – A callback to call, if none of the type_options matched. Must have the signature (ParseCtx& pctx, bool& parsed) -> error., where parsed is set to true, if the flag at pctx.next_char() was parsed and advanced past.

template<typename ParseCtx>
inline error parse_default(ParseCtx &pctx)

Invoke parse_common() with default options (no type flags)

struct common_parser_default : public scn::common_parser

Derives from common_parser, and implements parse() with parse_default()

template<typename WrappedRange, typename Format, typename ...Args>
error scn::scan_usertype(basic_context<WrappedRange> &ctx, const Format &f, Args&... a)

A convenience function for creating scanners for user-provided types.

Wraps vscan_usertype

Example use:

// 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);
    }
};

Parameters
  • ctx – Context given to the scanning function

  • f – Format string to parse

  • a – Member types (etc) to parse

template<typename WrappedRange, typename CharT = typename WrappedRange::char_type>
error scn::vscan_usertype(basic_context<WrappedRange> &ctx, basic_string_view<CharT> f, basic_args<CharT> &&args)

See also

scan_usertype

See also

vscan

Low-level range reading

The following functions abstract away the source range in easier to understand parsing operations.

template<typename WrappedRange>
expected<typename WrappedRange::char_type> scn::read_code_unit(WrappedRange &r, bool advance = true)

Reads a single character (= code unit) from the range.

Dereferences the begin iterator, wrapping it in an expected if necessary.

Encoding-agnostic, doesn’t care about code points, and may leave behind partial ones.

Parameters
  • r – Range to read from

  • advance – If true, and the read was successful, the range is advanced by a single character, as if by calling r.advance().

Returns

The next character in the range, obtained as if by dereferencing the begin iterator *r.begin(). If r.begin() == r.end(), returns EOF. If r is direct, returns *r.begin() wrapped in an expected. If r is not direct, returns *r.begin() as-is, with any errors that may have been caused by the read.

template<typename CharT>
struct scn::read_code_point_result

Type returned by read_code_point

Template Parameters

CharT – Character type of the range

Public Members

span<const CharT> chars

Code units, may point to writebuf given to read_code_point

code_point cp

Parsed code point.

template<typename WrappedRange, typename BufValueT>
expected<read_code_point_result<typename WrappedRange::char_type>> scn::read_code_point(WrappedRange &r, span<BufValueT> writebuf)

Read a single Unicode code point from r as if by repeatedly calling read_code_unit().

Advances the range past the read code point. On error, rolls back the range into the state it was before calling this function, as if by calling putback_n().

Parameters
  • r – Range to read from

  • writebuf – Buffer to use for reading into, if necessary. BufValueT can be any trivial type. Must be at least 4 bytes long. May be written over.

Returns

An instance of read_code_point_result, wrapped in an expected. chars contains the code units read from r, which may point to writebuf. cp contains the code point parsed. If r.begin() == r.end(), returns EOF. If read_code_unit() or putback_n() fails, returns any errors returned by it. If the code point was not encoded correctly, returns error::invalid_encoding.

template<typename WrappedRange, typename std::enable_if<WrappedRange::is_contiguous>::type* = nullptr>
expected<span<const typename detail::extract_char_type<typename WrappedRange::iterator>::type>> scn::read_zero_copy(WrappedRange &r, ranges::range_difference_t<WrappedRange> n)

Reads up to n characters (= code units) from r, as if by repeatedly incrementing r.begin(), and returns a span pointing into r.

Let count be min(r.size(), n). Reads, and advances r by count characters. r.begin() is in no point dereferenced. If r.size() is not defined, the range is not contiguous, and an empty span is returned.

Returns

A span pointing to r, starting from r.begin() and with a size of count. If r.begin() == r.end(), returns EOF. If the range does not satisfy contiguous_range, returns an empty span.

template<typename WrappedRange, typename std::enable_if<WrappedRange::is_contiguous>::type* = nullptr>
expected<span<const typename detail::extract_char_type<typename WrappedRange::iterator>::type>> scn::read_all_zero_copy(WrappedRange &r)

Reads every character from r, as if by repeatedly incrementing r.begin(), and returns a span pointing into r.

If there’s no error, r is advanced to the end. r.begin() is in no point dereferenced. If r.size() is not defined, the range is not contiguous, and an empty span is returned.

Returns

A span pointing to r, starting at r.begin() and ending at r.end(). If r.begin() == r.end(), returns EOF. If the range does not satisfy contiguous_range, returns an empty span.

template<typename WrappedRange, typename OutputIterator, typename std::enable_if<WrappedRange::is_contiguous>::type* = nullptr>
error scn::read_into(WrappedRange &r, OutputIterator &it, ranges::range_difference_t<WrappedRange> n)

Reads up to n characters (= code units) from r, as if by repeatedly calling read_code_unit(), and writing the characters into it.

If reading fails at any point, the error is returned. r is advanced by as many characters that were successfully read.

Parameters
  • r – Range to read

  • it – Iterator to write into, e.g. std::back_insert_iterator. Must satisfy output_iterator, and be incrementable by n times.

  • n – Characters to read from r

Returns

error::good if n characters were read. If r.begin() == r.end() at any point before n characters has been read, returns EOF. Any error returned by read_code_unit() if one occurred.

template<typename WrappedRange, typename Predicate>
expected<span<const typename WrappedRange::char_type>> scn::read_until_space_zero_copy(WrappedRange &r, Predicate &&is_space, bool keep_final_space)

Reads code points from r, until a space, as determined by is_space, is found, and returns a span pointing to r.

If no error occurs r is advanced past the returned span. On error, r is not advanced.

Parameters
  • r – Range to read from

  • is_space – Predicate taking a span of code units encompassing a code point, and returning a bool, where true means that the character is a space. Additionally, it must have a member function is_space.is_multibyte(), returning a bool, where true means that a space character can encompass multiple code units.

  • keep_final_space – If true, the space code point found is included in the returned span, and it is advanced past in r. If false, it is not included, and r.begin() will point to the space.

Returns

Span of code units, pointing to r, starting at r.begin(), and ending at the space character, the precise location determined by the keep_final_space parameter. If r.begin() == r.end(), returns EOF. r reaching its end before a space character is found is not considered an error. If r contains invalid encoding, returns error::invalid_encoding. If the range is not contiguous, returns an empty span.

template<typename WrappedRange, typename OutputIterator, typename Predicate, typename std::enable_if<WrappedRange::is_contiguous>::type* = nullptr>
error scn::read_until_space(WrappedRange &r, OutputIterator &out, Predicate &&is_space, bool keep_final_space)

Reads code points from r, until a space, as determined by is_space, is found, and writes them into out, a single code unit at a time.

If no error occurs, r is advanced past the last character written into out.

On error, r is advanced an indeterminate amount, as if by calling r.advance(n), where n is a non-negative integer. It is, however, not advanced past any space characters.

Parameters
  • r – Range to read from

  • out – Iterator to write read characters into. Must satisfy output_iterator.

  • is_space – Predicate taking a span of code units encompassing a code point, and returning a bool, where true means that the character is a space. Additionally, it must have a member function is_space.is_multibyte(), returning a bool, where true means that a space character can encompass multiple code units.

  • keep_final_space – If true, the space code point found is written into out, and it is advanced past in r. If false, it is not included, and r.begin() will point to the space.

Returns

error::good on success. If r.begin() == r.end(), returns EOF. r reaching its end before a space character is found is not considered an error. If r contains invalid encoding, returns error::invalid_encoding.

template<typename WrappedRange, typename OutputIterator, typename Sentinel, typename Predicate>
error scn::read_until_space_ranged(WrappedRange &r, OutputIterator &out, Sentinel end, Predicate &&is_space, bool keep_final_space)

Otherwise equivalent to read_until_space, except will also stop reading if out == end.

See also

read_until_space

template<typename WrappedRange, typename std::enable_if<WrappedRange::is_contiguous>::type* = nullptr>
error scn::putback_n(WrappedRange &r, ranges::range_difference_t<WrappedRange> n)

Puts back n characters (= code units) into r as if by repeatedly calling r.advance(-1).

Encoding-agnostic, may leave behind partial code points.

Parameters
  • r – Range to roll back

  • n – Characters to put back, must be less than or equal to the number of characters already read from r.

Returns

If r is contiguous, will always return error::good. Otherwise, may return error::unrecoverable_source_error, if the putback fails.

template<typename Context, typename std::enable_if<!Context::range_type::is_contiguous>::type* = nullptr>
error scn::skip_range_whitespace(Context &ctx, bool localized) noexcept

Reads code points from ctx.range(), as if by repeatedly calling read_code_point(), until a non-space character is found, or EOF is reached.

That non-space character is then put back into the range.

Whether a character is a space, is determined by ctx.locale() and the localized parameter.

Parameters
  • ctx – Context to get the range and locale from.

  • localized – If true, ctx.locale().get_custom() is used. Otherwise, ctx.locale().get_static() is used. In practice, means whether locale-specific whitespace characters are accepted, or just those given by std::isspace with the "C" locale.

Returns

error::good on success. If ctx.range().begin() == ctx.range().end(), returns EOF. If ctx.range() contains invalid encoding, returns error::invalid_encoding.

Tuple scanning

template<typename ...Args, typename Range, typename Format>
auto scn::scan_tuple(Range &&r, Format f) -> std::tuple<detail::scan_result_for_range<Range>, Args...>

Alternative interface for scanning, returning values as a tuple, instead of taking them by reference.

It’s highly recommended to use this interface only with C++17 or later, as structured bindings make it way more ergonomic.

Compared to the regular scan interface, the performance of this interface is the same (generated code is virtually identical with optimizations enabled), but the compile time is slower.

Values scanned by this function still need to be default-constructible. To scan a non-default-constructible value, use scn::optional

Parameters
  • r – Input range

  • f – Format string to use

Returns

Tuple, where the first element is the scan result, and the remaining elements are the scanned values.

template<typename ...Args, typename Range>
auto scn::scan_tuple_default(Range &&r) -> std::tuple<detail::scan_result_for_range<Range>, Args...>

Equivalent to scan_tuple, except uses vscan_default under the hood.

Utility types

template<typename CharT>
class basic_string_view

A view over a (sub)string.

Used even when std::string_view is available to avoid compatibility issues.

using scn::string_view = basic_string_view<char>
using scn::wstring_view = basic_string_view<wchar_t>
template<typename T>
class span

A view over a contiguous range.

Stripped-down version of std::span.

template<typename T>
class optional

A very lackluster optional implementation.

Useful when scanning non-default-constructible types, especially with <tuple_return.h>:

// implement scn::scanner for optional<mytype>
optional<mytype> val;
scn::scan(source, "{}", val);

// with tuple_return:
auto [result, val] = scn::scan_tuple<optional<mytype>>(source, "{}");