ページへ戻る
印刷
技術系備忘録/C++/Boost/boost.formatを使った文字列フォーマット。printf系関数を置き換え
をテンプレートにして作成 ::
シンクリッジ
xpwiki
:技術系備忘録/C++/Boost/boost.formatを使った文字列フォーマット。printf系関数を置き換え をテンプレートにして作成
開始行:
C++ だと文字列をフォーマットするコードって結構大変です。
C++11(14) になって色々な選択肢は増えたんですが、いまいち...
というわけで boost です。boost::format を使った文字列フォ...
% で繋げることなく、可変引数テンプレートを使って printf ...
boost::format の機能のおかげで、型安全で例外が発生するこ...
&font(80%){例えばアプリで処理失敗が発生したときに、アプリ...
printf 系の関数は(windows の CString::Format とかも)使...
がしかし printf 系の使いやすさは捨て難いです。慣れちゃっ...
そんなわけで printf 風の書き方も出来てしまう boost::forma...
開発者の方に感謝しつつ使わせていただこうと思います。
#contents
** 2021/3 追記
C++17 を前提にコードを刷新いたしました。
と同時に、色々な型を展開して出力する機能を追加してみまし...
基本的な使い方は変わってませんが、引数に渡せる型を景気よ...
std::pair std::tuple
std::unique_ptr std::shared_ptr std::optional
std::array std::vector std::list std::deque std::initiali...
std::set std::multiset std::map std::multimap
これらが渡された場合は、その中身を出力します。
&font(80%){std::tuple や std::unique_ptr などは、まぁ使い...
*** github にも公開しました
[[rlib-StringFormat>https://github.com/tr-takatsuka/rlib-...
*** よもやま
C++17 の if constexpr はとても便利だなと感じます。
その分、昨今のC++の発展っぷりに付いていくことは、自分程度...
そのため、わからないことはすぐにネットで答えを探していま...
そしてここまで複雑な(と自分は思う) C++言語の、仕様決めを...
ありがとうございます。
* コード
- 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::a...
template<class> struct IsVector : std::false_type {};
template<class T1, class T2> struct IsVector<std::vect...
template<class> struct IsList : std::false_type {};
template<class T1, class T2> struct IsList<std::list<T...
template<class> struct IsDeque : std::false_type {};
template<class T1, class T2> struct IsDeque<std::deque...
template<class> struct IsInitializerList : std::false_...
template<class T> struct IsList<std::initializer_list<...
template<class> struct IsSet : std::false_type {};
template<class T1, class T2, class T3> struct IsSet<st...
template<class> struct IsMultiSet : std::false_type {};
template<class T1, class T2, class T3> struct IsMultiS...
template<class> struct IsMap : std::false_type {};
template<class T1, class T2, class T3, class T4> struc...
template<class> struct IsMultiMap : std::false_type {};
template<class T1, class T2, class T3, class T4> struc...
template<class> struct IsPair : std::false_type { };
template<class T1, class T2> struct IsPair<std::pair<T...
template<class> struct IsUniquePtr : std::false_type {};
template<class T1, class T2> struct IsUniquePtr<std::u...
template<class> struct IsSharedPtr : std::false_type {};
template<class T> struct IsSharedPtr<std::shared_ptr<T...
template<class> struct IsOptional : std::false_type {};
template<class T> struct IsOptional<std::optional<T>> ...
template<class> struct IsTuple : std::false_type {};
template<class... T> struct IsTuple<std::tuple<T...>> ...
template <class CharT, class Head, class... Tail> void...
template <class CharT> void f(const boost::basic_forma...
template <std::size_t I, class CharT, class... T, clas...
if constexpr (I < sizeof...(T)) {
f<I + 1, CharT>(format % std::get<I>(head), head, ta...
} else {
f<CharT>(format, tail...);
}
}
template <class CharT, class Head, class... Tail> void...
using HeadT = std::remove_const_t<Head>;
if constexpr (IsUniquePtr<HeadT>::value|| IsSharedPtr...
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 || IsVecto...
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> ...
f<CharT>(format % std::basic_string<CharT>(head.GetS...
#endif
} else {
f<CharT>(format % head, tail...);
}
}
}
template <class CharT, class... Args> std::basic_string...
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...
return format<CharT>(s.c_str(), args...);
}
}
}
}}
* 使い方の例
- こちらのコードでは printf っぽい書式しか使っておりませ...
#prettify(){{
#include "StringFormat.h" // ヘッダをインクルードするのみ...
void main() {
using namespace rlib::string;
{// 普通の使い方
std::string r = format(u8"%s %dSX SR%dDE%s", u8"日産", ...
}
{// std::wstring も可です
std::wstring r = format(L"BNR%d RB%dDE%s", 32, 26, L"TT...
}
{// 適してない型でも例外発生しません
std::string r = format("%dSX %s", 240, L"日産"); // "24...
}
{// std::string をそのまま書けます。c_str() が不要なので...
const std::string a = u8"トヨタ";
std::string b = "1JZ-GTE";
std::string r = format("%s jzx90 %s", a, b); // "トヨタ...
}
#ifdef _MSC_VER
{// VisualStudio の CString
CString t0(_T("マツダ"));
const CString t1(_T("13B-T"));
auto r = format(_T("%s FC3S %s"), t0, t1); // L"マツダ ...
}
#endif
{// std::tuple std::pair は展開します。
std::tuple<std::string, int, double> a{ "RPS13",1998,11...
const std::pair<std::string, int> b{ "FR", 1848000 };
auto r = format( // "形式:RPS13 排気量:1998cc 燃費:11...
u8"形式:%s 排気量:%dcc 燃費:%.1fkm/l 駆動方式:%s 価格:...
}
{// std::unique_ptr std::shared_ptr std::optional は中身...
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,(...
}
{// その他コンテナも展開します。(やりすぎかもしれません...
auto r0 = format("%d,%d,%d", std::array<int, 3>{ 1, 2, ...
auto r1 = format("%d,%d,%d", std::vector<int>{4, 5, 6})...
auto r2 = format("%d,%d,%d", std::list<int>{7, 8, 9}); ...
auto r3 = format("%d,%d,%d", std::deque<int>{10, 11, 12...
auto r4 = format("%d,%d,%d", std::set<int>{3, 2, 1}); ...
auto r5 = format("%d,%d,%d", std::map<int, int>{ {1, 2}...
auto r6 = format("%d,%d,%d", std::multiset<int>{1, 2, 3...
auto r7 = format("%d,%d,%d", std::multimap<int, int>{ {...
}
{// コンテナの中も再帰で展開するので以下みたいな型も可です
std::map<int, std::pair<std::string, std::vector<int>>>...
{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); // コンパイルエラー
}
}}
* 以前の版
#region(以前の版)
#prettify{{
namespace String
{
template <class CharT> std::basic_string<CharT> Format(co...
{
return format.str();
}
#ifdef _MSC_VER // VisualC++ の CString 対応
template <class CharT,class... Tail> std::basic_string<Ch...
{
return Format<CharT>( format % std::basic_string<CharT>(...
}
template <class CharT,class... Tail> std::basic_string<Ch...
{
return Format<CharT>( format % std::basic_string<CharT>(...
}
template <class CharT,class... Tail> std::basic_string<Ch...
{
return Format<CharT>( format % std::basic_string<CharT>(...
}
template <class CharT,class... Tail> std::basic_string<Ch...
{
return Format<CharT>( format % std::basic_string<CharT>(...
}
#endif
template <class CharT,class Head,class... Tail> std::basi...
{
return Format<CharT>( format % head, tail... );
}
template <class CharT,class... Args> std::basic_string<Ch...
{
boost::basic_format<CharT> format;
format.exceptions( boost::io::no_error_bits ); // 例外を...
format.parse(lpszFormat);
return Format<CharT>(format,args...);
}
}
// 使い方例
int main()
{
std::string s0 = String::Format( "%s %dSX SR%dDE%s", "日...
std::wstring ws0 = String::Format( L"BNR%d RB%dDE%s", 32...
std::string s1 = String::Format( "%s %dSX SR%dDE%s", 180...
const std::string s2a = u8"トヨタ";
const std::string s2b = "1JZ-GTE";
std::string s2c = String::Format( "%s jzx90 %s", s2a, s2...
CString t0(_T("マツダ"));
const CString t1(_T("13B-T"));
auto tr = String::Format(_T("%s FC3S %s"),t0,t1); // tr...
}
}}
#endregion
* 追記履歴
- C++17 を使って諸々改修した版を追加しました 2021/3
- CString 対応コードを追加しました 2019/5/15
終了行:
C++ だと文字列をフォーマットするコードって結構大変です。
C++11(14) になって色々な選択肢は増えたんですが、いまいち...
というわけで boost です。boost::format を使った文字列フォ...
% で繋げることなく、可変引数テンプレートを使って printf ...
boost::format の機能のおかげで、型安全で例外が発生するこ...
&font(80%){例えばアプリで処理失敗が発生したときに、アプリ...
printf 系の関数は(windows の CString::Format とかも)使...
がしかし printf 系の使いやすさは捨て難いです。慣れちゃっ...
そんなわけで printf 風の書き方も出来てしまう boost::forma...
開発者の方に感謝しつつ使わせていただこうと思います。
#contents
** 2021/3 追記
C++17 を前提にコードを刷新いたしました。
と同時に、色々な型を展開して出力する機能を追加してみまし...
基本的な使い方は変わってませんが、引数に渡せる型を景気よ...
std::pair std::tuple
std::unique_ptr std::shared_ptr std::optional
std::array std::vector std::list std::deque std::initiali...
std::set std::multiset std::map std::multimap
これらが渡された場合は、その中身を出力します。
&font(80%){std::tuple や std::unique_ptr などは、まぁ使い...
*** github にも公開しました
[[rlib-StringFormat>https://github.com/tr-takatsuka/rlib-...
*** よもやま
C++17 の if constexpr はとても便利だなと感じます。
その分、昨今のC++の発展っぷりに付いていくことは、自分程度...
そのため、わからないことはすぐにネットで答えを探していま...
そしてここまで複雑な(と自分は思う) C++言語の、仕様決めを...
ありがとうございます。
* コード
- 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::a...
template<class> struct IsVector : std::false_type {};
template<class T1, class T2> struct IsVector<std::vect...
template<class> struct IsList : std::false_type {};
template<class T1, class T2> struct IsList<std::list<T...
template<class> struct IsDeque : std::false_type {};
template<class T1, class T2> struct IsDeque<std::deque...
template<class> struct IsInitializerList : std::false_...
template<class T> struct IsList<std::initializer_list<...
template<class> struct IsSet : std::false_type {};
template<class T1, class T2, class T3> struct IsSet<st...
template<class> struct IsMultiSet : std::false_type {};
template<class T1, class T2, class T3> struct IsMultiS...
template<class> struct IsMap : std::false_type {};
template<class T1, class T2, class T3, class T4> struc...
template<class> struct IsMultiMap : std::false_type {};
template<class T1, class T2, class T3, class T4> struc...
template<class> struct IsPair : std::false_type { };
template<class T1, class T2> struct IsPair<std::pair<T...
template<class> struct IsUniquePtr : std::false_type {};
template<class T1, class T2> struct IsUniquePtr<std::u...
template<class> struct IsSharedPtr : std::false_type {};
template<class T> struct IsSharedPtr<std::shared_ptr<T...
template<class> struct IsOptional : std::false_type {};
template<class T> struct IsOptional<std::optional<T>> ...
template<class> struct IsTuple : std::false_type {};
template<class... T> struct IsTuple<std::tuple<T...>> ...
template <class CharT, class Head, class... Tail> void...
template <class CharT> void f(const boost::basic_forma...
template <std::size_t I, class CharT, class... T, clas...
if constexpr (I < sizeof...(T)) {
f<I + 1, CharT>(format % std::get<I>(head), head, ta...
} else {
f<CharT>(format, tail...);
}
}
template <class CharT, class Head, class... Tail> void...
using HeadT = std::remove_const_t<Head>;
if constexpr (IsUniquePtr<HeadT>::value|| IsSharedPtr...
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 || IsVecto...
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> ...
f<CharT>(format % std::basic_string<CharT>(head.GetS...
#endif
} else {
f<CharT>(format % head, tail...);
}
}
}
template <class CharT, class... Args> std::basic_string...
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...
return format<CharT>(s.c_str(), args...);
}
}
}
}}
* 使い方の例
- こちらのコードでは printf っぽい書式しか使っておりませ...
#prettify(){{
#include "StringFormat.h" // ヘッダをインクルードするのみ...
void main() {
using namespace rlib::string;
{// 普通の使い方
std::string r = format(u8"%s %dSX SR%dDE%s", u8"日産", ...
}
{// std::wstring も可です
std::wstring r = format(L"BNR%d RB%dDE%s", 32, 26, L"TT...
}
{// 適してない型でも例外発生しません
std::string r = format("%dSX %s", 240, L"日産"); // "24...
}
{// std::string をそのまま書けます。c_str() が不要なので...
const std::string a = u8"トヨタ";
std::string b = "1JZ-GTE";
std::string r = format("%s jzx90 %s", a, b); // "トヨタ...
}
#ifdef _MSC_VER
{// VisualStudio の CString
CString t0(_T("マツダ"));
const CString t1(_T("13B-T"));
auto r = format(_T("%s FC3S %s"), t0, t1); // L"マツダ ...
}
#endif
{// std::tuple std::pair は展開します。
std::tuple<std::string, int, double> a{ "RPS13",1998,11...
const std::pair<std::string, int> b{ "FR", 1848000 };
auto r = format( // "形式:RPS13 排気量:1998cc 燃費:11...
u8"形式:%s 排気量:%dcc 燃費:%.1fkm/l 駆動方式:%s 価格:...
}
{// std::unique_ptr std::shared_ptr std::optional は中身...
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,(...
}
{// その他コンテナも展開します。(やりすぎかもしれません...
auto r0 = format("%d,%d,%d", std::array<int, 3>{ 1, 2, ...
auto r1 = format("%d,%d,%d", std::vector<int>{4, 5, 6})...
auto r2 = format("%d,%d,%d", std::list<int>{7, 8, 9}); ...
auto r3 = format("%d,%d,%d", std::deque<int>{10, 11, 12...
auto r4 = format("%d,%d,%d", std::set<int>{3, 2, 1}); ...
auto r5 = format("%d,%d,%d", std::map<int, int>{ {1, 2}...
auto r6 = format("%d,%d,%d", std::multiset<int>{1, 2, 3...
auto r7 = format("%d,%d,%d", std::multimap<int, int>{ {...
}
{// コンテナの中も再帰で展開するので以下みたいな型も可です
std::map<int, std::pair<std::string, std::vector<int>>>...
{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); // コンパイルエラー
}
}}
* 以前の版
#region(以前の版)
#prettify{{
namespace String
{
template <class CharT> std::basic_string<CharT> Format(co...
{
return format.str();
}
#ifdef _MSC_VER // VisualC++ の CString 対応
template <class CharT,class... Tail> std::basic_string<Ch...
{
return Format<CharT>( format % std::basic_string<CharT>(...
}
template <class CharT,class... Tail> std::basic_string<Ch...
{
return Format<CharT>( format % std::basic_string<CharT>(...
}
template <class CharT,class... Tail> std::basic_string<Ch...
{
return Format<CharT>( format % std::basic_string<CharT>(...
}
template <class CharT,class... Tail> std::basic_string<Ch...
{
return Format<CharT>( format % std::basic_string<CharT>(...
}
#endif
template <class CharT,class Head,class... Tail> std::basi...
{
return Format<CharT>( format % head, tail... );
}
template <class CharT,class... Args> std::basic_string<Ch...
{
boost::basic_format<CharT> format;
format.exceptions( boost::io::no_error_bits ); // 例外を...
format.parse(lpszFormat);
return Format<CharT>(format,args...);
}
}
// 使い方例
int main()
{
std::string s0 = String::Format( "%s %dSX SR%dDE%s", "日...
std::wstring ws0 = String::Format( L"BNR%d RB%dDE%s", 32...
std::string s1 = String::Format( "%s %dSX SR%dDE%s", 180...
const std::string s2a = u8"トヨタ";
const std::string s2b = "1JZ-GTE";
std::string s2c = String::Format( "%s jzx90 %s", s2a, s2...
CString t0(_T("マツダ"));
const CString t1(_T("13B-T"));
auto tr = String::Format(_T("%s FC3S %s"),t0,t1); // tr...
}
}}
#endregion
* 追記履歴
- C++17 を使って諸々改修した版を追加しました 2021/3
- CString 対応コードを追加しました 2019/5/15
ページ名: