| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #pragma once |
|
|
| #include <string> |
|
|
| |
| |
| |
| |
| |
| |
|
|
| namespace cif |
| { |
|
|
| namespace detail |
| { |
| template <typename T> |
| struct to_varg |
| { |
| using type = T; |
|
|
| to_varg(const T &v) |
| : m_value(v) |
| { |
| } |
|
|
| type operator*() { return m_value; } |
|
|
| T m_value; |
| }; |
|
|
| template <> |
| struct to_varg<const char *> |
| { |
| using type = const char *; |
|
|
| to_varg(const char *v) |
| : m_value(v) |
| { |
| } |
|
|
| type operator*() { return m_value.c_str(); } |
|
|
| std::string m_value; |
| }; |
|
|
| template <> |
| struct to_varg<std::string> |
| { |
| using type = const char *; |
|
|
| to_varg(const std::string &v) |
| : m_value(v) |
| { |
| } |
|
|
| type operator*() { return m_value.c_str(); } |
|
|
| std::string m_value; |
| }; |
|
|
| } |
|
|
| |
|
|
| template <typename... Args> |
| class format_plus_arg |
| { |
| public: |
| using args_vector_type = std::tuple<detail::to_varg<Args>...>; |
| using vargs_vector_type = std::tuple<typename detail::to_varg<Args>::type...>; |
|
|
| format_plus_arg(const format_plus_arg &) = delete; |
| format_plus_arg &operator=(const format_plus_arg &) = delete; |
|
|
|
|
| format_plus_arg(std::string_view fmt, Args... args) |
| : m_fmt(fmt) |
| , m_args(std::forward<Args>(args)...) |
| { |
| auto ix = std::make_index_sequence<sizeof...(Args)>(); |
| copy_vargs(ix); |
| } |
|
|
| std::string str() |
| { |
| char buffer[1024]; |
| std::string::size_type r = std::apply(snprintf, std::tuple_cat(std::make_tuple(buffer, sizeof(buffer), m_fmt.c_str()), m_vargs)); |
| return { buffer, r }; |
| } |
|
|
| friend std::ostream &operator<<(std::ostream &os, const format_plus_arg &f) |
| { |
| char buffer[1024]; |
| std::string::size_type r = std::apply(snprintf, std::tuple_cat(std::make_tuple(buffer, sizeof(buffer), f.m_fmt.c_str()), f.m_vargs)); |
| os.write(buffer, r); |
| return os; |
| } |
|
|
| private: |
|
|
| template <size_t... I> |
| void copy_vargs(std::index_sequence<I...>) |
| { |
| ((std::get<I>(m_vargs) = *std::get<I>(m_args)), ...); |
| } |
|
|
| std::string m_fmt; |
| args_vector_type m_args; |
| vargs_vector_type m_vargs; |
| }; |
|
|
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| template <typename... Args> |
| constexpr auto format(std::string_view fmt, Args... args) |
| { |
| return format_plus_arg(fmt, std::forward<Args>(args)...); |
| } |
|
|
| |
| |
|
|
| class fill_out_streambuf : public std::streambuf |
| { |
| public: |
|
|
| |
|
|
| using base_type = std::streambuf; |
| using int_type = base_type::int_type; |
| using char_type = base_type::char_type; |
| using traits_type = base_type::traits_type; |
|
|
| |
|
|
| |
| |
| |
| |
| fill_out_streambuf(std::ostream &os, int width = 80) |
| : m_os(os) |
| , m_upstream(os.rdbuf()) |
| , m_width(width) |
| { |
| } |
|
|
| |
|
|
| ~fill_out_streambuf() |
| { |
| m_os.rdbuf(m_upstream); |
| } |
|
|
| |
|
|
| |
| |
| |
| |
| |
| |
| virtual int_type |
| overflow(int_type ic = traits_type::eof()) |
| { |
| char ch = traits_type::to_char_type(ic); |
|
|
| int_type result = ic; |
|
|
| if (ch == '\n') |
| { |
| for (int i = m_column_count; result != traits_type::eof() and i < m_width; ++i) |
| result = m_upstream->sputc(' '); |
| } |
|
|
| if (result != traits_type::eof()) |
| result = m_upstream->sputc(ch); |
|
|
| if (result != traits_type::eof()) |
| { |
| if (ch == '\n') |
| { |
| m_column_count = 0; |
| ++m_line_count; |
| } |
| else |
| ++m_column_count; |
| } |
|
|
| return result; |
| } |
|
|
| |
| std::streambuf *get_upstream() const { return m_upstream; } |
|
|
| |
| int get_line_count() const { return m_line_count; } |
|
|
| private: |
| std::ostream &m_os; |
| std::streambuf *m_upstream; |
| int m_width; |
| int m_line_count = 0; |
| int m_column_count = 0; |
| }; |
|
|
| } |
|
|