mirror of https://github.com/odrling/Aegisub
185 lines
5.3 KiB
C++
185 lines
5.3 KiB
C++
// 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/fs_fwd.h>
|
|
|
|
#include <boost/interprocess/streams/vectorstream.hpp>
|
|
#include <boost/io/ios_state.hpp>
|
|
#include <type_traits>
|
|
|
|
class wxString;
|
|
|
|
extern template class boost::interprocess::basic_vectorstream<std::string>;
|
|
extern template class boost::interprocess::basic_vectorstream<std::wstring>;
|
|
extern template class boost::interprocess::basic_vectorbuf<std::string>;
|
|
extern template class boost::interprocess::basic_vectorbuf<std::wstring>;
|
|
|
|
namespace agi { namespace format_detail {
|
|
// A static cast which throws at runtime if the cast is invalid rather than
|
|
// failing to compile, as with format strings we don't know what type to cast
|
|
// to at compile time.
|
|
template<typename In, typename Out, bool = std::is_convertible<In, Out>::value>
|
|
struct runtime_cast_helper {
|
|
static Out cast(In const&) { throw std::bad_cast(); }
|
|
};
|
|
|
|
template<typename In, typename Out>
|
|
struct runtime_cast_helper<In, Out, true> {
|
|
static Out cast(In const& value) {
|
|
return static_cast<Out>(value);
|
|
}
|
|
};
|
|
|
|
template<typename Out, typename In>
|
|
Out runtime_cast(In const& value) {
|
|
return runtime_cast_helper<In, Out>::cast(value);
|
|
}
|
|
}
|
|
|
|
template<typename Char, typename T>
|
|
struct writer {
|
|
static void write(std::basic_ostream<Char>& out, int, T const& value) {
|
|
out << value;
|
|
}
|
|
};
|
|
|
|
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
|
|
template<> struct writer<char, agi::fs::path>;
|
|
template<> struct writer<wchar_t, agi::fs::path>;
|
|
template<> struct writer<char, wxString>;
|
|
template<> struct writer<wchar_t, wxString>;
|
|
|
|
namespace format_detail {
|
|
template<typename Char>
|
|
struct formatter_state {
|
|
std::basic_ostream<Char>& out;
|
|
|
|
const Char *fmt;
|
|
const Char *fmt_cur = nullptr;
|
|
|
|
bool read_width = false;
|
|
bool read_precision = false;
|
|
bool pending = false;
|
|
|
|
int width = 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;
|
|
|
|
bool parse_next();
|
|
Char next_format();
|
|
|
|
public:
|
|
formatter(std::basic_ostream<Char>& out, const Char *fmt)
|
|
: formatter_state<Char>(out, fmt), saver(out) { }
|
|
~formatter();
|
|
|
|
template<typename T>
|
|
void operator()(T&& value) {
|
|
if (!this->pending && !parse_next()) return;
|
|
|
|
if (this->read_width) {
|
|
this->width = runtime_cast<int>(value);
|
|
this->read_width = false;
|
|
return;
|
|
}
|
|
|
|
if (this->read_precision) {
|
|
this->precision = runtime_cast<int>(value);
|
|
this->read_precision = false;
|
|
return;
|
|
}
|
|
|
|
Char c = next_format();
|
|
|
|
switch (c) {
|
|
case 'c':
|
|
this->out << runtime_cast<Char>(value);
|
|
break;
|
|
case 'd': case 'i':
|
|
this->out << runtime_cast<intmax_t>(value);
|
|
break;
|
|
case 'o':
|
|
this->out << runtime_cast<intmax_t>(value);
|
|
break;
|
|
case 'x':
|
|
this->out << runtime_cast<intmax_t>(value);
|
|
break;
|
|
case 'u':
|
|
this->out << runtime_cast<uintmax_t>(value);
|
|
break;
|
|
case 'e':
|
|
this->out << runtime_cast<double>(value);
|
|
break;
|
|
case 'f':
|
|
this->out << runtime_cast<double>(value);
|
|
break;
|
|
case 'g':
|
|
this->out << runtime_cast<double>(value);
|
|
break;
|
|
case 'p':
|
|
this->out << runtime_cast<const void *>(value);
|
|
break;
|
|
default: // s and other
|
|
writer<Char, typename std::decay<T>::type>::write(this->out, this->precision, value);
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
// Base case for variadic template recursion
|
|
template<typename Char>
|
|
inline void format(formatter<Char>&&) { }
|
|
|
|
template<typename Char, typename T, typename... Args>
|
|
void format(formatter<Char>&& fmt, T&& first, Args&&... rest) {
|
|
fmt(first);
|
|
format(std::move(fmt), std::forward<Args>(rest)...);
|
|
}
|
|
} // namespace format_detail
|
|
|
|
template<typename Char, typename... Args>
|
|
void format(std::basic_ostream<Char>& out, const Char *fmt, Args&&... args) {
|
|
format(format_detail::formatter<Char>(out, fmt), std::forward<Args>(args)...);
|
|
}
|
|
|
|
template<typename Char, typename... Args>
|
|
std::basic_string<Char> format(const Char *fmt, Args&&... args) {
|
|
boost::interprocess::basic_vectorstream<std::basic_string<Char>> out;
|
|
format(out, fmt, std::forward<Args>(args)...);
|
|
return out.vector();
|
|
}
|
|
}
|