1 // Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org> 2 // 3 // Permission to use, copy, modify, and distribute this software for any 4 // purpose with or without fee is hereby granted, provided that the above 5 // copyright notice and this permission notice appear in all copies. 6 // 7 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 // 15 // Aegisub Project http://www.aegisub.org/ 16 17 #include <libaegisub/fs_fwd.h> 18 19 #include <boost/interprocess/streams/vectorstream.hpp> 20 #include <boost/io/ios_state.hpp> 21 #include <type_traits> 22 #include <typeinfo> // std::bad_cast 23 24 class wxString; 25 26 namespace agi { namespace format_detail { 27 // A static cast which throws at runtime if the cast is invalid rather than 28 // failing to compile, as with format strings we don't know what type to cast 29 // to at compile time. 30 template<typename In, typename Out, bool = std::is_convertible<In, Out>::value> 31 struct runtime_cast_helper { castruntime_cast_helper32 static Out cast(In const&) { throw std::bad_cast(); } 33 }; 34 35 template<typename In, typename Out> 36 struct runtime_cast_helper<In, Out, true> { 37 static Out cast(In const& value) { 38 return static_cast<Out>(value); 39 } 40 }; 41 42 template<typename Out, typename In> 43 Out runtime_cast(In const& value) { 44 return runtime_cast_helper<In, Out>::cast(value); 45 } 46 } 47 48 template<typename Char, typename T> 49 struct writer { 50 static void write(std::basic_ostream<Char>& out, int, T const& value) { 51 out << value; 52 } 53 }; 54 55 template<typename StreamChar, typename Char> 56 struct writer<StreamChar, const Char *> { 57 static void write(std::basic_ostream<StreamChar>& out, int max_len, const Char *value); 58 }; 59 60 template<typename StreamChar, typename Char> 61 struct writer<StreamChar, std::basic_string<Char>> { 62 static void write(std::basic_ostream<StreamChar>& out, int max_len, std::basic_string<Char> const& value); 63 }; 64 65 // Ensure things with specializations don't get implicitly initialized 66 template<> struct writer<char, agi::fs::path>; 67 template<> struct writer<wchar_t, agi::fs::path>; 68 template<> struct writer<char, wxString>; 69 template<> struct writer<wchar_t, wxString>; 70 71 namespace format_detail { 72 template<typename Char> 73 struct formatter_state { 74 std::basic_ostream<Char>& out; 75 76 const Char *fmt; 77 const Char *fmt_cur = nullptr; 78 79 bool read_width = false; 80 bool read_precision = false; 81 bool pending = false; 82 83 int width = 0; 84 int precision = 0; 85 86 formatter_state(std::basic_ostream<Char>&out , const Char *fmt) 87 : out(out), fmt(fmt) { } 88 }; 89 90 template<typename Char> 91 class formatter : formatter_state<Char> { 92 formatter(const formatter&) = delete; 93 formatter& operator=(const formatter&) = delete; 94 95 boost::io::basic_ios_all_saver<Char> saver; 96 97 bool parse_next(); 98 99 public: 100 formatter(std::basic_ostream<Char>& out, const Char *fmt) 101 : formatter_state<Char>(out, fmt), saver(out) { } 102 ~formatter(); 103 104 template<typename T> 105 void operator()(T&& value) { 106 if (!this->pending && !parse_next()) return; 107 108 if (this->read_width) { 109 this->width = runtime_cast<int>(value); 110 this->read_width = false; 111 return; 112 } 113 114 if (this->read_precision) { 115 this->precision = runtime_cast<int>(value); 116 this->read_precision = false; 117 return; 118 } 119 this->pending = false; 120 121 if (this->width < 0) { 122 this->out.fill(' '); 123 this->out.setf(std::ios::left, std::ios::adjustfield); 124 this->width = -this->width; 125 } 126 this->out.width(this->width); 127 this->out.precision(this->precision < 0 ? 6 : this->precision); 128 129 Char c = *this->fmt_cur ? this->fmt_cur[0] : 's'; 130 if (c >= 'A' && c <= 'Z') { 131 this->out.setf(std::ios::uppercase); 132 c += 'a' - 'A'; 133 } 134 135 switch (c) { 136 case 'c': 137 this->out.setf(std::ios::dec, std::ios::basefield); 138 this->out << runtime_cast<Char>(value); 139 break; 140 case 'd': case 'i': 141 this->out.setf(std::ios::dec, std::ios::basefield); 142 this->out << runtime_cast<intmax_t>(value); 143 break; 144 case 'o': 145 this->out.setf(std::ios::oct, std::ios::basefield); 146 this->out << runtime_cast<intmax_t>(value); 147 break; 148 case 'x': 149 this->out.setf(std::ios::hex, std::ios::basefield); 150 this->out << runtime_cast<intmax_t>(value); 151 break; 152 case 'u': 153 this->out.setf(std::ios::dec, std::ios::basefield); 154 this->out << runtime_cast<uintmax_t>(value); 155 break; 156 case 'e': 157 this->out.setf(std::ios::scientific, std::ios::floatfield); 158 this->out.setf(std::ios::dec, std::ios::basefield); 159 this->out << runtime_cast<double>(value); 160 break; 161 case 'f': 162 this->out.setf(std::ios::fixed, std::ios::floatfield); 163 this->out << runtime_cast<double>(value); 164 break; 165 case 'g': 166 this->out.setf(std::ios::dec, std::ios::basefield); 167 this->out.flags(this->out.flags() & ~std::ios::floatfield); 168 this->out << runtime_cast<double>(value); 169 break; 170 case 'p': 171 this->out.setf(std::ios::hex, std::ios::basefield); 172 this->out << runtime_cast<const void *>(value); 173 break; 174 default: // s and other 175 this->out.setf(std::ios::boolalpha); 176 writer<Char, typename std::decay<T>::type>::write(this->out, this->precision, value); 177 break; 178 } 179 180 this->fmt = *this->fmt_cur ? this->fmt_cur + 1 : this->fmt_cur; 181 } 182 }; 183 184 // Base case for variadic template recursion 185 template<typename Char> 186 inline void format(formatter<Char>&&) { } 187 188 template<typename Char, typename T, typename... Args> 189 void format(formatter<Char>&& fmt, T&& first, Args&&... rest) { 190 fmt(first); 191 format(std::move(fmt), std::forward<Args>(rest)...); 192 } 193 } // namespace format_detail 194 195 template<typename Char, typename... Args> 196 void format(std::basic_ostream<Char>& out, const Char *fmt, Args&&... args) { 197 format(format_detail::formatter<Char>(out, fmt), std::forward<Args>(args)...); 198 } 199 200 template<typename Char, typename... Args> 201 std::basic_string<Char> format(const Char *fmt, Args&&... args) { 202 boost::interprocess::basic_vectorstream<std::basic_string<Char>> out; 203 format(out, fmt, std::forward<Args>(args)...); 204 return out.vector(); 205 } 206 } 207