diff --git a/CMakeLists.txt b/CMakeLists.txt index 5224ef0..d09370c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,7 +31,7 @@ target_sources(papilio PUBLIC "natvis/papilio.natvis") target_include_directories(papilio PUBLIC include) if(${papilio_build_example}) - add_subdirectory(example) + #add_subdirectory(example) endif() if(${papilio_build_unit_test}) enable_testing() diff --git a/example/playground.cpp b/example/playground.cpp index 63f1ee9..8fbd126 100644 --- a/example/playground.cpp +++ b/example/playground.cpp @@ -255,7 +255,7 @@ namespace playground { papilio::println("Formatting result:"); - papilio::mutable_format_arg_store arg_store; + papilio::mutable_format_args arg_store; for(auto& i : m_args) { auto visitor = [&](auto&& v) diff --git a/include/papilio/core.hpp b/include/papilio/core.hpp index b5e3db4..f1bba7d 100644 --- a/include/papilio/core.hpp +++ b/include/papilio/core.hpp @@ -69,38 +69,12 @@ namespace papilio space }; - class format_spec_parse_context; - - namespace detail - { - struct std_format_spec - { - using char_type = char; - - char_type fill = ' '; - format_align align = format_align::default_align; - format_sign sign = format_sign::default_sign; - bool alternate_form = false; // specified by '#' - bool fill_zero = false; - std::size_t width = 0; - bool use_locale = false; - char_type type_char = '\0'; - }; - - std_format_spec parse_std_format_spec(format_spec_parse_context& ctx); - } - class common_format_spec { public: common_format_spec() = default; common_format_spec(const common_format_spec&) noexcept = default; - common_format_spec(format_spec_parse_context& spec_ctx) - { - parse(spec_ctx); - } - void parse(format_spec_parse_context& spec_ctx); void reset() noexcept { *this = common_format_spec(); @@ -1079,7 +1053,7 @@ namespace papilio } } - class format_arg_store_base + class format_args_base { public: using char_type = char; @@ -1116,7 +1090,7 @@ namespace papilio } template - class static_format_arg_store final : public detail::format_arg_store_base + class static_format_arg_store final : public detail::format_args_base { public: template @@ -1138,13 +1112,13 @@ namespace papilio raise_invalid_named_argument(); return it->second; } - using format_arg_store_base::get; + using format_args_base::get; bool check(string_view_type key) const noexcept { return m_named_args.contains(key); } - using format_arg_store_base::check; + using format_args_base::check; size_type size() const noexcept override { @@ -1191,14 +1165,14 @@ namespace papilio } }; - class mutable_format_arg_store final : public detail::format_arg_store_base + class mutable_format_args final : public detail::format_args_base { public: - mutable_format_arg_store() = default; - mutable_format_arg_store(const mutable_format_arg_store&) = delete; - mutable_format_arg_store(mutable_format_arg_store&&) = default; + mutable_format_args() = default; + mutable_format_args(const mutable_format_args&) = delete; + mutable_format_args(mutable_format_args&&) = default; template - mutable_format_arg_store(Args&&... args) + mutable_format_args(Args&&... args) { push(std::forward(args)...); } @@ -1223,10 +1197,10 @@ namespace papilio const format_arg& get(size_type i) const override; const format_arg& get(string_view_type key) const override; - using format_arg_store_base::get; + using format_args_base::get; bool check(string_view_type key) const noexcept override; - using format_arg_store_base::check; + using format_args_base::check; [[nodiscard]] size_type size() const noexcept override @@ -1251,12 +1225,12 @@ namespace papilio }; // type-erased argument store - class dynamic_format_arg_store final : public detail::format_arg_store_base + class dynamic_format_args final : public detail::format_args_base { public: - dynamic_format_arg_store() = delete; - dynamic_format_arg_store(const dynamic_format_arg_store&) noexcept = default; - dynamic_format_arg_store(const detail::format_arg_store_base& store) noexcept + dynamic_format_args() = delete; + dynamic_format_args(const dynamic_format_args&) noexcept = default; + dynamic_format_args(const detail::format_args_base& store) noexcept : m_ref(&store) { assert(m_ref != this); // circular reference @@ -1270,7 +1244,7 @@ namespace papilio { return m_ref->get(k); } - using format_arg_store_base::get; + using format_args_base::get; size_type size() const noexcept override { @@ -1285,20 +1259,20 @@ namespace papilio { return m_ref->check(k); } - using format_arg_store_base::check; + using format_args_base::check; [[nodiscard]] - const detail::format_arg_store_base& to_underlying() const noexcept + const detail::format_args_base& to_underlying() const noexcept { return *m_ref; } private: - const detail::format_arg_store_base* m_ref; + const detail::format_args_base* m_ref; }; template - concept format_arg_store = std::is_base_of_v; + concept format_arg_store = std::is_base_of_v; template auto make_format_args(Args&&... args) @@ -1327,9 +1301,9 @@ namespace papilio format_parse_context() = delete; format_parse_context(const format_parse_context&) = delete; - format_parse_context(string_view_type str, const dynamic_format_arg_store& store); + format_parse_context(string_view_type str, const dynamic_format_args& store); - const dynamic_format_arg_store& get_store() const noexcept + const dynamic_format_args& get_store() const noexcept { return m_store; } @@ -1387,7 +1361,7 @@ namespace papilio iterator m_it; mutable bool m_manual_indexing = false; std::size_t m_default_arg_idx = 0; - dynamic_format_arg_store m_store; + dynamic_format_args m_store; void enable_manual_indexing() const noexcept { @@ -1395,104 +1369,13 @@ namespace papilio } }; - // WARNING: This class only holds the view of string and reference of arguments - // The invoker needs to handle lifetimes manually - class format_spec_parse_context - { - public: - using char_type = char; - using string_type = std::basic_string; - using string_view_type = std::basic_string_view; - using iterator = string_view_type::const_iterator; - using reverse_iterator = string_view_type::const_reverse_iterator; - - format_spec_parse_context() = delete; - format_spec_parse_context(const format_spec_parse_context&) = delete; - constexpr format_spec_parse_context(string_view_type str, const dynamic_format_arg_store& store) noexcept - : m_base(nullptr), m_spec_str(str), m_store(store) {} - format_spec_parse_context(format_parse_context& base, string_view_type str) noexcept - : m_base(&base), m_spec_str(str), m_store(base.get_store()) {} - - [[nodiscard]] - constexpr bool has_base() const noexcept - { - return m_base != nullptr; - } - format_parse_context& get_base() const noexcept - { - return *m_base; - } - - std::size_t current_arg_id() const - { - return get_base().current_arg_id(); - } - std::size_t next_arg_id() - { - return get_base().next_arg_id(); - } - std::size_t check_arg_id(std::size_t i) const - { - return get_base().check_arg_id(i); - } - - [[nodiscard]] - bool manual_indexing() const noexcept - { - return get_base().manual_indexing(); - } - - [[nodiscard]] - constexpr iterator begin() const noexcept - { - return m_spec_str.begin(); - } - [[nodiscard]] - constexpr iterator end() const noexcept - { - return m_spec_str.end(); - } - [[nodiscard]] - constexpr reverse_iterator rbegin() const noexcept - { - return m_spec_str.rbegin(); - } - [[nodiscard]] - constexpr reverse_iterator rend() const noexcept - { - return m_spec_str.rend(); - } - - [[nodiscard]] - constexpr operator string_view_type() const noexcept - { - return m_spec_str; - } - - [[deprecated("no effect")]] - constexpr bool has_store() const noexcept - { - return true; - } - [[nodiscard]] - const dynamic_format_arg_store& get_store() const noexcept - { - return m_store; - } - - private: - format_parse_context* m_base; - string_view_type m_spec_str; - dynamic_format_arg_store m_store; - }; - template class basic_format_context { public: using char_type = char; using iterator = OutputIt; - using store_type = dynamic_format_arg_store; + using store_type = dynamic_format_args; basic_format_context(iterator it, const store_type& store) : m_out(std::move(it)), m_store(store) {} @@ -1537,7 +1420,7 @@ namespace papilio using char_type = char; virtual void push_back(char_type ch) = 0; - virtual const dynamic_format_arg_store& get_store() const noexcept = 0; + virtual const dynamic_format_args& get_store() const noexcept = 0; virtual locale_ref getloc_ref() const noexcept = 0; std::locale getloc() const @@ -1550,7 +1433,7 @@ namespace papilio { public: using iterator = OutputIt; - using store_type = dynamic_format_arg_store; + using store_type = dynamic_format_args; dynamic_format_context_impl(basic_format_context& ctx) noexcept : m_out(ctx.out()), m_store(ctx.get_store()), m_loc(ctx.getloc_ref()) {} @@ -1560,7 +1443,7 @@ namespace papilio *m_out = ch; ++m_out; } - const dynamic_format_arg_store& get_store() const noexcept override + const dynamic_format_args& get_store() const noexcept override { return m_store; } @@ -1570,7 +1453,7 @@ namespace papilio } private: iterator m_out; - dynamic_format_arg_store m_store; + dynamic_format_args m_store; locale_ref m_loc; }; } @@ -1580,7 +1463,7 @@ namespace papilio public: using char_type = char; using iterator = std::back_insert_iterator; - using store_type = dynamic_format_arg_store; + using store_type = dynamic_format_args; using value_type = char_type; dynamic_format_context() = delete; diff --git a/include/papilio/format.hpp b/include/papilio/format.hpp index 90ad946..7e4a125 100644 --- a/include/papilio/format.hpp +++ b/include/papilio/format.hpp @@ -1,5 +1,6 @@ #pragma once +#include "macros.hpp" #include "core.hpp" #include "script.hpp" #include @@ -7,177 +8,6 @@ namespace papilio { - template - class format_executor - { - public: - using char_type = char; - using string_type = std::basic_string; - using string_view_type = std::basic_string_view; - - format_executor(string_view_type fmt, Context& ctx) noexcept - : m_fmt(fmt), m_ctx(ctx) {} - format_executor(const format_executor&) = delete; - - void execute() - { - using context_traits = format_context_traits; - - format_parse_context parse_ctx(m_fmt, context_traits::get_store(m_ctx)); - script::lexer lex; - script::interpreter intp; - - auto it = parse_ctx.begin(); - for(; it != parse_ctx.end(); it = parse_ctx.begin()) - { - char_type ch = *it; - if(ch == '{') - { - auto next_it = std::next(it); - if(next_it == parse_ctx.end()) - { - throw std::runtime_error("missing replacement field"); - } - - if(*next_it == '{') - { - context_traits::append(m_ctx, '{'); - parse_ctx.advance_to(std::next(next_it)); - continue; - } - else - { - std::optional default_arg_id = std::nullopt; - if(!parse_ctx.manual_indexing()) - default_arg_id = parse_ctx.current_arg_id(); - auto field_begin = std::next(it); - lex.clear(); - auto result = lex.parse( - string_view_type(field_begin, parse_ctx.end()), - script::lexer_mode::replacement_field, - default_arg_id - ); - if(result.default_arg_idx_used) - parse_ctx.next_arg_id(); - else - parse_ctx.enable_manual_indexing(); - auto arg_end = std::next(field_begin, result.parsed_char); - if(arg_end == parse_ctx.end()) - { - throw std::runtime_error("missing right brace ('}')"); - } - - auto pred = [counter = std::size_t(0)](char_type ch) mutable - { - if(ch == '{') - ++counter; - if(ch == '}') - { - if(counter == 0) - return true; - --counter; - } - - return false; - }; - auto fmt_begin = arg_end; - if(*arg_end == ':') - ++fmt_begin; - auto field_end = std::find_if(fmt_begin, parse_ctx.end(), pred); - if(field_end == parse_ctx.end()) - { - throw std::runtime_error("missing right brace ('}')"); - } - - auto [idx, acc] = intp.access(lex.lexemes()); - // TODO: Separate parsing and formatting - - format_spec_parse_context spec_ctx( - parse_ctx, - string_view_type(fmt_begin, field_end) - ); - acc.access(context_traits::get_store(m_ctx).get(idx)).format( - spec_ctx, - m_ctx - ); - - parse_ctx.advance_to(std::next(field_end)); - } - } - else if(ch == '}') - { - auto next_it = std::next(it); - if(next_it != parse_ctx.end() && *next_it == '}') - { - context_traits::append(m_ctx, '}'); - parse_ctx.advance_to(std::next(next_it)); - } - else - { - throw std::runtime_error("invalid right brace ('}')"); - } - } - else if(ch == '[') - { - auto next_it = std::next(it); - if(next_it == parse_ctx.end()) - { - throw std::runtime_error("missing script block"); - } - - if(*next_it == '[') - { - context_traits::append(m_ctx, '['); - parse_ctx.advance_to(std::next(next_it)); - continue; - } - else - { - string_view_type src(next_it, parse_ctx.end()); - lex.clear(); - auto result = lex.parse( - src, - script::lexer_mode::script_block - ); - auto script_end = std::next(next_it, result.parsed_char); - if(script_end == parse_ctx.end() || *script_end != ']') - { - throw std::runtime_error("missing right bracket (']')"); - } - - script::executor::context ex_ctx(context_traits::get_store(m_ctx)); - intp.compile(lex.lexemes())(ex_ctx); - - context_traits::append(m_ctx, ex_ctx.get_result()); - parse_ctx.advance_to(std::next(script_end)); - } - } - else if(ch == ']') - { - auto next_it = std::next(it); - if(next_it != parse_ctx.end() && *next_it == ']') - { - context_traits::append(m_ctx, ']'); - parse_ctx.advance_to(std::next(next_it)); - } - else - { - throw std::runtime_error("invalid right brace ('}')"); - } - } - else - { - context_traits::append(m_ctx, ch); - parse_ctx.advance_to(std::next(it)); - } - } - } - - private: - string_view_type m_fmt; - Context& m_ctx; - }; - template struct format_to_n_result { @@ -186,22 +16,16 @@ namespace papilio }; template - OutputIt vformat_to(OutputIt out, std::string_view fmt, const dynamic_format_arg_store& store) + OutputIt vformat_to(OutputIt out, std::string_view fmt, const dynamic_format_args& store) { - basic_format_context fmt_ctx(out, store); - format_executor fmt_ex(fmt, fmt_ctx); - fmt_ex.execute(); - - return fmt_ctx.out(); + // TODO + return out; // placeholder } template - OutputIt vformat_to(OutputIt out, const std::locale& loc, std::string_view fmt, const dynamic_format_arg_store& store) + OutputIt vformat_to(OutputIt out, const std::locale& loc, std::string_view fmt, const dynamic_format_args& store) { - basic_format_context fmt_ctx(loc, out, store); - format_executor fmt_ex(fmt, fmt_ctx); - fmt_ex.execute(); - - return fmt_ctx.out(); + // TODO + return out; // placeholder } namespace detail @@ -240,7 +64,7 @@ namespace papilio } template - format_to_n_result vformat_to_n(OutputIt out, std::iter_difference_t n, std::string_view fmt, const dynamic_format_arg_store& store) + format_to_n_result vformat_to_n(OutputIt out, std::iter_difference_t n, std::string_view fmt, const dynamic_format_args& store) { auto result = vformat_to( detail::format_to_n_wrapper(out, n), @@ -250,7 +74,7 @@ namespace papilio return { result.out, result.counter }; } template - format_to_n_result vformat_to_n(OutputIt out, std::iter_difference_t n, const std::locale& loc, std::string_view fmt, const dynamic_format_arg_store& store) + format_to_n_result vformat_to_n(OutputIt out, std::iter_difference_t n, const std::locale& loc, std::string_view fmt, const dynamic_format_args& store) { auto result = vformat_to( detail::format_to_n_wrapper(out, n), @@ -260,10 +84,10 @@ namespace papilio ); return { result.out, result.counter }; } - std::size_t vformatted_size(std::string_view fmt, const dynamic_format_arg_store& store); - std::size_t vformatted_size(const std::locale& loc, std::string_view fmt, const dynamic_format_arg_store& store); - std::string vformat(std::string_view fmt, const dynamic_format_arg_store& store); - std::string vformat(const std::locale& loc, std::string_view fmt, const dynamic_format_arg_store& store); + std::size_t vformatted_size(std::string_view fmt, const dynamic_format_args& store); + std::size_t vformatted_size(const std::locale& loc, std::string_view fmt, const dynamic_format_args& store); + std::string vformat(std::string_view fmt, const dynamic_format_args& store); + std::string vformat(const std::locale& loc, std::string_view fmt, const dynamic_format_args& store); template OutputIt format_to(OutputIt out, std::string_view fmt, Args&&... args) @@ -309,685 +133,6 @@ namespace papilio std::string format(const std::locale& loc, std::string_view fmt, Args&&... args) { // use namespace prefix to avoid collision with std::format caused by ADL - return vformat(loc, fmt, papilio::make_format_args(std::forward(args)...)); - } - - namespace detail - { - template - concept supported_integral = - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v; - - class integral_formatter_base - { - public: - static char map_char(unsigned char digit, bool uppercase) noexcept - { - if(digit < 10) - return '0' + digit; - else - { - if(uppercase) - return 'A' + (digit - 10); - else - return 'a' + (digit - 10); - } - } - }; - - // front and back - std::pair calc_fill_width(format_align align, std::size_t width, std::size_t current); + return PAPILIO_NS vformat(loc, fmt, papilio::make_format_args(std::forward(args)...)); } - - template - class formatter : public detail::integral_formatter_base - { - public: - using char_type = char; - using value_type = Integral; - using unsigned_value_type = std::make_unsigned_t; - - void parse(format_spec_parse_context& ctx) - { - m_spec.parse(ctx); - std::tie(m_base, m_uppercase) = get_base(m_spec.type_char_or(U'd')); - - if(m_spec.sign() == format_sign::default_sign) - m_spec.sign(format_sign::negative); - if(m_spec.width() != 0) - { - if(m_spec.align() == format_align::default_align) - m_spec.align(format_align::right); - if(!m_spec.has_fill()) - { - if(!(m_spec.align() == format_align::right && m_spec.fill_zero())) - m_spec.fill(U' '); - } - } - } - template - void format(value_type val, Context& ctx) const - { - format_impl( - val, ctx, - m_spec, m_base, m_uppercase - ); - } - - // return base of the integer - // the second Boolean value indicates whether use the uppercase letters for hexadecimal output - static std::pair get_base(char32_t type) - { - if(type == U'd') - return std::make_pair(10, false); - else if(type == U'x') - return std::make_pair(16, false); - else if(type == U'X') - return std::make_pair(16, true); - else if(type == U'b') - return std::make_pair(2, false); - else if(type == U'o') - return std::make_pair(8, false); - else - { - throw invalid_format("invalid type '" + std::string(utf8::codepoint(type)) + "' for integer"); - } - } - - // Internal API - // This function can be used by other formatter (e.g. formatter for single character) - // when they need to redirect their outputs to integral formatter. - // For example, when character formatter needs to output the code point of character - // it can redirect its parsed specification to here - template - static void format_impl( - value_type val, Context& ctx, - const common_format_spec& spec, unsigned int base, bool uppercase) - { - std::size_t len = 0; - if(spec.sign() == format_sign::negative) - { - if constexpr(std::is_signed_v) - { - if(val < 0) - ++len; - } - } - else - ++len; - if(spec.alternate_form() && base != 10) - len += 2; - std::size_t raw_num_len = raw_formatted_size(val, base); - len += raw_num_len; - - using context_traits = format_context_traits; - - std::size_t fill_front = 0; - std::size_t fill_back = 0; - if(spec.has_fill()) - { - std::tie(fill_front, fill_back) = detail::calc_fill_width( - spec.align(), - spec.width(), - len - ); - } - - if(fill_front != 0) - context_traits::append(ctx, spec.fill(), fill_front); - - switch(spec.sign()) - { - case format_sign::negative: - if(val < 0) - context_traits::append(ctx, '-'); - break; - case format_sign::positive: - if(val < 0) - context_traits::append(ctx, '-'); - else - context_traits::append(ctx, '+'); - break; - case format_sign::space: - if(val < 0) - context_traits::append(ctx, '-'); - else - context_traits::append(ctx, ' '); - break; - } - if(spec.alternate_form()) - { - switch(base) - { - case 2: - context_traits::append(ctx, "0b"); - break; - [[unlikely]] case 8: - context_traits::append(ctx, "0o"); - break; - case 16: - if(uppercase) - context_traits::append(ctx, "0X"); - else - context_traits::append(ctx, "0x"); - break; - } - } - - if(!spec.has_fill() && spec.fill_zero()) - { - using enum format_align; - if(spec.align() == right || spec.align() == default_align) - { - std::size_t to_fill = spec.width(); - to_fill = raw_num_len > to_fill ? 0 : to_fill - raw_num_len; - context_traits::append(ctx, '0', to_fill); - } - } - - unsigned_value_type uval = val < 0 ? -val : val; - char buf[sizeof(value_type) * 8]; - std::size_t buf_idx = raw_num_len; - do - { - unsigned_value_type rem = uval % base; - uval /= base; - buf[--buf_idx] = map_char(rem, uppercase); - } while(uval != 0); - - context_traits::append(ctx, buf, buf + raw_num_len); - - if(fill_back != 0) - context_traits::append(ctx, spec.fill(), fill_back); - } - - private: - unsigned int m_base = 10; - bool m_uppercase = false; - common_format_spec m_spec; - - static std::size_t raw_formatted_size(value_type val, unsigned int base) noexcept - { - if constexpr(std::is_signed_v) - { - if(val < 0) - val = -val; - } - - unsigned_value_type uval = static_cast(val); - std::size_t counter = 1; - while(uval /= base) - ++counter; - return counter; - } - }; - - template <> - class formatter - { - public: - void parse(format_spec_parse_context& ctx) - { - m_spec.parse(ctx); - if(m_spec.align() == format_align::default_align) - m_spec.align(format_align::left); - } - template - void format(utf8::codepoint val, Context& ctx) - { - using context_traits = format_context_traits; - - if(auto type = m_spec.type_char_or(U'c'); type != U'c') - { - using int_formatter = formatter; - auto [base, uppercase] = int_formatter::get_base(type); - - int_formatter::format_impl( - static_cast(val.to_int().first), ctx, - m_spec, base, uppercase - ); - return; - } - - std::size_t fill_front = 0; - std::size_t fill_back = 0; - if(std::size_t w = m_spec.width(); w != 0) - { - std::tie(fill_front, fill_back) = detail::calc_fill_width( - m_spec.align(), - w, - val.estimate_width() - ); - } - - if(fill_front != 0) - context_traits::append(ctx, m_spec.fill_or(U' '), fill_front); - - context_traits::append(ctx, val); - - if(fill_back != 0) - context_traits::append(ctx, m_spec.fill_or(U' '), fill_back); - } - - private: - common_format_spec m_spec; - }; - template <> - class formatter - { - public: - void parse(format_spec_parse_context& ctx) - { - m_spec.parse(ctx); - if(m_spec.type_char_or(U's') != U's') - throw invalid_format("invalid string format"); - if(m_spec.width() != 0 && !m_spec.has_fill()) - { - if(m_spec.fill_zero()) - m_spec.fill(U'0'); - else - m_spec.fill(U' '); - } - if(m_spec.align() == format_align::default_align) - m_spec.align(format_align::left); - } - template - void format(const string_container& val, Context& ctx) - { - format_impl(val, ctx, m_spec); - } - - // Internal API - template - static void format_impl( - const string_container& val, Context& ctx, - const common_format_spec& spec - ) { - using context_traits = format_context_traits; - - auto [str_end, est_width] = find_str_end(val, spec.precision()); - - std::size_t fill_front = 0; - std::size_t fill_back = 0; - if(std::size_t w = spec.width(); w != 0) - { - std::tie(fill_front, fill_back) = detail::calc_fill_width( - spec.align(), - w, - est_width - ); - } - - if(fill_front != 0) - context_traits::append(ctx, spec.fill(), fill_front); - - for(auto it = val.begin(); it != str_end; ++it) - context_traits::append(ctx, *it); - - if(fill_back != 0) - context_traits::append(ctx, spec.fill(), fill_back); - } - - private: - common_format_spec m_spec; - - static std::pair find_str_end( - const string_container& str, - std::size_t precision - ) { - std::size_t current_width = 0; - auto it = str.begin(); - const auto end = str.end(); - for(; it != end; ++it) - { - std::size_t est_cp_width = (*it).estimate_width(); - if(current_width + est_cp_width > precision) - break; - current_width += est_cp_width; - } - - return std::make_pair(it, current_width); - } - }; - - template <> - class formatter - { - public: - void parse(format_spec_parse_context& ctx) - { - m_spec.parse(ctx); - } - template - void format(bool val, Context& ctx) - { - if(char32_t type = m_spec.type_char_or(U's'); type == U's') - { - using str_formatter = formatter; - if(m_spec.use_locale()) - { - str_formatter::format_impl( - get_name(val, ctx.getloc()), ctx, - m_spec - ); - } - else - { - str_formatter::format_impl( - get_name(val), ctx, - m_spec - ); - } - } - else - { - using int_formatter = formatter; - auto [base, uppercase] = int_formatter::get_base(type); - int_formatter::format_impl( - static_cast(val), ctx, - m_spec, base, uppercase - ); - } - } - - [[nodiscard]] - static string_container get_name(bool val) noexcept - { - if(val) - { - return "true"; - } - else - { - return "false"; - } - } - [[nodiscard]] - static string_container get_name(bool val, const std::locale& loc) - { - auto& numpunct_facet = std::use_facet>(loc); - return val ? numpunct_facet.truename() : numpunct_facet.falsename(); - } - - private: - common_format_spec m_spec; - }; - - template <> - class formatter - { - public: - void parse(format_spec_parse_context& ctx) - { - m_spec.parse(ctx); - if(m_spec.type_char_or(U'p') != U'p') - { - throw invalid_format("invalid pointer format"); - } - - m_spec.type_char(U'x'); - m_spec.alternate_form(true); - } - template - void format(const void* val, Context& ctx) - { - using int_formatter = formatter; - int_formatter::format_impl( - reinterpret_cast(val), ctx, - m_spec, 16, false - ); - } - - private: - common_format_spec m_spec; - }; - - namespace detail - { - class floating_pointer_formatter_base - { - public: - static void nan_to_chars(char* buf, bool uppercase) noexcept - { - if(uppercase) - { - buf[0] = 'N'; - buf[1] = 'A'; - buf[2] = 'N'; - } - else - { - buf[0] = 'n'; - buf[1] = 'a'; - buf[2] = 'n'; - } - } - static void inf_to_chars(char* buf, bool uppercase) noexcept - { - if(uppercase) - { - buf[0] = 'I'; - buf[1] = 'N'; - buf[2] = 'F'; - } - else - { - buf[0] = 'i'; - buf[1] = 'n'; - buf[2] = 'f'; - } - } - }; - } - - template - class formatter : public detail::floating_pointer_formatter_base - { - public: - using value_type = Float; - - void parse(format_spec_parse_context& ctx) - { - m_spec.parse(ctx); - if(!m_spec.has_type_char()) - return; - if(char32_t type = m_spec.type_char(); type == U'a' || type == U'A') - { - m_chars_fmt = std::chars_format::hex; - if(type == 'A') - m_uppercase = true; - } - else if(type == 'e' || type == 'E') - { - m_chars_fmt = std::chars_format::scientific; - if(m_spec.precision() == -1) - m_spec.precision(6); - if(type == 'E') - m_uppercase = true; - } - else if(type == 'f' || type == 'F') - { - m_chars_fmt = std::chars_format::fixed; - if(m_spec.precision() == -1) - m_spec.precision(6); - } - else if(type == 'g' || type == 'G') - { - m_chars_fmt = std::chars_format::general; - if(m_spec.precision() == -1) - m_spec.precision(6); - if(type == 'G') - m_uppercase = true; - } - - if(m_spec.width() != 0) - { - if(m_spec.align() == format_align::default_align) - m_spec.align(format_align::right); - if(!m_spec.has_fill()) - { - if(!(m_spec.align() == format_align::right && m_spec.fill_zero())) - m_spec.fill(U' '); - } - } - } - template - void format(Float val, Context& ctx) - { - format_impl( - val, ctx, - m_spec, m_chars_fmt, m_uppercase - ); - } - - template - static void format_impl( - value_type val, Context& ctx, - const common_format_spec& spec, std::chars_format chars_fmt, bool uppercase - ) { - char buf[128]; - char* p_end = buf; - std::size_t raw_num_len = 0; - bool is_special_val = false; - - if(std::isnan(val)) - { - nan_to_chars(buf, uppercase); - p_end += 3; - is_special_val = true; - } - else if(std::isinf(val)) - { - inf_to_chars(buf, uppercase); - p_end += 3; - is_special_val = true; - } - else - { - if(spec.precision() == -1) - { - std::to_chars_result result = std::to_chars( - buf, buf + 128, - val, - chars_fmt - ); - if(result.ec != std::errc()) - { - throw std::system_error(std::make_error_code(result.ec)); - } - p_end = result.ptr; - } - else - { - std::to_chars_result result = std::to_chars( - buf, buf + 128, - val, - chars_fmt, - static_cast(spec.precision()) - ); - if(result.ec != std::errc()) - { - throw std::system_error(std::make_error_code(result.ec)); - } - p_end = result.ptr; - } - - raw_num_len = p_end - buf; - - if(uppercase) - { - for(std::size_t i = 0; i < p_end - buf; ++i) - { - if('a' <= buf[i] && buf[i] <= 'z') - { - buf[i] -= 'a' - 'A'; - } - } - } - } - - std::size_t num_len = 0; - char sign_ch = get_sign(spec.sign(), std::signbit(val)); - if(sign_ch != '\0') - ++num_len; - num_len += p_end - buf; - - std::size_t fill_front = 0; - std::size_t fill_back = 0; - if(std::size_t w = spec.width(); w != 0) - { - std::tie(fill_front, fill_back) = detail::calc_fill_width( - spec.align(), - w, - num_len - ); - } - - using context_traits = format_context_traits; - - if(fill_front != 0) - context_traits::append(ctx, spec.fill(), fill_front); - - if(!spec.has_fill() && spec.fill_zero() && !is_special_val) - { - using enum format_align; - if(spec.align() == right || spec.align() == default_align) - { - std::size_t to_fill = spec.width(); - to_fill = raw_num_len > to_fill ? 0 : to_fill - raw_num_len; - context_traits::append(ctx, '0', to_fill); - } - } - - if(sign_ch != '\0') - context_traits::append(ctx, sign_ch); - context_traits::append(ctx, buf, p_end); - - if(fill_back != 0) - context_traits::append(ctx, spec.fill(), fill_back); - } - - private: - common_format_spec m_spec; - std::chars_format m_chars_fmt = std::chars_format::general; - bool m_uppercase = false; - - // returns null character when sign character is not needed - static char get_sign(format_sign sign, bool sign_bit) - { - switch(sign) - { - using enum format_sign; - case positive: - if(sign_bit) - return '-'; - else - return '+'; - - default: - case default_sign: - [[likely]] case negative: - if(sign_bit) - return '-'; - else - return '\0'; - - case space: - if(sign_bit) - return '-'; - else - return ' '; - } - } - }; } diff --git a/include/papilio/locale.hpp b/include/papilio/locale.hpp index 5372e8b..563a797 100644 --- a/include/papilio/locale.hpp +++ b/include/papilio/locale.hpp @@ -27,7 +27,9 @@ namespace papilio return *this; } + [[nodiscard]] std::locale get() const; + operator std::locale() const { return get(); diff --git a/include/papilio/macros.hpp b/include/papilio/macros.hpp index b1b7d5b..d962f5c 100644 --- a/include/papilio/macros.hpp +++ b/include/papilio/macros.hpp @@ -3,6 +3,8 @@ #include +#define PAPILIO_NS ::papilio:: + #ifdef _MSC_VER # define PAPILIO_COMPILER_MSVC 1 #endif diff --git a/include/papilio/print.hpp b/include/papilio/print.hpp index c9b0bc4..63cc8e0 100644 --- a/include/papilio/print.hpp +++ b/include/papilio/print.hpp @@ -7,12 +7,12 @@ namespace papilio { - void vprint(std::FILE* file, std::string_view fmt, const dynamic_format_arg_store& store); - void vprintln(std::FILE* file, std::string_view fmt, const dynamic_format_arg_store& store); - void vprint_conv(std::FILE* file, std::string_view fmt, const dynamic_format_arg_store& store); - void vprintln_conv(std::FILE* file, std::string_view fmt, const dynamic_format_arg_store& store); - void vprint_conv(std::string_view fmt, const dynamic_format_arg_store& store); - void vprintln_conv(std::string_view fmt, const dynamic_format_arg_store& store); + void vprint(std::FILE* file, std::string_view fmt, const dynamic_format_args& store); + void vprintln(std::FILE* file, std::string_view fmt, const dynamic_format_args& store); + void vprint_conv(std::FILE* file, std::string_view fmt, const dynamic_format_args& store); + void vprintln_conv(std::FILE* file, std::string_view fmt, const dynamic_format_args& store); + void vprint_conv(std::string_view fmt, const dynamic_format_args& store); + void vprintln_conv(std::string_view fmt, const dynamic_format_args& store); template void print(std::FILE* file, std::string_view fmt, Args&&... args) @@ -38,7 +38,7 @@ namespace papilio void println(std::FILE* file); void println(); - void vprint(std::ostream& os, std::string_view fmt, const dynamic_format_arg_store& store); + void vprint(std::ostream& os, std::string_view fmt, const dynamic_format_args& store); template void print(std::ostream& os, std::string_view fmt, Args&&... args) diff --git a/include/papilio/script.hpp b/include/papilio/script.hpp index f826f26..35bb1a3 100644 --- a/include/papilio/script.hpp +++ b/include/papilio/script.hpp @@ -600,7 +600,7 @@ namespace papilio::script { public: context() = delete; - context(const dynamic_format_arg_store& arg_store) + context(const dynamic_format_args& arg_store) : m_arg_store(arg_store) {} [[nodiscard]] @@ -615,7 +615,7 @@ namespace papilio::script } [[nodiscard]] - const dynamic_format_arg_store& get_store() const noexcept + const dynamic_format_args& get_store() const noexcept { return m_arg_store; } @@ -660,7 +660,7 @@ namespace papilio::script private: stack_type m_var_stack; - dynamic_format_arg_store m_arg_store; + dynamic_format_args m_arg_store; }; class base @@ -859,13 +859,13 @@ namespace papilio::script using string_type = std::basic_string; using string_view_type = std::basic_string_view; - string_type run(string_view_type src, const dynamic_format_arg_store& args); + string_type run(string_view_type src, const dynamic_format_args& args); template string_type run(string_view_type src, Args&&... args) { return run( src, - dynamic_format_arg_store(papilio::make_format_args(std::forward(args)...)) + dynamic_format_args(papilio::make_format_args(std::forward(args)...)) ); } diff --git a/include/papilio/util/chrono.hpp b/include/papilio/util/chrono.hpp deleted file mode 100644 index be257cb..0000000 --- a/include/papilio/util/chrono.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include -#include -#include -#include "../core.hpp" - - -namespace papilio -{ - template <> - class formatter - { - public: - void parse(format_spec_parse_context& ctx) - { - m_fmt = std::string_view(ctx); - } - template - void format(const std::tm& val, Context& ctx) - { - using context_traits = format_context_traits; - - if(m_fmt.empty()) - { - context_traits::append(ctx, std::asctime(&val)); - } - else - { - char buf[128]; - std::size_t len = std::strftime(buf, 128, m_fmt.c_str(), &val); - context_traits::append(ctx, buf, buf + len); - } - } - - private: - std::string m_fmt; - }; -} diff --git a/include/papilio/util/join.hpp b/include/papilio/util/join.hpp index 76b14dc..9fbfffa 100644 --- a/include/papilio/util/join.hpp +++ b/include/papilio/util/join.hpp @@ -77,83 +77,4 @@ namespace papilio { return tuple_joiner>(p, sep); } - - template - class formatter> - { - public: - using joiner_type = joiner; - using value_type = std::ranges::range_value_t; - using underlying_formatter = formatter_traits::formatter_type; - - void parse(format_spec_parse_context& spec_ctx) - { - m_fmt.parse(spec_ctx); - } - template - void format(const joiner& rng, Context& ctx) - { - using context_traits = format_context_traits; - - bool first = true; - for(auto&& i : rng.get_range()) - { - if(!first) - { - context_traits::append(ctx, rng.get_separator()); - } - m_fmt.format(i, ctx); - first = false; - } - } - - private: - underlying_formatter m_fmt; - }; - template - class formatter> - { - public: - using joiner_type = tuple_joiner; - using tuple_type = Tuple; - using string_view_type = std::basic_string_view; - - void parse(format_spec_parse_context& spec_ctx) - { - if(!string_view_type(spec_ctx).empty()) - { - throw invalid_format( - "invalid format specification for tuple joiner: " + - std::string(spec_ctx) - ); - } - } - template - void format(const joiner_type& tp_joiner, Context& ctx) - { - auto helper = [&] (std::index_sequence) - { - (format_helper(tp_joiner, ctx), ...); - }; - helper(std::make_index_sequence>()); - } - - private: - template - void format_helper(const joiner_type& tp_joiner, Context& ctx) - { - using context_traits = format_context_traits; - const auto& val = std::get(tp_joiner.get_tuple()); - - // TODO: Optimization - if constexpr(Index != 0) - { - context_traits::append(ctx, tp_joiner.get_separator()); - } - context_traits::advance_to( - ctx, - papilio::format_to(context_traits::out(ctx), "{}", val) - ); - } - }; } diff --git a/src/core.cpp b/src/core.cpp index b9b32c1..ed0a245 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -8,390 +8,6 @@ namespace papilio { - namespace detail - { - std_format_spec parse_std_format_spec(format_spec_parse_context& ctx) - { - std_format_spec result; - - auto it = ctx.rbegin(); - if(it == ctx.rend()) - return result; - - // type - if('A' <= *it && *it <= 'z' && *it != 'L') - { - result.type_char = *it; - ++it; - } - if(it == ctx.rend()) - return result; - - // locale - if(*it == 'L') - { - result.use_locale = true; - ++it; - } - if(it == ctx.rend()) - return result; - - if(is_digit(*it)) - { - auto digit_end = std::find_if( - std::next(it), ctx.rend(), is_digit - ); - std::string_view digits( - std::to_address(digit_end), - std::to_address(it) - ); - if(digits.length() > 1 && digits[0] == '0') - { - result.fill_zero = true; - digits = digits.substr(1); - } - auto conv_result = std::from_chars( - digits.data(), digits.data() + digits.size(), - result.width, - 10 - ); - if(conv_result.ec != std::errc()) - { - throw invalid_format("invalid format width"); - } - it = digit_end; - } - if(it == ctx.rend()) - return result; - - return result; - } - } - - namespace detail - { - static bool is_special_spec_ch(char32_t ch) noexcept - { - return - ch == '}' || - ch == ']' || - ch == '{' || - ch == '['; - } - - static bool is_spec_type_char(char32_t ch) noexcept - { - char32_t type_char_list[] = { - 's', // string - 'b', 'B', 'c', 'o', 'd', 'x', 'X', // integer and char - 'a', 'A', 'e', 'E', 'f', 'F', 'g', 'G', // floating point - 'p' // pointer - }; - - namespace stdr = std::ranges; - return stdr::find(type_char_list, ch) != stdr::end(type_char_list); - } - - // WARNING: Please remember that the base iterator of a reverse one - // points to the position after the reverse iterator - std::string_view to_string_view( - string_container::const_reverse_iterator start, - string_container::const_reverse_iterator stop - ) { - auto begin = stop.base(); - auto end = start.base(); - - using std::to_address; - return std::string_view( - to_address(begin), - to_address(end) - ); - } - - template - std::pair> rparse_value( - ReverseIterator start, - ReverseIterator stop, - std::size_t& val_out - ) { - char32_t last_ch = *start; - if(last_ch == '}') - { - ++start; - auto field_end = start; - auto field_begin = script::rfind_field_begin(start, stop); - ++field_end; - std::string_view view = to_string_view( - field_begin, - start - ); - - return std::make_pair(field_end, view); - } - else if(is_digit(last_ch)) - { - auto next_it = start; - for(auto it = start; it != stop; ++it) - { - char32_t ch = *it; - if(U'1' <= ch && ch <= U'9') - next_it = it; - else if(ch != U'0') - break; - } - - ++next_it; - std::string_view view = to_string_view( - start, - next_it - ); - - std::size_t value = 0; - auto conv_result = std::from_chars( - view.data(), - view.data() + view.size(), - value, - 10 - ); - - val_out = value; - return std::make_pair(next_it, std::nullopt); - } - - return std::make_pair(start, std::nullopt); - } - - class common_format_spec_parse_context - { - public: - using char_type = char; - using string_view_type = std::basic_string_view; - using iterator = string_view_type::const_iterator; - using reverse_iterator = string_view_type::const_reverse_iterator; - - void parse(format_spec_parse_context& ctx) - { - parse_string(string_view_type(ctx)); - eval_dyn_val(ctx); - } - void parse_string(string_container view) - { - if(view.empty()) - return; - assert(view.is_borrowed()); - - auto it = view.crbegin(); - auto rend = view.crend(); - -#define PAPILIO_CHECK_SPEC_ITER(iter) if(iter == rend) return; - - if(utf8::codepoint cp = *it; is_spec_type_char(cp)) - { - spec.type_char(cp); - ++it; - } - - PAPILIO_CHECK_SPEC_ITER(it); - if(*it == 'L') - { - spec.use_locale(true); - ++it; - } - - PAPILIO_CHECK_SPEC_ITER(it); - { - std::optional dyn_val; - std::size_t static_val = 0; - auto tmp_it = it; - std::tie(tmp_it, dyn_val) = rparse_value(it, rend, static_val); - if(it == tmp_it) - goto parse_value_end; - it = tmp_it; - - bool has_dot = false; - if(it != rend && *it == U'.') - { - has_dot = true; - ++it; - } - - if(!has_dot) - { - if(dyn_val.has_value()) - m_dyn_width = dyn_val; - else - spec.width(static_val); - } - else - { - if(dyn_val.has_value()) - m_dyn_precision = dyn_val; - else - spec.precision(static_val); - - PAPILIO_CHECK_SPEC_ITER(it); - std::tie(it, dyn_val) = rparse_value(it, rend, static_val); - - if(dyn_val.has_value()) - m_dyn_width = dyn_val; - else - spec.width(static_val); - } - } - parse_value_end: - - PAPILIO_CHECK_SPEC_ITER(it); - if(*it == U'0') - { - spec.fill_zero(true); - ++it; - } - PAPILIO_CHECK_SPEC_ITER(it); - if(*it == U'#') - { - spec.alternate_form(true); - ++it; - } - PAPILIO_CHECK_SPEC_ITER(it); - if(char32_t ch = *it; common_format_spec::is_sign_spec(ch)) - { - spec.sign(common_format_spec::get_sign(ch)); - ++it; - } - PAPILIO_CHECK_SPEC_ITER(it); - if(char32_t ch = *it; common_format_spec::is_align_spec(ch)) - { - spec.align(common_format_spec::get_align(ch)); - ++it; - } - PAPILIO_CHECK_SPEC_ITER(it); - spec.fill(*it); - ++it; - - if(it != rend) - { - throw invalid_format( - "invalid format specification for common type: " + - std::string(view) - ); - } - -#undef PAPILIO_CHECK_SPEC_ITER - } - - void eval_dyn_val(format_spec_parse_context& spec_ctx) - { - script::interpreter intp; - if(m_dyn_width.has_value()) - { - std::optional default_arg_id; - if(!spec_ctx.manual_indexing()) - { - default_arg_id = spec_ctx.current_arg_id(); - spec_ctx.next_arg_id(); - } - auto [idx, acc] = intp.access(*m_dyn_width, default_arg_id); - std::size_t val = acc.access(spec_ctx.get_store().get(idx)).as_variable().as(); - spec.width(val); - } - if(m_dyn_precision.has_value()) - { - std::optional default_arg_id; - if(!spec_ctx.manual_indexing()) - { - default_arg_id = spec_ctx.current_arg_id(); - spec_ctx.next_arg_id(); - } - auto [idx, acc] = intp.access(*m_dyn_precision, default_arg_id); - std::size_t val = acc.access(spec_ctx.get_store().get(idx)).as_variable().as(); - spec.precision(val); - } - } - - common_format_spec spec; - - private: - std::optional m_dyn_width; - std::optional m_dyn_precision; - bool m_has_fill_zero = false; - }; - - template - std::optional next_char(Iterator current, Iterator end) - { - ++current; - if(current == end) - return std::nullopt; - return *current; - } - - template - std::pair parse_spec_value(format_spec_parse_context& spec_ctx, Iterator begin, Iterator end) - { - if(*begin == '{') - { - auto field_end = script::find_field_end(std::next(begin), end); - if(field_end == end) - { - throw invalid_format("missing right brace: " + std::string(spec_ctx)); - } - - std::string_view view( - std::next(begin), - field_end - ); - script::interpreter intp; - std::optional default_arg_id; - if(!spec_ctx.manual_indexing()) - { - default_arg_id = spec_ctx.current_arg_id(); - spec_ctx.next_arg_id(); - } - auto [idx, arg_access] = intp.access(view, default_arg_id); - - assert(spec_ctx.has_store()); - format_arg arg = arg_access.access(spec_ctx.get_store()[idx]); - - return std::make_pair( - arg.as_variable().as(), - ++field_end - ); - } - else - { - assert(is_digit(*begin)); - auto digit_end = std::find_if_not( - std::next(begin), end, - &is_digit - ); - std::size_t val = 0; - std::from_chars_result result = std::from_chars( - std::to_address(begin), - std::to_address(digit_end), - val, - 10 - ); - assert(result.ptr == std::to_address(digit_end)); - if(result.ec != std::errc()) - { - throw std::system_error(std::make_error_code(result.ec)); - } - - return std::make_pair( - val, digit_end - ); - } - } - } - - void common_format_spec::parse(format_spec_parse_context& spec_ctx) - { - detail::common_format_spec_parse_context parse_ctx; - parse_ctx.parse(spec_ctx); - - *this = parse_ctx.spec; - } - bool attribute_name::validate(string_view_type name) noexcept { bool first = true; @@ -517,7 +133,7 @@ namespace papilio namespace detail { - const format_arg& format_arg_store_base::get(const indexing_value& idx) const + const format_arg& format_args_base::get(const indexing_value& idx) const { auto visitor = [&](auto&& v)->const format_arg& { @@ -544,11 +160,11 @@ namespace papilio return std::visit(visitor, idx.to_underlying()); } - bool format_arg_store_base::check(size_type i) const noexcept + bool format_args_base::check(size_type i) const noexcept { return i < size(); } - bool format_arg_store_base::check(const indexing_value& idx) const noexcept + bool format_args_base::check(const indexing_value& idx) const noexcept { auto visitor = [this](auto&& v)->bool { @@ -574,23 +190,23 @@ namespace papilio return std::visit(visitor, idx.to_underlying()); } - void format_arg_store_base::raise_index_out_of_range() + void format_args_base::raise_index_out_of_range() { throw std::out_of_range("index out of range"); } - void format_arg_store_base::raise_invalid_named_argument() + void format_args_base::raise_invalid_named_argument() { throw std::out_of_range("invalid named argument"); } } - const format_arg& mutable_format_arg_store::get(size_type i) const + const format_arg& mutable_format_args::get(size_type i) const { if(i >= m_args.size()) raise_index_out_of_range(); return m_args[i]; } - const format_arg& mutable_format_arg_store::get(string_view_type key) const + const format_arg& mutable_format_args::get(string_view_type key) const { auto it = m_named_args.find(key); if(it == m_named_args.end()) @@ -598,12 +214,12 @@ namespace papilio return it->second; } - bool mutable_format_arg_store::check(string_view_type k) const noexcept + bool mutable_format_args::check(string_view_type k) const noexcept { return m_named_args.contains(k); } - format_parse_context::format_parse_context(string_view_type str, const dynamic_format_arg_store& store) + format_parse_context::format_parse_context(string_view_type str, const dynamic_format_args& store) : m_view(str), m_store(store) { m_it = str.begin(); } diff --git a/src/format.cpp b/src/format.cpp index cf17b1b..1fa91f5 100644 --- a/src/format.cpp +++ b/src/format.cpp @@ -3,17 +3,17 @@ namespace papilio { - std::string vformat(std::string_view fmt, const dynamic_format_arg_store& store) + std::string vformat(std::string_view fmt, const dynamic_format_args& store) { std::string result; - vformat_to(std::back_inserter(result), fmt, store); + PAPILIO_NS vformat_to(std::back_inserter(result), fmt, store); return result; } - std::string vformat(const std::locale& loc, std::string_view fmt, const dynamic_format_arg_store& store) + std::string vformat(const std::locale& loc, std::string_view fmt, const dynamic_format_args& store) { std::string result; - vformat_to(std::back_inserter(result), loc, fmt, store); + PAPILIO_NS vformat_to(std::back_inserter(result), loc, fmt, store); return result; } @@ -45,46 +45,14 @@ namespace papilio }; } - std::size_t vformatted_size(std::string_view fmt, const dynamic_format_arg_store& store) + std::size_t vformatted_size(std::string_view fmt, const dynamic_format_args& store) { - auto result = vformat_to(detail::formatted_size_counter(), fmt, store); + auto result = PAPILIO_NS vformat_to(detail::formatted_size_counter(), fmt, store); return result.value; } - std::size_t vformatted_size(const std::locale& loc, std::string_view fmt, const dynamic_format_arg_store& store) + std::size_t vformatted_size(const std::locale& loc, std::string_view fmt, const dynamic_format_args& store) { - auto result = vformat_to(detail::formatted_size_counter(), loc, fmt, store); + auto result = PAPILIO_NS vformat_to(detail::formatted_size_counter(), loc, fmt, store); return result.value; } - - namespace detail - { - std::pair calc_fill_width(format_align align, std::size_t width, std::size_t current) - { - if(width <= current) - return std::make_pair(0, 0); - - std::size_t fill_front = 0; - std::size_t fill_back = 0; - std::size_t to_fill = width - current; - - using enum format_align; - switch(align) - { - case left: - fill_back = to_fill; - break; - - [[likely]] case right: - fill_front = to_fill; - break; - - case middle: - fill_front = to_fill / 2; // floor(to_fill / 2) - fill_back = to_fill / 2 + to_fill % 2; // ceil(to_fill / 2) - break; - } - - return std::make_pair(fill_front, fill_back); - } - } } diff --git a/src/print.cpp b/src/print.cpp index b303ccb..c1c8d1e 100644 --- a/src/print.cpp +++ b/src/print.cpp @@ -175,22 +175,22 @@ namespace papilio cfile_iterator m_underlying; }; - void vprint(std::FILE* file, std::string_view fmt, const dynamic_format_arg_store& store) + void vprint(std::FILE* file, std::string_view fmt, const dynamic_format_args& store) { - vformat_to(cfile_iterator(file), fmt, store); + PAPILIO_NS vformat_to(cfile_iterator(file), fmt, store); } - void vprintln(std::FILE* file, std::string_view fmt, const dynamic_format_arg_store& store) + void vprintln(std::FILE* file, std::string_view fmt, const dynamic_format_args& store) { - auto it = vformat_to(cfile_iterator(file), fmt, store); + auto it = PAPILIO_NS vformat_to(cfile_iterator(file), fmt, store); *it = '\n'; } - void vprint_conv(std::FILE* file, std::string_view fmt, const dynamic_format_arg_store& store) + void vprint_conv(std::FILE* file, std::string_view fmt, const dynamic_format_args& store) { - vformat_to(cfile_iterator_utf8(file), fmt, store); + PAPILIO_NS vformat_to(cfile_iterator_utf8(file), fmt, store); } - void vprintln_conv(std::FILE* file, std::string_view fmt, const dynamic_format_arg_store& store) + void vprintln_conv(std::FILE* file, std::string_view fmt, const dynamic_format_args& store) { - auto it = vformat_to(cfile_iterator_utf8(file), fmt, store); + auto it = PAPILIO_NS vformat_to(cfile_iterator_utf8(file), fmt, store); *it = '\n'; } @@ -202,13 +202,13 @@ namespace papilio return 0; #endif } - void vprint_conv(std::string_view fmt, const dynamic_format_arg_store& store) + void vprint_conv(std::string_view fmt, const dynamic_format_args& store) { - vformat_to(cfile_iterator_utf8(stdout, get_output_cp_win()), fmt, store); + PAPILIO_NS vformat_to(cfile_iterator_utf8(stdout, get_output_cp_win()), fmt, store); } - void vprintln_conv(std::string_view fmt, const dynamic_format_arg_store& store) + void vprintln_conv(std::string_view fmt, const dynamic_format_args& store) { - auto it = vformat_to(cfile_iterator_utf8(stdout, get_output_cp_win()), fmt, store); + auto it = PAPILIO_NS vformat_to(cfile_iterator_utf8(stdout, get_output_cp_win()), fmt, store); *it = '\n'; } @@ -222,7 +222,7 @@ namespace papilio println(stdout); } - void vprint(std::ostream& os, std::string_view fmt, const dynamic_format_arg_store& store) + void vprint(std::ostream& os, std::string_view fmt, const dynamic_format_args& store) { vformat_to(std::ostream_iterator(os), os.getloc(), fmt, store); } diff --git a/src/script.cpp b/src/script.cpp index 4500870..940fd75 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -499,7 +499,7 @@ namespace papilio::script return std::make_pair(static_cast(0), 0); } - interpreter::string_type interpreter::run(string_view_type src, const dynamic_format_arg_store& args) + interpreter::string_type interpreter::run(string_view_type src, const dynamic_format_args& args) { auto ex = compile(src); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 72f6676..4517689 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -10,8 +10,6 @@ macro(define_papilio_test test_name) gtest_discover_tests(${test_name}) endmacro() -define_papilio_test(test_chrono) - define_papilio_test(test_core) define_papilio_test(test_container) diff --git a/test/test_chrono.cpp b/test/test_chrono.cpp deleted file mode 100644 index 0308de0..0000000 --- a/test/test_chrono.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include -#include -#include - - -TEST(TestChrono, Tm) -{ - using namespace papilio; - - { - std::tm t{}; - t.tm_year = 2022 - 1900; - t.tm_mday = 1; - t.tm_wday = 6; - - std::string result = papilio::format("{}", t); - EXPECT_EQ(result, "Sat Jan 1 00:00:00 2022\n"); - result = papilio::format("{:%H:%M:%S}", t); - EXPECT_EQ(result, "00:00:00"); - } -} - -int main(int argc, char* argv[]) -{ - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/test/test_core.cpp b/test/test_core.cpp index 1f746f3..4759ed4 100644 --- a/test/test_core.cpp +++ b/test/test_core.cpp @@ -8,46 +8,6 @@ TEST(TestCore, Utilities) { using namespace std::literals; - { - using namespace papilio; - - static_assert(string_like); - static_assert(string_like); - static_assert(string_like); - static_assert(string_like); - static_assert(string_like); - static_assert(string_like); - } - - { - using namespace papilio; - - static_assert(!accessor_traits::has_index()); - static_assert(!accessor_traits::has_index()); - static_assert(accessor_traits::has_index()); - static_assert(accessor_traits::has_custom_index()); - static_assert(!accessor_traits::has_key()); - static_assert(accessor_traits::has_slice()); - static_assert(accessor_traits::has_custom_slice()); - - string_container test = "hello world"; - EXPECT_EQ(accessor_traits::get(test, 0), U'h'); - EXPECT_EQ(accessor_traits::get(test, slice(6, slice::npos)), "world"); - - EXPECT_EQ( - accessor_traits::get_arg(test, -1).as_variable(), - "d" - ); - EXPECT_EQ( - accessor_traits::get_attr(test, "length").as_variable(), - test.length() - ); - EXPECT_EQ( - accessor_traits::get_attr(test, "size").as_variable(), - test.size() - ); - } - { using namespace papilio; @@ -234,10 +194,10 @@ TEST(TestCore, MutableFormatArgStore) { using namespace papilio; - static_assert(format_arg_store); + static_assert(format_arg_store); { - mutable_format_arg_store store(1, "three"_a = 3, 2); + mutable_format_args store(1, "three"_a = 3, 2); EXPECT_EQ(store.size(), 2); EXPECT_EQ(store.named_size(), 1); @@ -266,11 +226,11 @@ TEST(TestCore, DynamicFormatArgStore) { using namespace papilio; - static_assert(format_arg_store); + static_assert(format_arg_store); { - mutable_format_arg_store underlying_store; - dynamic_format_arg_store dyn_store(underlying_store); + mutable_format_args underlying_store; + dynamic_format_args dyn_store(underlying_store); EXPECT_EQ(&dyn_store.to_underlying(), &underlying_store); } @@ -281,7 +241,7 @@ TEST(TestCore, FormatContext) { std::string result; - mutable_format_arg_store store; + mutable_format_args store; basic_format_context ctx( std::back_inserter(result), store @@ -304,7 +264,7 @@ TEST(TestCore, FormatContext) { std::string result; - mutable_format_arg_store store; + mutable_format_args store; basic_format_context ctx( std::back_inserter(result), store @@ -326,72 +286,6 @@ TEST(TestCore, FormatContext) EXPECT_EQ(result, (const char*)u8"ää"); } } -TEST(TestCore, CommonFormatSpec) -{ - using namespace papilio; - - auto helper = [](std::string_view fmt, Args&&... args) - { - common_format_spec spec; - mutable_format_arg_store store(std::forward(args)...); - format_parse_context parse_ctx(fmt, store); - parse_ctx.next_arg_id(); - format_spec_parse_context spec_ctx(parse_ctx, fmt.substr(2, fmt.size() - 3)); - - spec.parse(spec_ctx); - return spec; - }; - - { - common_format_spec spec; - spec = helper("{:}", 0); - EXPECT_FALSE(spec.has_type_char()); - EXPECT_EQ(spec.type_char_or('d'), 'd'); - - spec = helper("{: }", 0); - EXPECT_FALSE(spec.has_type_char()); - EXPECT_EQ(spec.sign(), format_sign::space); - - spec = helper("{:d}", 0); - EXPECT_EQ(spec.precision(), -1); - EXPECT_EQ(spec.width(), 0); - EXPECT_EQ(spec.type_char(), 'd'); - - spec = helper("{:.10d}", 0); - EXPECT_EQ(spec.precision(), 10); - EXPECT_EQ(spec.type_char(), 'd'); - - spec = helper("{:10d}", 0); - EXPECT_EQ(spec.width(), 10); - EXPECT_EQ(spec.type_char(), 'd'); - - spec = helper("{:10.6d}", 0); - EXPECT_EQ(spec.width(), 10); - EXPECT_EQ(spec.precision(), 6); - EXPECT_EQ(spec.type_char(), 'd'); - - spec = helper("{: >}", ""); - EXPECT_FALSE(spec.has_type_char()); - EXPECT_EQ(spec.fill(), ' '); - EXPECT_EQ(spec.align(), format_align::right); - } - { - common_format_spec spec; - - spec = helper("{:.{}d}", 0, 10); - EXPECT_EQ(spec.precision(), 10); - EXPECT_EQ(spec.type_char(), 'd'); - - spec = helper("{:{}d}", 0, 10); - EXPECT_EQ(spec.width(), 10); - EXPECT_EQ(spec.type_char(), 'd'); - - spec = helper("{:{}.{}d}", 0, 10, 6); - EXPECT_EQ(spec.width(), 10); - EXPECT_EQ(spec.precision(), 6); - EXPECT_EQ(spec.type_char(), 'd'); - } -} int main(int argc, char* argv[]) { diff --git a/test/test_format.cpp b/test/test_format.cpp index 468f342..557eb75 100644 --- a/test/test_format.cpp +++ b/test/test_format.cpp @@ -2,311 +2,9 @@ #include -TEST(TestFormat, Formatter) -{ - using namespace papilio; - - { - static_assert(formatter_traits::has_formatter()); - static_assert(formatter_traits::has_formatter()); - static_assert(formatter_traits::has_formatter()); - static_assert(formatter_traits::has_formatter()); - static_assert(formatter_traits::has_formatter()); - static_assert(formatter_traits::has_formatter()); - - EXPECT_EQ(format("{}", 0), "0"); - EXPECT_EQ(format("{}", 10), "10"); - EXPECT_EQ(format("{}", 0u), "0"); - EXPECT_EQ(format("{}", 10u), "10"); - - EXPECT_EQ(format("{:x}", 0xF), "f"); - EXPECT_EQ(format("{:b}", 0b10), "10"); - EXPECT_EQ(format("{:#b}", 0b10), "0b10"); - EXPECT_EQ(format("{:x}", 0xFF), "ff"); - EXPECT_EQ(format("{:X}", 0xFF), "FF"); - EXPECT_EQ(format("{:o}", 017), "17"); - EXPECT_EQ(format("{:#o}", 017), "0o17"); - - EXPECT_EQ(format("{:08x}", 0xff), "000000ff"); - - EXPECT_EQ(format("{:#08x}", 0), "0x00000000"); - EXPECT_EQ(format("{0:},{0:+},{0:-},{0: }", 1), "1,+1,1, 1"); - EXPECT_EQ(format("{0:},{0:+},{0:-},{0: }", -1), "-1,-1,-1,-1"); - EXPECT_EQ(format("{:6}", 42), " 42"); - EXPECT_EQ(format("{:06}", 42), "000042"); - EXPECT_EQ(format("{:*<6}", 1), "1*****"); - EXPECT_EQ(format("{:*>6}", 1), "*****1"); - EXPECT_EQ(format("{:*^6}", 1), "**1***"); - EXPECT_EQ(format("{:<06}", -42), "-42 "); // ignore zero - - EXPECT_EQ(format("{:}", U'A'), "A"); - EXPECT_EQ(format("{:*<6}", U'A'), "A*****"); - EXPECT_EQ(format("{:*^6}", U'A'), "**A***"); - EXPECT_EQ(format("{:*>6}", U'A'), "*****A"); - EXPECT_EQ(format("{:c}", U'A'), "A"); - EXPECT_EQ(format("{:}", U'我'), (const char*)u8"我"); - EXPECT_EQ(format("{:*<6}", U'我'), (const char*)u8"我****"); - EXPECT_EQ(format("{:*^6}", U'我'), (const char*)u8"**我**"); - EXPECT_EQ(format("{:*>6}", U'我'), (const char*)u8"****我"); - EXPECT_EQ(format("{:c}", U'我'), (const char*)u8"我"); - EXPECT_EQ(format("{:#x}", U'我'), "0x6211"); - EXPECT_EQ(format("{:d}", U'A'), "65"); - - EXPECT_EQ(format("{.length:06}", "ABCDEFGHIJKL"), "000012"); - } - - { - static_assert(formatter_traits::has_formatter()); - - EXPECT_EQ(format("{}", nullptr), "0x0"); - EXPECT_EQ(format("{:p}", nullptr), "0x0"); - EXPECT_EQ(format("{:#08p}", nullptr), "0x00000000"); - EXPECT_EQ(format("{:#08p}", (const void*)0xFFFF), "0x0000ffff"); - // redirection to other type is not allowed for pointer - EXPECT_THROW(format("{:08d}", nullptr), invalid_format); - } - - { - static_assert(formatter_traits::has_formatter()); - - EXPECT_EQ(format("{}", "hello"), "hello"); - EXPECT_EQ(format("{:6}", "hello"), "hello "); - EXPECT_EQ(format("{:>6}", "hello"), " hello"); - EXPECT_EQ(format("{:^6}", "hello"), "hello "); - EXPECT_EQ(format("{:.^5s}", (const char*)u8"我"), ".我.."); - EXPECT_EQ(format("{:.5s}", (const char*)u8"我我我"), "我我"); - EXPECT_EQ(format("{:.<5.5s}", (const char*)u8"我我我"), "我我."); - } - - { - static_assert(formatter_traits::has_formatter()); - - EXPECT_EQ(format("{}", true), "true"); - EXPECT_EQ(format("{}", false), "false"); - EXPECT_EQ(format("{:s}", true), "true"); - EXPECT_EQ(format("{:s}", false), "false"); - EXPECT_EQ(format("{:d}", true), "1"); - EXPECT_EQ(format("{:d}", false), "0"); - EXPECT_EQ(format("{:#02b}", true), "0b01"); - EXPECT_EQ(format("{:#02b}", false), "0b00"); - } - - { - static_assert(formatter_traits::has_formatter()); - static_assert(formatter_traits::has_formatter()); - static_assert(formatter_traits::has_formatter()); - - EXPECT_EQ(format("{}", 1.0), "1"); - EXPECT_EQ(format("{}", 1.5f), "1.5"); - EXPECT_EQ(format("{}", 1.5), "1.5"); - EXPECT_EQ(format("{}", 1.5L), "1.5"); - - const float pi = 3.14f; - EXPECT_EQ(format("{:10f}", pi), " 3.140000"); - EXPECT_EQ(format("{:{}f}", pi, 10), " 3.140000"); - EXPECT_EQ(format("{:.5f}", pi), "3.14000"); - EXPECT_EQ(format("{:.{}f}", pi, 5), "3.14000"); - EXPECT_EQ(format("{:10.5f}", pi), " 3.14000"); - EXPECT_EQ(format("{:{}.{}f}", pi, 10, 5), " 3.14000"); - - constexpr double inf = std::numeric_limits::infinity(); - constexpr double nan = std::numeric_limits::quiet_NaN(); - EXPECT_EQ(format("{0:},{0:+},{0:-},{0: }", inf), "inf,+inf,inf, inf"); - EXPECT_EQ(format("{0:},{0:+},{0:-},{0: }", nan), "nan,+nan,nan, nan"); - } -} -TEST(TestFormat, VFormat) -{ - using namespace papilio; - - { - std::string result = vformat("plain text", make_format_args()); - EXPECT_EQ(result, "plain text"); - - result = vformat("Count: {}", make_format_args(10)); - EXPECT_EQ(result, "Count: 10"); - - result = vformat("Bin {0:b}, Dec {0}, Hex {0:x}", make_format_args(0xF)); - EXPECT_EQ(result, "Bin 1111, Dec 15, Hex f"); - - std::string_view apple_fmt = "{} apple[if $0 != 1: 's']"; - result = vformat(apple_fmt, make_format_args(1)); - EXPECT_EQ(result, "1 apple"); - result = vformat(apple_fmt, make_format_args(2)); - EXPECT_EQ(result, "2 apples"); - - result = vformat("length of \"{0}\" is {0.length}", make_format_args("hello")); - EXPECT_EQ(result, "length of \"hello\" is 5"); - - result = vformat("{[:5]:}", make_format_args("hello world")); - EXPECT_EQ(result, "hello"); - result = vformat("{[-5:]:}", make_format_args("hello world")); - EXPECT_EQ(result, "world"); - result = vformat("{[0]:}", make_format_args("hello world")); - EXPECT_EQ(result, "h"); - result = vformat("{[-1]:}", make_format_args("hello world")); - EXPECT_EQ(result, "d"); - } -} -TEST(TestFormat, FormatTo) -{ - using namespace papilio; - - { - std::vector buf; - - format_to(std::back_inserter(buf), "plain text"); - EXPECT_EQ(std::string_view(buf.data(), buf.size()), "plain text"); - - buf.clear(); - buf.resize(4); - EXPECT_EQ(format_to(buf.begin(), "{}", 1234), buf.end()); - EXPECT_EQ(std::string_view(buf.data(), 4), "1234"); - } -} -TEST(TestFormat, FormatToN) -{ - using namespace papilio; - - { - std::vector buf; - buf.resize(10); - - auto result = format_to_n(buf.begin(), 5, "plain text"); - EXPECT_EQ(result.out, buf.begin() + 5); - EXPECT_EQ(result.size, 5); - EXPECT_EQ(std::string_view(buf.data(), result.size), "plain"); - - buf.clear(); - buf.resize(10); - result = format_to_n(buf.begin(), buf.size(), "{}", 1234); - EXPECT_EQ(result.out, buf.begin() + 4); - EXPECT_EQ(result.size, 4); - EXPECT_EQ(std::string_view(buf.data(), result.size), "1234"); - } - - { - std::size_t size = formatted_size("{}", 123); - EXPECT_EQ(size, 3); - - std::vector buf(size); - format_to_n(buf.begin(), size, "{}", 123); - EXPECT_EQ(std::string_view(buf.data(), size), "123"); - } -} TEST(TestFormat, Format) { - using namespace papilio; - - EXPECT_EQ(format("plain text"), "plain text"); - EXPECT_EQ(format("{{}}"), "{}"); - EXPECT_EQ(format("[[]]"), "[]"); - - EXPECT_EQ(format("{}", true), "true"); - EXPECT_EQ(format("{}", false), "false"); - EXPECT_EQ(format("{}", nullptr), "0x0"); - EXPECT_EQ(format("{}", (const void*)0xABCD), "0xabcd"); - EXPECT_EQ(format("{.length}", "hello"), "5"); - - std::string_view fmt = - "There " - "[if $0 != 1: 'are' else: 'is']" - " {} " - "apple[if $0 != 1: 's']"; - EXPECT_EQ(format(fmt, 1), "There is 1 apple"); - EXPECT_EQ(format(fmt, 2), "There are 2 apples"); - - struct tmp_type {}; - EXPECT_ANY_THROW(format("{}", tmp_type())); -} -namespace test_format -{ - class my_value - { - public: - char ch; - int count; - }; -} -namespace papilio -{ - template <> - class formatter - { - public: - void parse(format_spec_parse_context& spec) - { - std::string_view view(spec.begin(), spec.end()); - if(view == "s") - m_as_str = true; - } - template - void format(const test_format::my_value& val, Context& ctx) - { - using context_traits = format_context_traits; - - if(m_as_str) - context_traits::append(ctx, val.ch, val.count); - else - { - std::string result; - result += '('; - result += val.ch; - result += ", "; - result += std::to_string(val.count); - result += ')'; - context_traits::append(ctx, result); - } - } - - private: - bool m_as_str = false; - }; -} -TEST(TestFormat, CustomType) -{ - using namespace papilio; - using test_format::my_value; - - { - static_assert(formatter_traits::has_formatter()); - - my_value my_val_a('A', 4); - - EXPECT_EQ(format("{:s}", my_val_a), "AAAA"); - EXPECT_EQ(format("{}", my_val_a), "(A, 4)"); - - my_value my_val_b('B', 3); - EXPECT_EQ(format("{}", my_val_b), "(B, 3)"); - EXPECT_EQ(format("{:s}", my_val_b), "BBB"); - } -} -namespace test_format -{ - struct ostream_only_string - { - std::string data; - - friend std::ostream& operator<<(std::ostream& os, const ostream_only_string& val) - { - os << val.data; - return os; - } - }; -} -TEST(TestFormat, OStreamCompatibility) -{ - using namespace papilio; - using namespace std::literals; - - static_assert(!formatter_traits::has_formatter()); - - { - test_format::ostream_only_string str("operator<<"s); - EXPECT_EQ(format("{}", str), "operator<<"); - EXPECT_THROW(format("{:s}", str), invalid_format); - } } int main(int argc, char* argv[]) diff --git a/test/test_locale.cpp b/test/test_locale.cpp index fe94d43..5c0dd8e 100644 --- a/test/test_locale.cpp +++ b/test/test_locale.cpp @@ -47,143 +47,6 @@ TEST(TestLocale, LocaleRef) EXPECT_EQ(bool_helper(false, custom_ref), "no"); } } -TEST(TestLocale, FormatTo) -{ - using namespace papilio; - - { - std::vector buf; - - format_to(std::back_inserter(buf), "{:L}", true); - EXPECT_EQ(std::string_view(buf.data(), 4), "true"); - buf.clear(); - format_to(std::back_inserter(buf), "{:L}", false); - EXPECT_EQ(std::string_view(buf.data(), 5), "false"); - } - - { - std::vector buf; - std::locale custom(std::locale("C"), new test_locale::yes_no); - - format_to(std::back_inserter(buf), custom, "{:L}", true); - EXPECT_EQ(std::string_view(buf.data(), 3), "yes"); - buf.clear(); - format_to(std::back_inserter(buf), custom, "{:L}", false); - EXPECT_EQ(std::string_view(buf.data(), 2), "no"); - } -} -TEST(TestLocale, FormatToN) -{ - using namespace papilio; - - { - std::vector buf; - buf.resize(10); - - auto result = format_to_n(buf.begin(), 5, "{:L} {:L}", false, true); - EXPECT_EQ(result.out, buf.begin() + 5); - EXPECT_EQ(result.size, 5); - EXPECT_EQ(std::string_view(buf.data(), result.size), "false"); - - buf.clear(); - buf.resize(10); - result = format_to_n(buf.begin(), buf.size(), "{:L}", true); - EXPECT_EQ(result.out, buf.begin() + 4); - EXPECT_EQ(result.size, 4); - EXPECT_EQ(std::string_view(buf.data(), result.size), "true"); - } - - { - std::locale custom(std::locale("C"), new test_locale::yes_no); - - std::size_t size = formatted_size(custom, "{:L}", true); - EXPECT_EQ(size, 3); // "yes" - - std::vector buf(size); - format_to_n(buf.begin(), size, custom, "{:L}", true); - EXPECT_EQ(std::string_view(buf.data(), size), "yes"); - } -} -TEST(TestLocale, Format) -{ - using namespace papilio; - - { - EXPECT_EQ(format("{:L}", true), "true"); - EXPECT_EQ(format("{:L}", false), "false"); - - std::locale custom(std::locale("C"), new test_locale::yes_no); - EXPECT_EQ(format(custom, "{:L}", true), "yes"); - EXPECT_EQ(format(custom, "{:L}", false), "no"); - } -} -TEST(TestLocale, PrintToStream) -{ - using namespace papilio; - - { - std::stringstream ss; - ss.imbue(std::locale("C")); - - print(ss, "{:L} {:L}", true, false); - - EXPECT_EQ(ss.str(), "true false"); - } - - { - std::stringstream ss; - ss.imbue(std::locale(std::locale("C"), new test_locale::yes_no)); - - print(ss, "{:L} {:L}", true, false); - - EXPECT_EQ(ss.str(), "yes no"); - } -} -namespace test_locale -{ - struct ostream_only_bool - { - bool data; - - friend std::ostream& operator<<(std::ostream& os, const ostream_only_bool& val) - { - os << std::boolalpha << val.data; - return os; - } - }; -} -TEST(TestLocale, OStreamCompatibility) -{ - using namespace papilio; - - static_assert(!formatter_traits::has_formatter()); - - { - test_locale::ostream_only_bool val_true(true); - test_locale::ostream_only_bool val_false(false); - - EXPECT_EQ(format("{:L} {:L}", val_true, val_false), "true false"); - } - - { - test_locale::ostream_only_bool val_true(true); - test_locale::ostream_only_bool val_false(false); - - std::locale custom(std::locale("C"), new test_locale::yes_no); - - EXPECT_EQ(format(custom, "{:L} {:L}", val_true, val_false), "yes no"); - } - - { - test_locale::ostream_only_bool val_true(true); - test_locale::ostream_only_bool val_false(false); - - std::locale custom(std::locale("C"), new test_locale::yes_no); - - // without locale specifiers "L" - EXPECT_EQ(format(custom, "{} {}", val_true, val_false), "true false"); - } -} int main(int argc, char* argv[]) { diff --git a/test/test_print.cpp b/test/test_print.cpp index 36fac27..54a1fd7 100644 --- a/test/test_print.cpp +++ b/test/test_print.cpp @@ -3,24 +3,7 @@ #include -TEST(TestPrint, PrintToStream) -{ - using namespace papilio; - - { - std::stringstream ss; - print(ss, "{}", "hello"); - - EXPECT_EQ(ss.str(), "hello"); - } - - { - std::stringstream ss; - print(ss, "{}", 1234); - - EXPECT_EQ(ss.str(), "1234"); - } -} +// TODO int main(int argc, char* argv[]) { diff --git a/test/test_script.cpp b/test/test_script.cpp index 43e6ebe..021b8f0 100644 --- a/test/test_script.cpp +++ b/test/test_script.cpp @@ -429,7 +429,7 @@ TEST(TestScript, Executor) int a1 = 1; float a2 = 2.0f; std::string a3 = "test"; - mutable_format_arg_store store(a1, a2, "string"_a = a3); + mutable_format_args store(a1, a2, "string"_a = a3); executor::context ctx(store); executor ex1(std::in_place_type, 0); @@ -653,7 +653,7 @@ TEST(TestScript, Interpreter) interpreter intp; auto acc = intp.access("0[0]"); - mutable_format_arg_store store("testing"); + mutable_format_args store("testing"); EXPECT_EQ( get(acc.second.access(store[acc.first])), U't' @@ -664,7 +664,7 @@ TEST(TestScript, Interpreter) interpreter intp; auto acc = intp.access("string[0]"); - mutable_format_arg_store store("string"_a = "testing"); + mutable_format_args store("string"_a = "testing"); EXPECT_EQ( get(acc.second.access(store[acc.first])), U't' @@ -679,7 +679,7 @@ TEST(TestScript, Interpreter) interpreter intp; auto ex = intp.compile(l.lexemes()); - mutable_format_arg_store store(0); + mutable_format_args store(0); executor::context ctx(std::move(store)); ex(ctx); diff --git a/test/test_stl_container.cpp b/test/test_stl_container.cpp index 0245fda..ba18ec7 100644 --- a/test/test_stl_container.cpp +++ b/test/test_stl_container.cpp @@ -4,162 +4,7 @@ #include -TEST(TestSTLContainer, Tuple) -{ - using namespace papilio; - - { - std::tuple<> empty_tp; - - static_assert(accessor_traits>::has_custom_index()); - - EXPECT_EQ(papilio::format("{.size}", empty_tp), "0"); - } - - { - using tuple_type = std::tuple; - tuple_type tp(0, 1.0f, "test"); - - static_assert(accessor_traits::has_custom_index()); - EXPECT_EQ(accessor_traits::get_arg(tp, 0).as_variable(), 0); - EXPECT_EQ(accessor_traits::get_arg(tp, 1).as_variable(), 1.0f); - EXPECT_EQ(accessor_traits::get_arg(tp, 2).as_variable(), "test"); - EXPECT_TRUE(accessor_traits::get_arg(tp, 4).empty()); - - EXPECT_EQ(papilio::format("{.size}", tp), "3"); - EXPECT_EQ(papilio::format("{[0]}", tp), "0"); - EXPECT_EQ(papilio::format("{[-1]}", tp), "test"); - } - - { - using pair_type = std::pair; - pair_type p(1, "hello"); - - static_assert(accessor_traits::has_custom_index()); - EXPECT_EQ(accessor_traits::get_arg(p, 0).as_variable(), 1); - EXPECT_EQ(accessor_traits::get_arg(p, 1).as_variable(), "hello"); - - EXPECT_EQ(papilio::format("{.size}", p), "2"); - EXPECT_EQ(papilio::format("{0.first} == {0[0]}", p), "1 == 1"); - EXPECT_EQ(papilio::format("{0.second} == {0[1]}", p), "hello == hello"); - } -} -TEST(TestSTLContainer, Map) -{ - using namespace papilio; - - { - std::map> m{ - { "one", 1 }, - { "two", 2 }, - { "three", 3 } - }; - - static_assert(accessor_traits::has_custom_key()); - EXPECT_EQ(accessor_traits::get_arg(m, "one").as_variable(), 1); - EXPECT_EQ(accessor_traits::get_arg(m, "two").as_variable(), 2); - EXPECT_EQ(accessor_traits::get_arg(m, "three").as_variable(), 3); - - // avoid collision with std::format caused by ADL - EXPECT_EQ(papilio::format("{.size}", m), "3"); - EXPECT_EQ(papilio::format("{['one']}", m), "1"); - EXPECT_EQ(papilio::format("{['two']}", m), "2"); - EXPECT_EQ(papilio::format("{['three']}", m), "3"); - } - - { - std::map> m{ - { 1, "one" }, - { 2, "two" }, - { 3, "three" } - }; - - static_assert(accessor_traits::has_custom_index()); - EXPECT_EQ(accessor_traits::get_arg(m, 1).as_variable(), "one"); - EXPECT_EQ(accessor_traits::get_arg(m, 2).as_variable(), "two"); - EXPECT_EQ(accessor_traits::get_arg(m, 3).as_variable(), "three"); - - // avoid collision with std::format caused by ADL - EXPECT_EQ(papilio::format("{.size}", m), "3"); - EXPECT_EQ(papilio::format("{[1]}", m), "one"); - EXPECT_EQ(papilio::format("{[2]}", m), "two"); - EXPECT_EQ(papilio::format("{[3]}", m), "three"); - } -} -TEST(TestSTLContainer, Vector) -{ - using namespace papilio; - - std::vector ints{ 0, 1, 2, 3 }; - - static_assert(accessor_traits::has_custom_index()); - EXPECT_EQ( - accessor_traits::get_attr(ints, "size").as_variable(), - ints.size() - ); - for(std::size_t i = 0; i < ints.size(); ++i) - { - EXPECT_EQ( - accessor_traits::get_arg(ints, i).as_variable(), - ints[i] - ); - } - - // avoid collision with std::format caused by ADL - EXPECT_EQ(papilio::format("{.size}", ints), "4"); - EXPECT_EQ(papilio::format("{[0]}", ints), "0"); - EXPECT_EQ(papilio::format("{[-1]}", ints), "3"); -} -TEST(TestSTLContainer, Join) -{ - using namespace papilio; - - { - std::array int_arr = { 1, 2, 3 }; - std::string result = format("{}", join(int_arr, ", ")); - EXPECT_EQ(result, "1, 2, 3"); - result = format("{:#x}", join(int_arr, ", ")); - EXPECT_EQ(result, "0x1, 0x2, 0x3"); - } - - { - char ch_arr[] = { 'A', 'B', 'C' }; - std::string result = format("{}", join(ch_arr, ";")); - EXPECT_EQ(result, "A;B;C"); - result = format("{:d}", join(ch_arr, ";")); - EXPECT_EQ(result, "65;66;67"); - } - - { - std::vector values{ 0, 1, 2, 3 }; - std::string result = format("{}", join(values, ", ")); - EXPECT_EQ(result, "0, 1, 2, 3"); - - auto rng = values | std::views::transform([](int v) { return v + 1; }); - result = format("{}", join(rng, ", ")); - EXPECT_EQ(result, "1, 2, 3, 4"); - } - - { - std::vector values{ "A", "BC", "DEF", "GHIJ" }; - std::string result = format("{}", join(values, ", ")); - EXPECT_EQ(result, "A, BC, DEF, GHIJ"); - result = format("{:>4}", join(values, ";")); - EXPECT_EQ(result, " A; BC; DEF;GHIJ"); - } - - { - std::tuple tp(1, false, "hello"); - std::string result = format("{}", join(tp, ", ")); - EXPECT_EQ(result, "1, false, hello"); - } - - { - std::pair p("key", 0); - std::string result = format("{}", join(p, ", ")); - EXPECT_EQ(result, "key, 0"); - } -} +// TODO int main(int argc, char* argv[]) {