C++11を使うと綺麗に実装できます
まず format() というテンプレート関数を作ります.C++11で新しく導入された,可変引数テンプレート,および std::snprintf() を使います.
#include <string> #include <cstdio> #include <vector> template <typename ... Args> std::string format(const std::string& fmt, Args ... args ) { size_t len = std::snprintf( nullptr, 0, fmt.c_str(), args ... ); std::vector<char> buf(len + 1); std::snprintf(&buf[0], len + 1, fmt.c_str(), args ... ); return std::string(&buf[0], &buf[0] + len); }
あとはこれを呼ぶだけ.
#include <iostream> int main() { std::cout << format("%e", 1./3) << std::endl; return 0; }
format()はstd::string を返すので .c_str()と組みわせることでメモリリークなしでc-stringを動的に生成することもできます
#include <iostream> void echo(const char *str) { printf("%s\n", str); } int main() { echo("Hello C++11"); echo(format("%e", 1./3).c_str()); // c_str()をつかう return 0; }
なお format()が返す std::string は一時オブジェクトなので、以下のコードは正常に動作しません
int main() { echo("Hello C++11"); const char *s = format("%e", 1./3).c_str(); // この時点で 一時オブジェクトは消滅しているので // ポインタ sが指し示すアドレスのメモリは無効になってる echo(s); return 0; }
この場合は次のように書き換えると、正常に動作します
int main() { echo("Hello C++11"); std::string str = format("%e", 1./3); // 一時オブジェクトを str にコピー const char *s = str.c_str(); echo(s); return 0; }
補足
C++ で "%d" のような書式指定を行うには boost::format を使う方法もあります
#include <iostream> #include <boost/format.hpp> int main() { std::cout << boost::format("%2% %1%") % 3 % std::string("Hello") << std::endl; }
という感じになります.しかしこのコードは
- 慣れてない人は全く読めない(悪い意味でboost的)
- boost のインストールが少々面倒(boostのbcpを知っていれば少しは楽ですが,再配布や納品時,ライセンスまで考えるとやはり面倒)
という点が不便でした.
一方,C++11版なら,
- コンパクト
- 移植性もバッチリ(追加ライブラリ不要.visual studio, clang, gccどれでも標準ライブラリだけで動きます)
という感じで使い勝手がいいかと思います.