2: 2019-05-15 (水) 12:01:00 takatsuka |
3: 2021-03-17 (水) 13:39:48 takatsuka |
| そんなわけで printf 風の書き方も出来てしまう boost::format はすごく便利です。 | | そんなわけで printf 風の書き方も出来てしまう boost::format はすごく便利です。 |
| 開発者の方に感謝しつつ使わせていただこうと思います。 | | 開発者の方に感謝しつつ使わせていただこうと思います。 |
| + | |
| + | #contents |
| + | |
| + | ** 2021/3 追記 [#icd7a123] |
| + | |
| + | C++17 を前提にコードを刷新いたしました。 |
| + | と同時に、色々な型を展開して出力する機能を追加してみました。 |
| + | |
| + | 基本的な使い方は変わってませんが、引数に渡せる型を景気よく増やしてみました。 |
| + | |
| + | std::pair std::tuple |
| + | std::unique_ptr std::shared_ptr std::optional |
| + | std::array std::vector std::list std::deque std::initializer_list |
| + | std::set std::multiset std::map std::multimap |
| + | |
| + | これらが渡された場合は、その中身を出力します。 |
| + | |
| + | &font(80%){std::tuple や std::unique_ptr などは、まぁ使い勝手が良くなったかなと感じますが、std::vector や std::map などのコンテナまで対応してしまったのはやりすぎかもしれません。&br;このあたりは実際に使う場合には要調整かなと思います。}; |
| + | |
| + | *** よもやま [#c622d3f0] |
| + | C++17 の if constexpr はとても便利だなと感じます。 |
| + | その分、昨今のC++の発展っぷりに付いていくことは、自分程度のスペックだととても大変です。C++20 や 23 の話題もあるし・・。 |
| + | そのため、わからないことはすぐにネットで答えを探しています。色々な情報を提供してくれている方々には感謝しかありません。 |
| + | そしてここまで複雑な(と自分は思う) C++言語の、仕様決めをしている方々ももちろんですが、コンパイラを作っていらっしゃる方々には、畏敬の念を通り越して畏怖の念すら感じざるを得ません! |
| + | ありがとうございます。 |
| | | |
| * コード [#rad4fc7a] | | * コード [#rad4fc7a] |
| + | |
| + | - StringFormat.h |
| + | |
| + | #prettify{{ |
| + | #include <boost/format.hpp> |
| + | |
| + | namespace rlib |
| + | { |
| + | namespace string |
| + | { |
| + | namespace inner |
| + | { |
| + | template<class> struct IsArray : std::false_type {}; |
| + | template<class T, std::size_t N> struct IsArray<std::array<T, N>> :std::true_type {}; |
| + | |
| + | template<class> struct IsVector : std::false_type {}; |
| + | template<class T1, class T2> struct IsVector<std::vector<T1, T2>> :std::true_type {}; |
| + | |
| + | template<class> struct IsList : std::false_type {}; |
| + | template<class T1, class T2> struct IsList<std::list<T1, T2>> :std::true_type {}; |
| + | |
| + | template<class> struct IsDeque : std::false_type {}; |
| + | template<class T1, class T2> struct IsDeque<std::deque<T1, T2>> :std::true_type {}; |
| + | |
| + | template<class> struct IsInitializerList : std::false_type {}; |
| + | template<class T> struct IsList<std::initializer_list<T>> :std::true_type {}; |
| + | |
| + | template<class> struct IsSet : std::false_type {}; |
| + | template<class T1, class T2, class T3> struct IsSet<std::set<T1, T2, T3>> :std::true_type {}; |
| + | |
| + | template<class> struct IsMultiSet : std::false_type {}; |
| + | template<class T1, class T2, class T3> struct IsMultiSet<std::multiset<T1, T2, T3>> :std::true_type {}; |
| + | |
| + | template<class> struct IsMap : std::false_type {}; |
| + | template<class T1, class T2, class T3, class T4> struct IsMap<std::map<T1, T2, T3, T4>> :std::true_type {}; |
| + | |
| + | template<class> struct IsMultiMap : std::false_type {}; |
| + | template<class T1, class T2, class T3, class T4> struct IsMultiMap<std::multimap<T1, T2, T3, T4>> :std::true_type {}; |
| + | |
| + | template<class> struct IsPair : std::false_type { }; |
| + | template<class T1, class T2> struct IsPair<std::pair<T1,T2>> :std::true_type {}; |
| + | |
| + | template<class> struct IsUniquePtr : std::false_type {}; |
| + | template<class T1, class T2> struct IsUniquePtr<std::unique_ptr<T1,T2>> :std::true_type {}; |
| + | |
| + | template<class> struct IsSharedPtr : std::false_type {}; |
| + | template<class T> struct IsSharedPtr<std::shared_ptr<T>> :std::true_type {}; |
| + | |
| + | template<class> struct IsOptional : std::false_type {}; |
| + | template<class T> struct IsOptional<std::optional<T>> :std::true_type {}; |
| + | |
| + | template<class> struct IsTuple : std::false_type {}; |
| + | template<class... T> struct IsTuple<std::tuple<T...>> :std::true_type {}; |
| + | |
| + | |
| + | template <class CharT, class Head, class... Tail> void f(boost::basic_format<CharT>&, Head&, Tail&&...); |
| + | template <class CharT> void f(const boost::basic_format<CharT>&) {} |
| + | |
| + | template <std::size_t I, class CharT, class... T, class... Tail> void f(boost::basic_format<CharT>& format, const std::tuple<T...>& head, Tail&&... tail) { |
| + | if constexpr (I < sizeof...(T)) { |
| + | f<I + 1, CharT>(format % std::get<I>(head), head, tail...); |
| + | } else { |
| + | f<CharT>(format, tail...); |
| + | } |
| + | } |
| + | |
| + | template <class CharT, class Head, class... Tail> void f(boost::basic_format<CharT>& format, Head& head, Tail&&... tail) { |
| + | using HeadT = std::remove_const_t<Head>; |
| + | if constexpr (IsUniquePtr<HeadT>::value|| IsSharedPtr<HeadT>::value|| IsOptional<HeadT>::value) { |
| + | if (head) { |
| + | f<CharT>(format, *head, tail...); |
| + | } else { |
| + | f<CharT>(format % "(null)", tail...); |
| + | } |
| + | } else if constexpr (IsPair<HeadT>::value) { |
| + | f<CharT>(format, head.first, head.second, tail...); |
| + | } else if constexpr (IsTuple<HeadT>::value) { |
| + | f<0, CharT>(format, head, tail...); |
| + | } else if constexpr (IsArray<HeadT>::value || IsVector<HeadT>::value || IsList<HeadT>::value || IsDeque<HeadT>::value || IsInitializerList<HeadT>::value || IsSet<HeadT>::value || IsMultiSet<HeadT>::value || IsMap<HeadT>::value || IsMultiMap<HeadT>::value) { // 範囲ループ可の型か?っていう便利記述がもしあるなら知りたい |
| + | for (const auto& i : head) { |
| + | f<CharT>(format, i); |
| + | } |
| + | f<CharT>(format, tail...); |
| + | #ifdef _MSC_VER |
| + | } else if constexpr (std::is_same_v<HeadT, CStringA> || std::is_same_v<HeadT, CStringW>) { |
| + | f<CharT>(format % std::basic_string<CharT>(head.GetString()), tail...); |
| + | #endif |
| + | } else { |
| + | f<CharT>(format % head, tail...); |
| + | } |
| + | } |
| + | } |
| + | |
| + | template <class CharT, class... Args> std::basic_string<CharT> format(const CharT* lpszFormat, Args&&... args) { |
| + | boost::basic_format<CharT> format; |
| + | format.exceptions(boost::io::no_error_bits); // 例外を発生させない |
| + | format.parse(lpszFormat); |
| + | try { |
| + | inner::f<CharT>(format, args...); |
| + | } catch (...) { |
| + | } |
| + | return format.str(); |
| + | } |
| + | template <class CharT, class... Args> std::basic_string<CharT> format(const std::basic_string<CharT>& s, Args&&... args) { |
| + | return format<CharT>(s.c_str(), args...); |
| + | } |
| + | } |
| + | } |
| + | }} |
| + | |
| + | |
| + | * 使い方の例 [#k9d70102] |
| + | |
| + | - こちらのコードでは printf っぽい書式しか使っておりませんが、中身は boost::format です。書式の詳細はそちらのリファレンスもご参照下さい。 |
| + | |
| + | #prettify(){{ |
| + | #include "StringFormat.h" // ヘッダをインクルードするのみで使えます |
| + | |
| + | void main() { |
| + | using namespace rlib::string; |
| + | |
| + | {// 普通の使い方 |
| + | std::string r = format(u8"%s %dSX SR%dDE%s", u8"日産", 180, 20, "T"); // "日産 180SX SR20DET" |
| + | } |
| + | {// std::wstring も可です |
| + | std::wstring r = format(L"BNR%d RB%dDE%s", 32, 26, L"TT"); // L"BNR32 RB26DETT" |
| + | } |
| + | {// 適してない型でも例外発生しません |
| + | std::string r = format("%dSX %s", 240, L"日産"); // "240SX 00007FF742CC123C" |
| + | } |
| + | {// std::string をそのまま書けます。c_str() が不要なので便利。 |
| + | const std::string a = u8"トヨタ"; |
| + | std::string b = "1JZ-GTE"; |
| + | std::string r = format("%s jzx90 %s", a, b); // "トヨタ jzx90 1JZ-GTE" |
| + | } |
| + | #ifdef _MSC_VER |
| + | {// VisualStudio の CString |
| + | CString t0(_T("マツダ")); |
| + | const CString t1(_T("13B-T")); |
| + | auto r = format(_T("%s FC3S %s"), t0, t1); // L"マツダ FC3S 13B-T" |
| + | } |
| + | #endif |
| + | {// std::tuple std::pair は展開します。 |
| + | std::tuple<std::string, int, double> a{ "RPS13",1998,11.8 }; |
| + | const std::pair<std::string, int> b{ "FR", 1848000 }; |
| + | auto r = format( // "形式:RPS13 排気量:1998cc 燃費:11.8km/l 駆動方式:FR 価格:1848000円" |
| + | u8"形式:%s 排気量:%dcc 燃費:%.1fkm/l 駆動方式:%s 価格:%d円", a, b); |
| + | } |
| + | {// std::unique_ptr std::shared_ptr std::optional は中身を出力にします。空(無効)の場合は"(null)"を出力します |
| + | std::unique_ptr<int> u = std::make_unique<int>(1); |
| + | std::shared_ptr<int> s = std::make_shared<int>(2); |
| + | std::optional<int> o(3); |
| + | std::optional<int> e; |
| + | auto r = format("%d,%d,%d,%d", u, s, o, e); // "1,2,3,(null)" |
| + | } |
| + | {// その他コンテナも展開します。(やりすぎかもしれません。素直にコンパイルエラーに振ってもよいかも。) |
| + | auto r0 = format("%d,%d,%d", std::array<int, 3>{ 1, 2, 3 }); // "1,2,3" |
| + | auto r1 = format("%d,%d,%d", std::vector<int>{4, 5, 6}); // "4,5,6" |
| + | auto r2 = format("%d,%d,%d", std::list<int>{7, 8, 9}); // "7,8,9" |
| + | auto r3 = format("%d,%d,%d", std::deque<int>{10, 11, 12}); // "10,11,12" |
| + | auto r4 = format("%d,%d,%d", std::set<int>{3, 2, 1}); // "1,2,3" 取れる順で出力します(以下同様) |
| + | auto r5 = format("%d,%d,%d", std::map<int, int>{ {1, 2}, { 3,4 }}); // "1,2,3" |
| + | auto r6 = format("%d,%d,%d", std::multiset<int>{1, 2, 3}); // "1,2,3" |
| + | auto r7 = format("%d,%d,%d", std::multimap<int, int>{ {1, 2}, { 3,4 }});// "1,2,3" |
| + | } |
| + | {// コンテナの中も再帰で展開するので以下みたいな型も可です |
| + | std::map<int, std::pair<std::string, std::vector<int>>> m{ |
| + | {1, {"a", {5,6,7}}}, |
| + | {8, {"b", {9,10}}} |
| + | }; |
| + | auto r = format("%s,%s,%s,%s,%s,%s", m); // "1,a,5,6,7,8" |
| + | } |
| + | |
| + | {// boost::format なので未対応の型はコンパイルエラーになってくれます |
| + | std::function<void()> f; |
| + | auto r = format("%s", f); // コンパイルエラー |
| + | } |
| + | }} |
| + | |
| + | * 以前の版 [#jf4a2124] |
| + | |
| + | #region(以前の版) |
| #prettify{{ | | #prettify{{ |
| namespace String | | namespace String |
| CString t0(_T("マツダ")); | | CString t0(_T("マツダ")); |
| const CString t1(_T("13B-T")); | | const CString t1(_T("13B-T")); |
- | auto tr = RLib::String::Format(_T("%s FC3S %s"),t0,t1); // tr = _T("マツダ FC3S 13B-T") vc の CString版 | + | auto tr = String::Format(_T("%s FC3S %s"),t0,t1); // tr = _T("マツダ FC3S 13B-T") vc の CString版 |
| | | |
| } | | } |
| }} | | }} |
| + | #endregion |
| | | |
| * 追記履歴 [#p57d9c21] | | * 追記履歴 [#p57d9c21] |
| + | - C++17 を使って諸々改修した版を追加しました 2021/3 |
| - CString 対応コードを追加しました 2019/5/15 | | - CString 対応コードを追加しました 2019/5/15 |