Extract some of the string formatter implementation to format.cpp
This commit is contained in:
parent
37c02ae127
commit
59489b8f8c
6 changed files with 303 additions and 232 deletions
|
@ -295,6 +295,9 @@
|
||||||
<ClCompile Include="$(SrcDir)common\fs.cpp">
|
<ClCompile Include="$(SrcDir)common\fs.cpp">
|
||||||
<Filter>Source Files\Common</Filter>
|
<Filter>Source Files\Common</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="$(SrcDir)common\format.cpp">
|
||||||
|
<Filter>Source Files\Common</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="$(SrcDir)common\dispatch.cpp">
|
<ClCompile Include="$(SrcDir)common\dispatch.cpp">
|
||||||
<Filter>Source Files\Common</Filter>
|
<Filter>Source Files\Common</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|
|
@ -26,6 +26,7 @@ SRC += \
|
||||||
common/color.cpp \
|
common/color.cpp \
|
||||||
common/dispatch.cpp \
|
common/dispatch.cpp \
|
||||||
common/file_mapping.cpp \
|
common/file_mapping.cpp \
|
||||||
|
common/format.cpp \
|
||||||
common/fs.cpp \
|
common/fs.cpp \
|
||||||
common/hotkey.cpp \
|
common/hotkey.cpp \
|
||||||
common/io.cpp \
|
common/io.cpp \
|
||||||
|
|
233
libaegisub/common/format.cpp
Normal file
233
libaegisub/common/format.cpp
Normal file
|
@ -0,0 +1,233 @@
|
||||||
|
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
//
|
||||||
|
// Aegisub Project http://www.aegisub.org/
|
||||||
|
|
||||||
|
#include <libaegisub/format.h>
|
||||||
|
|
||||||
|
#include <libaegisub/fs_fwd.h>
|
||||||
|
|
||||||
|
#include <boost/filesystem/path.hpp>
|
||||||
|
#include <codecvt>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
template<typename Char>
|
||||||
|
int actual_len(int max_len, const Char *value) {
|
||||||
|
int len = 0;
|
||||||
|
while (value[len] && (max_len <= 0 || len < max_len)) ++len;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Char>
|
||||||
|
int actual_len(int max_len, std::basic_string<Char> value) {
|
||||||
|
if (max_len > 0 && static_cast<size_t>(max_len) < value.size())
|
||||||
|
return max_len;
|
||||||
|
return value.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Char>
|
||||||
|
void do_write_str(std::basic_ostream<Char>& out, const Char *str, int len) {
|
||||||
|
out.write(str, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_write_str(std::ostream& out, const wchar_t *str, int len) {
|
||||||
|
std::wstring_convert<std::codecvt_utf8<wchar_t>> utf8_conv;
|
||||||
|
auto u8 = utf8_conv.to_bytes(str, str + len);
|
||||||
|
out.write(u8.data(), u8.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_write_str(std::wostream& out, const char *str, int len) {
|
||||||
|
std::wstring_convert<std::codecvt_utf8<wchar_t>> utf8_conv;
|
||||||
|
auto wc = utf8_conv.from_bytes(str, str + len);
|
||||||
|
out.write(wc.data(), wc.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Char>
|
||||||
|
struct format_parser {
|
||||||
|
agi::format_detail::formatter_state<Char> &s;
|
||||||
|
|
||||||
|
void read_and_append_up_to_next_specifier() {
|
||||||
|
for (std::streamsize len = 0; ; ++len) {
|
||||||
|
// Ran out of format specifiers; not an error due to that
|
||||||
|
// translated strings may not need them all
|
||||||
|
if (!s.fmt[len]) {
|
||||||
|
s.out.write(s.fmt, len);
|
||||||
|
s.fmt += len;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s.fmt[len] == '%') {
|
||||||
|
if (s.fmt[len + 1] == '%') {
|
||||||
|
s.out.write(s.fmt, len);
|
||||||
|
s.fmt += len + 1;
|
||||||
|
len = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
s.out.write(s.fmt, len);
|
||||||
|
s.fmt += len;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int read_int() {
|
||||||
|
int i = 0;
|
||||||
|
for (; *s.fmt_cur >= '0' && *s.fmt_cur <= '9'; ++s.fmt_cur)
|
||||||
|
i = 10 * i + (*s.fmt_cur - '0');
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_flags() {
|
||||||
|
for (; ; ++s.fmt_cur) {
|
||||||
|
switch (*s.fmt_cur) {
|
||||||
|
// Not supported: ' ' (add a space before positive numers to align with negative)
|
||||||
|
case '#':
|
||||||
|
s.out.setf(std::ios::showpoint | std::ios::showbase);
|
||||||
|
continue;
|
||||||
|
case '0':
|
||||||
|
// overridden by left alignment ('-' flag)
|
||||||
|
if (!(s.out.flags() & std::ios::left)) {
|
||||||
|
// Use internal padding so that numeric values are
|
||||||
|
// formatted correctly, eg -00010 rather than 000-10
|
||||||
|
s.out.fill('0');
|
||||||
|
s.out.setf(std::ios::internal, std::ios::adjustfield);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
case '-':
|
||||||
|
s.out.fill(' ');
|
||||||
|
s.out.setf(std::ios::left, std::ios::adjustfield);
|
||||||
|
continue;
|
||||||
|
case '+':
|
||||||
|
s.out.setf(std::ios::showpos);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_width() {
|
||||||
|
if (*s.fmt_cur >= '0' && *s.fmt_cur <= '9')
|
||||||
|
s.width = read_int();
|
||||||
|
else if (*s.fmt_cur == '*') {
|
||||||
|
s.read_width = true;
|
||||||
|
s.pending = true;
|
||||||
|
++s.fmt_cur;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_precision() {
|
||||||
|
if (*s.fmt_cur != '.') return;
|
||||||
|
++s.fmt_cur;
|
||||||
|
|
||||||
|
// Ignoring negative precision because it's dumb and pointless
|
||||||
|
if (*s.fmt_cur >= '0' && *s.fmt_cur <= '9')
|
||||||
|
s.precision = read_int();
|
||||||
|
else if (*s.fmt_cur == '*') {
|
||||||
|
s.read_precision = true;
|
||||||
|
s.pending = true;
|
||||||
|
++s.fmt_cur;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
s.precision = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_length_modifiers() {
|
||||||
|
// Where "parse" means "skip" since we don't need them
|
||||||
|
for (Char c = *s.fmt_cur;
|
||||||
|
c == 'l' || c == 'h' || c == 'L' || c == 'j' || c == 'z' || c == 't';
|
||||||
|
c = *++s.fmt_cur);
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_format_specifier() {
|
||||||
|
s.width = 0;
|
||||||
|
s.precision = -1;
|
||||||
|
s.out.fill(' ');
|
||||||
|
s.out.unsetf(
|
||||||
|
std::ios::adjustfield |
|
||||||
|
std::ios::basefield |
|
||||||
|
std::ios::boolalpha |
|
||||||
|
std::ios::floatfield |
|
||||||
|
std::ios::showbase |
|
||||||
|
std::ios::showpoint |
|
||||||
|
std::ios::showpos |
|
||||||
|
std::ios::uppercase);
|
||||||
|
|
||||||
|
// Don't touch fmt until the specifier is fully applied so that if we
|
||||||
|
// have insufficient arguments it'll get passed through to the output
|
||||||
|
s.fmt_cur = s.fmt + 1;
|
||||||
|
|
||||||
|
parse_flags();
|
||||||
|
parse_width();
|
||||||
|
parse_precision();
|
||||||
|
parse_length_modifiers();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace agi {
|
||||||
|
|
||||||
|
template<typename StreamChar, typename Char>
|
||||||
|
void writer<StreamChar, const Char *>::write(std::basic_ostream<StreamChar>& out, int max_len, const Char *value) {
|
||||||
|
do_write_str(out, value, actual_len(max_len, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename StreamChar, typename Char>
|
||||||
|
void writer<StreamChar, std::basic_string<Char>>::write(std::basic_ostream<StreamChar>& out, int max_len, std::basic_string<Char> const& value) {
|
||||||
|
do_write_str(out, value.data(), actual_len(max_len, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template struct writer<char, const char *>;
|
||||||
|
template struct writer<char, const wchar_t *>;
|
||||||
|
template struct writer<wchar_t, const char *>;
|
||||||
|
template struct writer<wchar_t, const wchar_t *>;
|
||||||
|
template struct writer<char, std::string>;
|
||||||
|
template struct writer<char, std::wstring>;
|
||||||
|
template struct writer<wchar_t, std::string>;
|
||||||
|
template struct writer<wchar_t, std::wstring>;
|
||||||
|
|
||||||
|
namespace format_detail {
|
||||||
|
|
||||||
|
template<typename Char>
|
||||||
|
bool formatter<Char>::parse_next() {
|
||||||
|
format_parser<Char> parser{*static_cast<formatter_state<Char> *>(this)};
|
||||||
|
parser.read_and_append_up_to_next_specifier();
|
||||||
|
if (!*this->fmt) return false;
|
||||||
|
parser.parse_format_specifier();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Char>
|
||||||
|
formatter<Char>::~formatter() {
|
||||||
|
// Write remaining formatting string
|
||||||
|
for (std::streamsize len = 0; ; ++len) {
|
||||||
|
if (!this->fmt[len]) {
|
||||||
|
this->out.write(this->fmt, len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->fmt[len] == '%' && this->fmt[len + 1] == '%') {
|
||||||
|
this->out.write(this->fmt, len);
|
||||||
|
this->fmt += len + 1;
|
||||||
|
len = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template class formatter<char>;
|
||||||
|
template class formatter<wchar_t>;
|
||||||
|
|
||||||
|
} }
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
|
|
||||||
#include <boost/interprocess/streams/vectorstream.hpp>
|
#include <boost/interprocess/streams/vectorstream.hpp>
|
||||||
#include <boost/io/ios_state.hpp>
|
#include <boost/io/ios_state.hpp>
|
||||||
#include <codecvt>
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
class wxString;
|
class wxString;
|
||||||
|
@ -43,38 +42,6 @@ template<typename Out, typename In>
|
||||||
Out runtime_cast(In const& value) {
|
Out runtime_cast(In const& value) {
|
||||||
return runtime_cast_helper<In, Out>::cast(value);
|
return runtime_cast_helper<In, Out>::cast(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check length for string types
|
|
||||||
template<typename Char>
|
|
||||||
int actual_len(int max_len, const Char *value) {
|
|
||||||
int len = 0;
|
|
||||||
while (value[len] && (max_len <= 0 || len < max_len)) ++len;
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Char>
|
|
||||||
int actual_len(int max_len, std::basic_string<Char> value) {
|
|
||||||
if (max_len > 0 && static_cast<size_t>(max_len) < value.size())
|
|
||||||
return max_len;
|
|
||||||
return value.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Char>
|
|
||||||
inline void do_write_str(std::basic_ostream<Char>& out, const Char *str, int len) {
|
|
||||||
out.write(str, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void do_write_str(std::ostream& out, const wchar_t *str, int len) {
|
|
||||||
std::wstring_convert<std::codecvt_utf8<wchar_t>> utf8_conv;
|
|
||||||
auto u8 = utf8_conv.to_bytes(str, str + len);
|
|
||||||
out.write(u8.data(), u8.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void do_write_str(std::wostream& out, const char *str, int len) {
|
|
||||||
std::wstring_convert<std::codecvt_utf8<wchar_t>> utf8_conv;
|
|
||||||
auto wc = utf8_conv.from_bytes(str, str + len);
|
|
||||||
out.write(wc.data(), wc.size());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Char, typename T>
|
template<typename Char, typename T>
|
||||||
|
@ -84,33 +51,27 @@ struct writer {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename StreamChar, typename Char>
|
||||||
|
struct writer<StreamChar, const Char *> {
|
||||||
|
static void write(std::basic_ostream<StreamChar>& out, int max_len, const Char *value);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename StreamChar, typename Char>
|
||||||
|
struct writer<StreamChar, std::basic_string<Char>> {
|
||||||
|
static void write(std::basic_ostream<StreamChar>& out, int max_len, std::basic_string<Char> const& value);
|
||||||
|
};
|
||||||
|
|
||||||
// Ensure things with specializations don't get implicitly initialized
|
// Ensure things with specializations don't get implicitly initialized
|
||||||
template<> struct writer<char, agi::fs::path>;
|
template<> struct writer<char, agi::fs::path>;
|
||||||
template<> struct writer<wchar_t, agi::fs::path>;
|
template<> struct writer<wchar_t, agi::fs::path>;
|
||||||
template<> struct writer<char, wxString>;
|
template<> struct writer<char, wxString>;
|
||||||
template<> struct writer<wchar_t, wxString>;
|
template<> struct writer<wchar_t, wxString>;
|
||||||
|
|
||||||
template<typename StreamChar, typename Char>
|
|
||||||
struct writer<StreamChar, const Char *> {
|
|
||||||
static void write(std::basic_ostream<StreamChar>& out, int max_len, const Char *value) {
|
|
||||||
format_detail::do_write_str(out, value, format_detail::actual_len(max_len, value));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename StreamChar, typename Char>
|
|
||||||
struct writer<StreamChar, std::basic_string<Char>> {
|
|
||||||
static void write(std::basic_ostream<StreamChar>& out, int max_len, std::basic_string<Char> const& value) {
|
|
||||||
format_detail::do_write_str(out, value.data(), format_detail::actual_len(max_len, value));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace format_detail {
|
namespace format_detail {
|
||||||
template<typename Char>
|
template<typename Char>
|
||||||
class formatter {
|
struct formatter_state {
|
||||||
formatter(const formatter&) = delete;
|
|
||||||
formatter& operator=(const formatter&) = delete;
|
|
||||||
|
|
||||||
std::basic_ostream<Char>& out;
|
std::basic_ostream<Char>& out;
|
||||||
|
|
||||||
const Char *fmt;
|
const Char *fmt;
|
||||||
const Char *fmt_cur = nullptr;
|
const Char *fmt_cur = nullptr;
|
||||||
|
|
||||||
|
@ -121,225 +82,101 @@ class formatter {
|
||||||
int width = 0;
|
int width = 0;
|
||||||
int precision = 0;
|
int precision = 0;
|
||||||
|
|
||||||
|
formatter_state(std::basic_ostream<Char>&out , const Char *fmt)
|
||||||
|
: out(out), fmt(fmt) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Char>
|
||||||
|
class formatter : formatter_state<Char> {
|
||||||
|
formatter(const formatter&) = delete;
|
||||||
|
formatter& operator=(const formatter&) = delete;
|
||||||
|
|
||||||
boost::io::basic_ios_all_saver<Char> saver;
|
boost::io::basic_ios_all_saver<Char> saver;
|
||||||
|
|
||||||
void read_and_append_up_to_next_specifier() {
|
bool parse_next();
|
||||||
for (std::streamsize len = 0; ; ++len) {
|
|
||||||
// Ran out of format specifiers; not an error due to that
|
|
||||||
// translated strings may not need them all
|
|
||||||
if (!fmt[len]) {
|
|
||||||
out.write(fmt, len);
|
|
||||||
fmt += len;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fmt[len] == '%') {
|
|
||||||
if (fmt[len + 1] == '%') {
|
|
||||||
out.write(fmt, len);
|
|
||||||
fmt += len + 1;
|
|
||||||
len = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
out.write(fmt, len);
|
|
||||||
fmt += len;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int read_int() {
|
|
||||||
int i = 0;
|
|
||||||
for (; *fmt_cur >= '0' && *fmt_cur <= '9'; ++fmt_cur)
|
|
||||||
i = 10 * i + (*fmt_cur - '0');
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
void parse_flags() {
|
|
||||||
for (; ; ++fmt_cur) {
|
|
||||||
switch (*fmt_cur) {
|
|
||||||
// Not supported: ' ' (add a space before positive numers to align with negative)
|
|
||||||
case '#':
|
|
||||||
out.setf(std::ios::showpoint | std::ios::showbase);
|
|
||||||
continue;
|
|
||||||
case '0':
|
|
||||||
// overridden by left alignment ('-' flag)
|
|
||||||
if (!(out.flags() & std::ios::left)) {
|
|
||||||
// Use internal padding so that numeric values are
|
|
||||||
// formatted correctly, eg -00010 rather than 000-10
|
|
||||||
out.fill('0');
|
|
||||||
out.setf(std::ios::internal, std::ios::adjustfield);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
case '-':
|
|
||||||
out.fill(' ');
|
|
||||||
out.setf(std::ios::left, std::ios::adjustfield);
|
|
||||||
continue;
|
|
||||||
case '+':
|
|
||||||
out.setf(std::ios::showpos);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void parse_width() {
|
|
||||||
if (*fmt_cur >= '0' && *fmt_cur <= '9')
|
|
||||||
width = read_int();
|
|
||||||
else if (*fmt_cur == '*') {
|
|
||||||
read_width = true;
|
|
||||||
pending = true;
|
|
||||||
++fmt_cur;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void parse_precision() {
|
|
||||||
if (*fmt_cur != '.') return;
|
|
||||||
++fmt_cur;
|
|
||||||
|
|
||||||
// Ignoring negative precision because it's dumb and pointless
|
|
||||||
if (*fmt_cur >= '0' && *fmt_cur <= '9')
|
|
||||||
precision = read_int();
|
|
||||||
else if (*fmt_cur == '*') {
|
|
||||||
read_precision = true;
|
|
||||||
pending = true;
|
|
||||||
++fmt_cur;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
precision = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void parse_length_modifiers() {
|
|
||||||
// Where "parse" means "skip" since we don't need them
|
|
||||||
for (Char c = *fmt_cur;
|
|
||||||
c == 'l' || c == 'h' || c == 'L' || c == 'j' || c == 'z' || c == 't';
|
|
||||||
c = *++fmt_cur);
|
|
||||||
}
|
|
||||||
|
|
||||||
void parse_format_specifier() {
|
|
||||||
width = 0;
|
|
||||||
precision = -1;
|
|
||||||
out.fill(' ');
|
|
||||||
out.unsetf(
|
|
||||||
std::ios::adjustfield |
|
|
||||||
std::ios::basefield |
|
|
||||||
std::ios::boolalpha |
|
|
||||||
std::ios::floatfield |
|
|
||||||
std::ios::showbase |
|
|
||||||
std::ios::showpoint |
|
|
||||||
std::ios::showpos |
|
|
||||||
std::ios::uppercase);
|
|
||||||
|
|
||||||
// Don't touch fmt until the specifier is fully applied so that if we
|
|
||||||
// have insufficient arguments it'll get passed through to the output
|
|
||||||
fmt_cur = fmt + 1;
|
|
||||||
|
|
||||||
parse_flags();
|
|
||||||
parse_width();
|
|
||||||
parse_precision();
|
|
||||||
parse_length_modifiers();
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
formatter(std::basic_ostream<Char>& out, const Char *fmt) : out(out), fmt(fmt), saver(out) { }
|
formatter(std::basic_ostream<Char>& out, const Char *fmt)
|
||||||
~formatter() {
|
: formatter_state<Char>(out, fmt), saver(out) { }
|
||||||
// Write remaining formatting string
|
~formatter();
|
||||||
for (std::streamsize len = 0; ; ++len) {
|
|
||||||
if (!fmt[len]) {
|
|
||||||
out.write(fmt, len);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fmt[len] == '%' && fmt[len + 1] == '%') {
|
|
||||||
out.write(fmt, len);
|
|
||||||
fmt += len + 1;
|
|
||||||
len = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void operator()(T&& value) {
|
void operator()(T&& value) {
|
||||||
if (!pending) {
|
if (!this->pending && !parse_next()) return;
|
||||||
read_and_append_up_to_next_specifier();
|
|
||||||
if (!*fmt) return;
|
|
||||||
parse_format_specifier();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (read_width) {
|
if (this->read_width) {
|
||||||
width = runtime_cast<int>(value);
|
this->width = runtime_cast<int>(value);
|
||||||
read_width = false;
|
this->read_width = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (read_precision) {
|
if (this->read_precision) {
|
||||||
precision = runtime_cast<int>(value);
|
this->precision = runtime_cast<int>(value);
|
||||||
read_precision = false;
|
this->read_precision = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pending = false;
|
this->pending = false;
|
||||||
|
|
||||||
if (width < 0) {
|
if (this->width < 0) {
|
||||||
out.fill(' ');
|
this->out.fill(' ');
|
||||||
out.setf(std::ios::left, std::ios::adjustfield);
|
this->out.setf(std::ios::left, std::ios::adjustfield);
|
||||||
width = -width;
|
this->width = -this->width;
|
||||||
}
|
}
|
||||||
out.width(width);
|
this->out.width(this->width);
|
||||||
out.precision(precision < 0 ? 6 : precision);
|
this->out.precision(this->precision < 0 ? 6 : this->precision);
|
||||||
|
|
||||||
Char c = *fmt_cur ? fmt_cur[0] : 's';
|
Char c = *this->fmt_cur ? this->fmt_cur[0] : 's';
|
||||||
if (c >= 'A' && c <= 'Z') {
|
if (c >= 'A' && c <= 'Z') {
|
||||||
out.setf(std::ios::uppercase);
|
this->out.setf(std::ios::uppercase);
|
||||||
c += 'a' - 'A';
|
c += 'a' - 'A';
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'c':
|
case 'c':
|
||||||
out.setf(std::ios::dec, std::ios::basefield);
|
this->out.setf(std::ios::dec, std::ios::basefield);
|
||||||
out << runtime_cast<Char>(value);
|
this->out << runtime_cast<Char>(value);
|
||||||
break;
|
break;
|
||||||
case 'd': case 'i':
|
case 'd': case 'i':
|
||||||
out.setf(std::ios::dec, std::ios::basefield);
|
this->out.setf(std::ios::dec, std::ios::basefield);
|
||||||
out << runtime_cast<intmax_t>(value);
|
this->out << runtime_cast<intmax_t>(value);
|
||||||
break;
|
break;
|
||||||
case 'o':
|
case 'o':
|
||||||
out.setf(std::ios::oct, std::ios::basefield);
|
this->out.setf(std::ios::oct, std::ios::basefield);
|
||||||
out << runtime_cast<intmax_t>(value);
|
this->out << runtime_cast<intmax_t>(value);
|
||||||
break;
|
break;
|
||||||
case 'x':
|
case 'x':
|
||||||
out.setf(std::ios::hex, std::ios::basefield);
|
this->out.setf(std::ios::hex, std::ios::basefield);
|
||||||
out << runtime_cast<intmax_t>(value);
|
this->out << runtime_cast<intmax_t>(value);
|
||||||
break;
|
break;
|
||||||
case 'u':
|
case 'u':
|
||||||
out.setf(std::ios::dec, std::ios::basefield);
|
this->out.setf(std::ios::dec, std::ios::basefield);
|
||||||
out << runtime_cast<uintmax_t>(value);
|
this->out << runtime_cast<uintmax_t>(value);
|
||||||
break;
|
break;
|
||||||
case 'e':
|
case 'e':
|
||||||
out.setf(std::ios::scientific, std::ios::floatfield);
|
this->out.setf(std::ios::scientific, std::ios::floatfield);
|
||||||
out.setf(std::ios::dec, std::ios::basefield);
|
this->out.setf(std::ios::dec, std::ios::basefield);
|
||||||
out << runtime_cast<double>(value);
|
this->out << runtime_cast<double>(value);
|
||||||
break;
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
out.setf(std::ios::fixed, std::ios::floatfield);
|
this->out.setf(std::ios::fixed, std::ios::floatfield);
|
||||||
out << runtime_cast<double>(value);
|
this->out << runtime_cast<double>(value);
|
||||||
break;
|
break;
|
||||||
case 'g':
|
case 'g':
|
||||||
out.setf(std::ios::dec, std::ios::basefield);
|
this->out.setf(std::ios::dec, std::ios::basefield);
|
||||||
out.flags(out.flags() & ~std::ios::floatfield);
|
this->out.flags(this->out.flags() & ~std::ios::floatfield);
|
||||||
out << runtime_cast<double>(value);
|
this->out << runtime_cast<double>(value);
|
||||||
break;
|
break;
|
||||||
case 'p':
|
case 'p':
|
||||||
out.setf(std::ios::hex, std::ios::basefield);
|
this->out.setf(std::ios::hex, std::ios::basefield);
|
||||||
out << runtime_cast<const void *>(value);
|
this->out << runtime_cast<const void *>(value);
|
||||||
break;
|
break;
|
||||||
default: // s and other
|
default: // s and other
|
||||||
out.setf(std::ios::boolalpha);
|
this->out.setf(std::ios::boolalpha);
|
||||||
writer<Char, typename std::decay<T>::type>::write(out, precision, value);
|
writer<Char, typename std::decay<T>::type>::write(this->out, this->precision, value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt = *fmt_cur ? fmt_cur + 1 : fmt_cur;
|
this->fmt = *this->fmt_cur ? this->fmt_cur + 1 : this->fmt_cur;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,6 @@
|
||||||
// Common C++
|
// Common C++
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <codecvt>
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
|
@ -23,16 +23,14 @@ namespace agi {
|
||||||
template<>
|
template<>
|
||||||
struct writer<char, wxString> {
|
struct writer<char, wxString> {
|
||||||
static void write(std::basic_ostream<char>& out, int max_len, wxString const& value) {
|
static void write(std::basic_ostream<char>& out, int max_len, wxString const& value) {
|
||||||
format_detail::do_write_str(out, value.wx_str(),
|
writer<char, const wxChar *>::write(out, max_len, value.wx_str());
|
||||||
format_detail::actual_len(max_len, value.wx_str()));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
struct writer<wchar_t, wxString> {
|
struct writer<wchar_t, wxString> {
|
||||||
static void write(std::basic_ostream<wchar_t>& out, int max_len, wxString const& value) {
|
static void write(std::basic_ostream<wchar_t>& out, int max_len, wxString const& value) {
|
||||||
format_detail::do_write_str(out, value.wx_str(),
|
writer<wchar_t, const wxChar *>::write(out, max_len, value.wx_str());
|
||||||
format_detail::actual_len(max_len, value.wx_str()));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue