// 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_DETAIL_ARGS_H #define SCN_DETAIL_ARGS_H #include "../reader/common.h" #include "../util/array.h" SCN_GCC_PUSH SCN_GCC_IGNORE("-Wnoexcept") #include SCN_GCC_POP namespace scn { SCN_BEGIN_NAMESPACE /** * Allows reading an rvalue. * Stores an rvalue and returns an lvalue reference to it via `operator()`. * Create one with \ref temp. */ template struct temporary { temporary(T&& val) : value(SCN_MOVE(val)) {} T& operator()() && noexcept { return value; } T value; }; /** * Factory function for \ref temporary. * * Canonical use case is with \ref scn::span: * \code{.cpp} * std::vector buffer(32, '\0'); * auto result = scn::scan("123", "{}", scn::temp(scn::make_span(buffer))); * // buffer == "123" * \endcode */ template ::value>::type* = nullptr> temporary temp(T&& val) { return {SCN_FWD(val)}; } namespace detail { enum type { none_type = 0, // signed integer schar_type, short_type, int_type, long_type, long_long_type, // unsigned integer uchar_type, ushort_type, uint_type, ulong_type, ulong_long_type, // other integral types bool_type, char_type, code_point_type, last_integer_type = code_point_type, // floats float_type, double_type, long_double_type, last_numeric_type = long_double_type, // other buffer_type, string_type, string_view_type, custom_type, last_type = custom_type }; constexpr bool is_integral(type t) noexcept { return t > none_type && t <= last_integer_type; } constexpr bool is_arithmetic(type t) noexcept { return t > none_type && t <= last_numeric_type; } struct custom_value { // using scan_type = error (*)(void*, Context&, ParseCtx&); void* value; void (*scan)(); }; template error scan_custom_arg(void* arg, Context& ctx, ParseCtx& pctx) noexcept { return visitor_boilerplate>(*static_cast(arg), ctx, pctx); } struct monostate { }; template struct ctx_tag { }; template struct parse_ctx_tag { }; class value { public: constexpr value() noexcept : m_empty{} {} template explicit SCN_CONSTEXPR14 value(T& val) noexcept : m_value(std::addressof(val)) { } template value(ctx_tag, parse_ctx_tag, T& val) noexcept : m_custom( custom_value{std::addressof(val), reinterpret_cast( &scan_custom_arg)}) { } template SCN_CONSTEXPR14 T& get_as() noexcept { return *static_cast(m_value); } template constexpr const T& get_as() const noexcept { return *static_cast(m_value); } SCN_CONSTEXPR14 custom_value& get_custom() noexcept { return m_custom; } SCN_NODISCARD constexpr const custom_value& get_custom() const noexcept { return m_custom; } private: union { monostate m_empty; void* m_value; custom_value m_custom; }; }; template struct init { T* val; static const type type_tag = Type; constexpr init(T& v) : val(std::addressof(v)) {} template SCN_CONSTEXPR14 value get() { SCN_EXPECT(val != nullptr); return value{*val}; } }; template struct init { T* val; static const type type_tag = custom_type; constexpr init(T& v) : val(std::addressof(v)) {} template SCN_CONSTEXPR14 value get() { SCN_EXPECT(val != nullptr); return {ctx_tag{}, parse_ctx_tag{}, *val}; } }; template SCN_CONSTEXPR14 basic_arg make_arg(T& value) noexcept; #define SCN_MAKE_VALUE(Tag, Type) \ template \ constexpr init make_value(Type& val, \ priority_tag<1>) noexcept \ { \ return val; \ } SCN_MAKE_VALUE(schar_type, signed char) SCN_MAKE_VALUE(short_type, short) SCN_MAKE_VALUE(int_type, int) SCN_MAKE_VALUE(long_type, long) SCN_MAKE_VALUE(long_long_type, long long) SCN_MAKE_VALUE(uchar_type, unsigned char) SCN_MAKE_VALUE(ushort_type, unsigned short) SCN_MAKE_VALUE(uint_type, unsigned) SCN_MAKE_VALUE(ulong_type, unsigned long) SCN_MAKE_VALUE(ulong_long_type, unsigned long long) SCN_MAKE_VALUE(bool_type, bool) SCN_MAKE_VALUE(code_point_type, code_point) SCN_MAKE_VALUE(float_type, float) SCN_MAKE_VALUE(double_type, double) SCN_MAKE_VALUE(long_double_type, long double) SCN_MAKE_VALUE(buffer_type, span) SCN_MAKE_VALUE(string_type, std::basic_string) SCN_MAKE_VALUE(string_view_type, basic_string_view) template constexpr init make_value( CharT& val, priority_tag<1>) noexcept { return val; } template constexpr inline auto make_value(T& val, priority_tag<0>) noexcept -> init { return val; } enum : size_t { packed_arg_bitsize = 5, packed_arg_mask = (1 << packed_arg_bitsize) - 1, max_packed_args = (sizeof(size_t) * 8 - 1) / packed_arg_bitsize, is_unpacked_bit = size_t{1} << (sizeof(size_t) * 8ull - 1ull) }; } // namespace detail SCN_CLANG_PUSH SCN_CLANG_IGNORE("-Wpadded") /// Type-erased scanning argument. template class SCN_TRIVIAL_ABI basic_arg { public: using char_type = CharT; class handle { public: explicit handle(detail::custom_value custom) : m_custom(custom) {} template error scan(Context& ctx, ParseCtx& pctx) { return reinterpret_cast( m_custom.scan)(m_custom.value, ctx, pctx); } private: detail::custom_value m_custom; }; constexpr basic_arg() = default; constexpr explicit operator bool() const noexcept { return m_type != detail::none_type; } SCN_NODISCARD constexpr detail::type type() const noexcept { return type; } SCN_NODISCARD constexpr bool is_integral() const noexcept { return detail::is_integral(m_type); } SCN_NODISCARD constexpr bool is_arithmetic() const noexcept { return detail::is_arithmetic(m_type); } private: constexpr basic_arg(detail::value v, detail::type t) noexcept : m_value(v), m_type(t) { } template friend SCN_CONSTEXPR14 basic_arg detail::make_arg(T& value) noexcept; template friend SCN_CONSTEXPR14 error visit_arg(Visitor&& vis, basic_arg& arg); friend class basic_args; detail::value m_value; detail::type m_type{detail::none_type}; }; SCN_CLANG_POP template SCN_CONSTEXPR14 error visit_arg(Visitor&& vis, basic_arg& arg) { switch (arg.m_type) { case detail::none_type: break; case detail::schar_type: return vis(arg.m_value.template get_as()); case detail::short_type: return vis(arg.m_value.template get_as()); case detail::int_type: return vis(arg.m_value.template get_as()); case detail::long_type: return vis(arg.m_value.template get_as()); case detail::long_long_type: return vis(arg.m_value.template get_as()); case detail::uchar_type: return vis(arg.m_value.template get_as()); case detail::ushort_type: return vis(arg.m_value.template get_as()); case detail::uint_type: return vis(arg.m_value.template get_as()); case detail::ulong_type: return vis(arg.m_value.template get_as()); case detail::ulong_long_type: return vis(arg.m_value.template get_as()); case detail::bool_type: return vis(arg.m_value.template get_as()); case detail::char_type: return vis(arg.m_value.template get_as()); case detail::code_point_type: return vis(arg.m_value.template get_as()); case detail::float_type: return vis(arg.m_value.template get_as()); case detail::double_type: return vis(arg.m_value.template get_as()); case detail::long_double_type: return vis(arg.m_value.template get_as()); case detail::buffer_type: return vis(arg.m_value.template get_as>()); case detail::string_type: return vis( arg.m_value.template get_as>()); case detail::string_view_type: return vis( arg.m_value.template get_as>()); case detail::custom_type: return vis(typename basic_arg::handle( arg.m_value.get_custom())); SCN_CLANG_PUSH SCN_CLANG_IGNORE("-Wcovered-switch-default") default: return vis(detail::monostate{}); SCN_CLANG_POP } SCN_UNREACHABLE; } namespace detail { template struct get_type { using value_type = decltype(make_value( SCN_DECLVAL(typename std::remove_reference< typename std::remove_cv::type>::type&), SCN_DECLVAL(priority_tag<1>))); static const type value = value_type::type_tag; }; template constexpr size_t get_types() { return 0; } template constexpr size_t get_types() { return static_cast(get_type::value) | (get_types() << 5); } template SCN_CONSTEXPR14 basic_arg make_arg(T& value) noexcept { basic_arg arg; arg.m_type = get_type::value; arg.m_value = make_value(value, priority_tag<1>{}) .template get(); return arg; } template inline auto make_arg(T& v) -> typename std::enable_if::type { return make_value(v, priority_tag<1>{}) .template get(); } template inline auto make_arg(T& v) -> typename std:: enable_if>::type { return make_arg(v); } } // namespace detail template class arg_store { static constexpr const size_t num_args = sizeof...(Args); static const bool is_packed = num_args < detail::max_packed_args; friend class basic_args; static constexpr size_t get_types() { return is_packed ? detail::get_types() : detail::is_unpacked_bit | num_args; } public: static constexpr size_t types = get_types(); using arg_type = basic_arg; using value_type = typename std::conditional::type; static constexpr size_t data_size = num_args + (is_packed && num_args != 0 ? 0 : 1); template SCN_CONSTEXPR14 arg_store(detail::ctx_tag, detail::parse_ctx_tag, Args&... a) noexcept : m_data{{detail::make_arg(a)...}} { } SCN_CONSTEXPR14 span data() noexcept { return make_span(m_data.data(), static_cast(m_data.size())); } private: detail::array m_data; }; template arg_store make_args(Args&... args) { return {detail::ctx_tag(), detail::parse_ctx_tag(), args...}; } template arg_store make_args_for(WrappedRange&, Format, Args&... args) { using context_type = basic_context; using parse_context_type = typename detail::parse_context_template_for_format< Format>::template type; return {detail::ctx_tag(), detail::parse_ctx_tag(), args...}; } template class basic_args { public: using arg_type = basic_arg; constexpr basic_args() noexcept = default; template SCN_CONSTEXPR14 basic_args(arg_store& store) noexcept : m_types(store.types) { set_data(store.m_data.data()); } SCN_CONSTEXPR14 basic_args(span args) noexcept : m_types(detail::is_unpacked_bit | args.size()) { set_data(args.data()); } SCN_CONSTEXPR14 arg_type get(std::ptrdiff_t i) const noexcept { return do_get(i); } SCN_NODISCARD SCN_CONSTEXPR14 bool check_id( std::ptrdiff_t i) const noexcept { if (!is_packed()) { return static_cast(i) < (m_types & ~static_cast(detail::is_unpacked_bit)); } return type(i) != detail::none_type; } SCN_NODISCARD constexpr size_t max_size() const noexcept { return is_packed() ? static_cast(detail::max_packed_args) : m_types & ~static_cast(detail::is_unpacked_bit); } private: size_t m_types{0}; union { detail::value* m_values; arg_type* m_args; }; SCN_NODISCARD constexpr bool is_packed() const noexcept { return (m_types & detail::is_unpacked_bit) == 0; } SCN_NODISCARD SCN_CONSTEXPR14 typename detail::type type( std::ptrdiff_t i) const noexcept { size_t shift = static_cast(i) * detail::packed_arg_bitsize; return static_cast( (static_cast(m_types) >> shift) & detail::packed_arg_mask); } SCN_CONSTEXPR14 void set_data(detail::value* values) noexcept { m_values = values; } SCN_CONSTEXPR14 void set_data(arg_type* args) noexcept { m_args = args; } SCN_CONSTEXPR14 arg_type do_get(std::ptrdiff_t i) const noexcept { SCN_EXPECT(i >= 0); arg_type arg; if (!is_packed()) { auto num_args = static_cast(max_size()); if (SCN_LIKELY(i < num_args)) { arg = m_args[i]; } return arg; } SCN_EXPECT(m_values); if (SCN_UNLIKELY( i > static_cast(detail::max_packed_args))) { return arg; } arg.m_type = type(i); if (arg.m_type == detail::none_type) { return arg; } arg.m_value = m_values[i]; return arg; } }; SCN_END_NAMESPACE } // namespace scn #endif // SCN_DETAIL_ARGS_H