今まで紹介してきたソースの中に何度も登場してはいましたが、改めて Poco::format を紹介します。
例えば、C で次のように書いていた箇所:
int numHardDrive = 5; printf("The PC has %d external Hard Drives.\n", numHardDrive); |
を C++ っぽく書いてみると:
int numHardDrive = 5; std::cout << "The PC has " << numHardDrive << " external Hard Drives.\n"; |
となると思いますが、ここに数値などの書式が絡んできたりすると、iomanip を include したりして、結構厄介なことになってしまいます。
そこで、Poco::format を使うと:
int numHardDrive = 5; std::cout << Poco::format("The PC has %d external Hard Drives.\n", numHardDrive); |
と、ほとんど printf のように書けるので楽です。
Poco::format のプロトタイプを列挙してみます。
まずは基本形:
std::string Poco::format(const std::string& fmt, const Poco::Any& value); std::string Poco::format(const std::string& fmt, const Poco::Any& value1, const Poco::Any& value2); std::string Poco::format(const std::string& fmt, const Poco::Any& value1, const Poco::Any& value2, const Poco::Any& value3); std::string Poco::format(const std::string& fmt, const Poco::Any& value1, const Poco::Any& value2, const Poco::Any& value3, const Poco::Any& value4); std::string Poco::format(const std::string& fmt, const Poco::Any& value1, const Poco::Any& value2, const Poco::Any& value3, const Poco::Any& value4, const Poco::Any& value5); std::string Poco::format(const std::string& fmt, const Poco::Any& value1, const Poco::Any& value2, const Poco::Any& value3, const Poco::Any& value4, const Poco::Any& value5, const Poco::Any& value6); |
ここで、fmt は printf の書式指定文字列的なもの。
見ての通り、最大 6 個のパラメータまでしかありません。
続いて result 文字列の末尾に追加されるタイプ:
void Poco::format(std::string& result, const std::string& fmt, const Poco::Any& value); void Poco::format(std::string& result, const std::string& fmt, const Poco::Any& value1, const Poco::Any& value2); void Poco::format(std::string& result, const std::string& fmt, const Poco::Any& value1, const Poco::Any& value2, const Poco::Any& value3); void Poco::format(std::string& result, const std::string& fmt, const Poco::Any& value1, const Poco::Any& value2, const Poco::Any& value3, const Poco::Any& value4); void Poco::format(std::string& result, const std::string& fmt, const Poco::Any& value1, const Poco::Any& value2, const Poco::Any& value3, const Poco::Any& value4, const Poco::Any& value5); void Poco::format(std::string& result, const std::string& fmt, const Poco::Any& value1, const Poco::Any& value2, const Poco::Any& value3, const Poco::Any& value4, const Poco::Any& value5, const Poco::Any& value6); void Poco::format(std::string& result, const std::string& fmt, const std::vector < Poco::Any >& values); |
こちらにだけは、パラメータをベクタで渡せるものがなぜか用意されています。
Poco::format を使うにあたって注意しなければいけないのは、
『fmt で指定した型と違う型のパラメータが渡された場合、例外が飛ぶ』
ということです。
(きちんと try – catch していなかった場合、突然飛ぶようになって面食らったりします)
パラメータと fmt で指定する型との対応は、Poco::format に書いてはあるのですが、分かりにくかったので、表にしてみました。(表示幅や 0 詰めなどは、printf から類推できるので省略)
Parameter Type | fmt | |
---|---|---|
bool | %b | %?i |
char | %c | |
int | %d, %i | |
unsigned | %u | |
short | %hd | |
unsigned short | %hu | |
long | %ld | |
unsigned long | %lu | |
long long | %Ld | |
unsigned long long | %Lu | |
unsigned (octal) | %o | |
unsigned (hex) | %x, %X | |
double | %f, %e, %E | |
float | %hf, %he, %hE | |
std::string | %s | |
std::size_t | %z |
ここで、%?i は整数系に対しては万能なので、例外嫌いな人はこれを使うのも良いかもしれません。
FormatTest.cpp
・FormatTest1() で、1 パラメータの場合、FormatTest2() で、2 パラメータの場合の処理。
共通部分は FormatTestCommon()。
・BadCastExceptionTest() で、fmt 内に指定した型とパラメータ型が異なった場合に飛ぶ例外を処理。
・Test*() で、それぞれの型ごとのテスト。
・TestAnyInteger() では(整数系の)全ての型で、例外が飛ばずに処理されることを確認。
#include <Poco/Format.h> #include "ScopedLogMessage.h" #include "PrepareConsoleLogger.h" void FormatTestCommon(std::string& str, const std::string& fmt, ScopedLogMessage& msg) { const std::size_t kstrLength = 32; do { str += " "; } while(str.length() < kstrLength); str += "[fmt=\""+fmt+"\"]"; msg.Message(str); } template <class T> void FormatTest1(const T& data, const std::string& fmt, ScopedLogMessage& msg) { std::string str = Poco::format(" "+fmt, data); FormatTestCommon(str, fmt, msg); } template <class T> void FormatTest2(const T& data1, const T& data2, const std::string& fmt, ScopedLogMessage& msg) { std::string str = Poco::format(" "+fmt, data1, data2); FormatTestCommon(str, fmt, msg); } template <class T> void BadCastExceptionTest(const T& data, const std::string& fmt, const std::string& type, ScopedLogMessage& msg) { try { msg.Message(Poco::format(fmt, data)); } catch (Poco::BadCastException&) { msg.Message(" [fmt=\""+fmt+"\"] does not accept "+type); } } void TestBoolean(void) { ScopedLogMessage msg("TestBoolean ", "start", "end\n"); bool t = true; bool f = false; FormatTest2(t, f, "true=%b, false=%b", msg); FormatTest2(t, f, "true=%-2b, false=%2b", msg); BadCastExceptionTest(static_cast<int>(1), "true=%b", "int", msg); } void TestCharacter(void) { ScopedLogMessage msg("TestCharacter ", "start", "end\n"); char c = 'a'; FormatTest1(c, "char=%c", msg); FormatTest2(c, c, "char=%-2c, char=%2c", msg); BadCastExceptionTest(std::string("foo"), "char=%c", "std::string", msg); } void TestSignedInteger(void) { ScopedLogMessage msg("TestSignedInteger ", "start", "end\n"); const int i = 12; FormatTest1(i, "int=%d", msg); FormatTest2(i, i, "int=%-3d, int=%3d", msg); FormatTest2(i, -1*i, "int=%+4d, int=%+4d", msg); BadCastExceptionTest(static_cast<short>(56), "int=%d", "short", msg); } void TestUnsignedInteger(void) { ScopedLogMessage msg("TestUnsignedInteger ", "start", "end\n"); const unsigned i = 12; FormatTest1(i, "unsigned=%u", msg); FormatTest2(i, i, "unsigned=%-3u, unsigned=%3u", msg); BadCastExceptionTest(static_cast<int>(56), "unsigned=%u", "int", msg); } void TestSignedShortInteger(void) { ScopedLogMessage msg("TestSignedShortInteger ", "start", "end\n"); const short i = 12; FormatTest1(i, "short=%hd", msg); FormatTest2(i, i, "short=%-3hd, short=%3hd", msg); FormatTest2(i, static_cast<short>(-1*i), "short=%+4hd, short=%+4hd", msg); BadCastExceptionTest(static_cast<int>(56), "short=%hd", "int", msg); } void TestUnsignedShortInteger(void) { ScopedLogMessage msg("TestUnsignedShortInteger ", "start", "end\n"); const unsigned short i = 12; FormatTest1(i, "ushort=%hu", msg); FormatTest2(i, i, "ushort=%-3hu, ushort=%3hu", msg); BadCastExceptionTest(static_cast<int>(56), "ushort=%hu", "int", msg); } void TestSignedLongInteger(void) { ScopedLogMessage msg("TestSignedLongInteger ", "start", "end\n"); const long i = 12; FormatTest1(i, "long=%ld", msg); FormatTest2(i, i, "long=%-3ld, long=%3ld", msg); FormatTest2(i, static_cast<long>(-1*i), "long=%+4ld, long=%+4ld", msg); BadCastExceptionTest(static_cast<int>(56), "long=%ld", "int", msg); } void TestUnsignedLongInteger(void) { ScopedLogMessage msg("TestUnsignedLongInteger ", "start", "end\n"); const unsigned long i = 12; FormatTest1(i, "ulong=%lu", msg); FormatTest2(i, i, "ulong=%-3lu, ulong=%3lu", msg); BadCastExceptionTest(static_cast<unsigned>(56), "ulong=%lu", "unsigned", msg); } void TestSignedLongLongInteger(void) { ScopedLogMessage msg("TestSignedLongLongInteger ", "start", "end\n"); const Poco::Int64 i = 12; FormatTest1(i, "Int64=%Ld", msg); FormatTest2(i, i, "Int64=%-3Ld, Int64=%3Ld", msg); FormatTest2(i, static_cast<Poco::Int64>(-1*i), "Int64=%+4Ld, Int64=%+4Ld", msg); BadCastExceptionTest(static_cast<int>(56), "Int64=%Ld", "int", msg); } void TestUnsignedLongLongInteger(void) { ScopedLogMessage msg("TestUnsignedLongLongInteger ", "start", "end\n"); const Poco::UInt64 i = 12; FormatTest1(i, "UInt64=%Lu", msg); FormatTest2(i, i, "UInt64=%-3Lu, UInt64=%3Lu", msg); BadCastExceptionTest(static_cast<unsigned>(56), "UInt64=%Lu", "unsigned", msg); } void TestUnsignedOctalInteger(void) { ScopedLogMessage msg("TestUnsignedOctalInteger ", "start", "end\n"); const unsigned i = 012; FormatTest1(i, "octal=%o", msg); FormatTest1(i, "octal=%#o", msg); FormatTest2(i, i, "octal=%4o, octal=%04o", msg); BadCastExceptionTest(static_cast<int>(56), "octal=%o", "int", msg); } void TestUnsignedHexInteger(void) { ScopedLogMessage msg("TestUnsignedHexInteger ", "start", "end\n"); const unsigned i = 0x1A; FormatTest1(i, "hex=%x", msg); FormatTest1(i, "hex=%X", msg); FormatTest1(i, "hex=%#x", msg); FormatTest1(i, "hex=%#X", msg); FormatTest2(i, i, "hex=%4x, hex=%04x", msg); FormatTest2(i, i, "hex=%4X, hex=%04X", msg); BadCastExceptionTest(static_cast<short>(56), "hex=%x", "short", msg); } void TestDouble(void) { ScopedLogMessage msg("TestDouble ", "start", "end\n"); const double i = 1.2; FormatTest1(i, "double=%f", msg); FormatTest2(i, i, "double=%-5.2f, double=%5.2f", msg); FormatTest1(i, "double=%e", msg); FormatTest2(i, i, "double=%-9.2e, double=%9.2e", msg); BadCastExceptionTest(static_cast<float>(1.2), "double=%f", "float", msg); } void TestFloat(void) { ScopedLogMessage msg("TestFloat ", "start", "end\n"); const float i = 1.2f; FormatTest1(i, "float=%hf", msg); FormatTest2(i, i, "float=%-5.2hf, float=%5.2hf", msg); FormatTest1(i, "float=%he", msg); FormatTest2(i, i, "float=%-9.2he, float=%9.2he", msg); BadCastExceptionTest(static_cast<double>(1.2), "float=%hf", "double", msg); } void TestString(void) { ScopedLogMessage msg("TestString ", "start", "end\n"); const std::string str("bar"); FormatTest1(str, "string=%s", msg); FormatTest2(str, str, "string=%-4s, string=%4s", msg); BadCastExceptionTest(static_cast<const char*>("test"), "string=%s", "const char*", msg); } void TestSize_t(void) { ScopedLogMessage msg("TestSize_t ", "start", "end\n"); const std::size_t i = 12; FormatTest1(i, "size_t=%z", msg); FormatTest2(i, i, "size_t=%-3z, size_t=%3z", msg); BadCastExceptionTest(static_cast<int>(12), "size_t=%z", "int", msg); } void TestAnyInteger(void) { ScopedLogMessage msg("TestAnyInteger ", "start", "end\n"); FormatTest1(static_cast<bool>(true), "bool=%?i", msg); FormatTest1(static_cast<char>(42), "char=%?i", msg); FormatTest1(static_cast<signed char>(-42), "signed char=%?i", msg); FormatTest1(static_cast<unsigned char>(65), "unsigned char=%?i", msg); FormatTest1(static_cast<short>(-134), "short=%?i", msg); FormatTest1(static_cast<unsigned short>(200), "unsigned short=%?i", msg); FormatTest1(static_cast<int>(-12345), "int=%?i", msg); FormatTest1(static_cast<unsigned>(12345), "unsigned=%?i", msg); FormatTest1(static_cast<long>(-54321), "long=%?i", msg); FormatTest1(static_cast<unsigned long>(54321), "unsigned long=%?i", msg); FormatTest1(static_cast<Poco::Int64>(-12345678), "Poco::Int64=%?i", msg); FormatTest1(static_cast<Poco::UInt64>(12345678), "Poco::UInt64=%?i", msg); FormatTest1(static_cast<short>(012), "octal=%#?o", msg); FormatTest1(static_cast<short>(127), "hex=%#?x", msg); } typedef void (*TestProc)(void); TestProc testProcs[] = { TestBoolean , TestCharacter , TestSignedInteger , TestUnsignedInteger , TestSignedShortInteger , TestUnsignedShortInteger , TestSignedLongInteger , TestUnsignedLongInteger , TestSignedLongLongInteger , TestUnsignedLongLongInteger , TestUnsignedOctalInteger , TestUnsignedHexInteger , TestDouble , TestFloat , TestString , TestSize_t , TestAnyInteger }; int main(int /*argc*/, char** /*argv*/) { PrepareConsoleLogger logger(Poco::Logger::ROOT, Poco::Message::PRIO_INFORMATION); ScopedLogMessage msg("FormatTest ", "start\n", "end"); for(std::size_t i=0; i<sizeof(testProcs)/sizeof(testProcs[0]); ++i) { testProcs[i](); } return 0; } |
Results of execution
[0] FormatTest start [0] TestBoolean start [0] true=1, false=0 [fmt="true=%b, false=%b"] [0] true=1 , false= 0 [fmt="true=%-2b, false=%2b"] [0] [fmt="true=%b"] does not accept int [0] TestBoolean end [0] TestCharacter start [0] char=a [fmt="char=%c"] [0] char=a , char= a [fmt="char=%-2c, char=%2c"] [0] [fmt="char=%c"] does not accept std::string [0] TestCharacter end [0] TestSignedInteger start [0] int=12 [fmt="int=%d"] [0] int=12 , int= 12 [fmt="int=%-3d, int=%3d"] [0] int= +12, int= -12 [fmt="int=%+4d, int=%+4d"] [0] [fmt="int=%d"] does not accept short [0] TestSignedInteger end [0] TestUnsignedInteger start [0] unsigned=12 [fmt="unsigned=%u"] [0] unsigned=12 , unsigned= 12 [fmt="unsigned=%-3u, unsigned=%3u"] [0] [fmt="unsigned=%u"] does not accept int [0] TestUnsignedInteger end [0] TestSignedShortInteger start [0] short=12 [fmt="short=%hd"] [0] short=12 , short= 12 [fmt="short=%-3hd, short=%3hd"] [0] short= +12, short= -12 [fmt="short=%+4hd, short=%+4hd"] [0] [fmt="short=%hd"] does not accept int [0] TestSignedShortInteger end [0] TestUnsignedShortInteger start [0] ushort=12 [fmt="ushort=%hu"] [0] ushort=12 , ushort= 12 [fmt="ushort=%-3hu, ushort=%3hu"] [0] [fmt="ushort=%hu"] does not accept int [0] TestUnsignedShortInteger end [0] TestSignedLongInteger start [0] long=12 [fmt="long=%ld"] [0] long=12 , long= 12 [fmt="long=%-3ld, long=%3ld"] [0] long= +12, long= -12 [fmt="long=%+4ld, long=%+4ld"] [0] [fmt="long=%ld"] does not accept int [0] TestSignedLongInteger end [0] TestUnsignedLongInteger start [0] ulong=12 [fmt="ulong=%lu"] [0] ulong=12 , ulong= 12 [fmt="ulong=%-3lu, ulong=%3lu"] [0] [fmt="ulong=%lu"] does not accept unsigned [0] TestUnsignedLongInteger end [0] TestSignedLongLongInteger start [0] Int64=12 [fmt="Int64=%Ld"] [0] Int64=12 , Int64= 12 [fmt="Int64=%-3Ld, Int64=%3Ld"] [0] Int64= +12, Int64= -12 [fmt="Int64=%+4Ld, Int64=%+4Ld"] [0] [fmt="Int64=%Ld"] does not accept int [0] TestSignedLongLongInteger end [0] TestUnsignedLongLongInteger start [0] UInt64=12 [fmt="UInt64=%Lu"] [0] UInt64=12 , UInt64= 12 [fmt="UInt64=%-3Lu, UInt64=%3Lu"] [0] [fmt="UInt64=%Lu"] does not accept unsigned [0] TestUnsignedLongLongInteger end [0] TestUnsignedOctalInteger start [0] octal=12 [fmt="octal=%o"] [0] octal=012 [fmt="octal=%#o"] [0] octal= 12, octal=0012 [fmt="octal=%4o, octal=%04o"] [0] [fmt="octal=%o"] does not accept int [0] TestUnsignedOctalInteger end [0] TestUnsignedHexInteger start [0] hex=1a [fmt="hex=%x"] [0] hex=1A [fmt="hex=%X"] [0] hex=0x1a [fmt="hex=%#x"] [0] hex=0X1A [fmt="hex=%#X"] [0] hex= 1a, hex=001a [fmt="hex=%4x, hex=%04x"] [0] hex= 1A, hex=001A [fmt="hex=%4X, hex=%04X"] [0] [fmt="hex=%x"] does not accept short [0] TestUnsignedHexInteger end [0] TestDouble start [0] double=1.200000 [fmt="double=%f"] [0] double=1.20 , double= 1.20 [fmt="double=%-5.2f, double=%5.2f"] [0] double=1.200000e+00 [fmt="double=%e"] [0] double=1.20e+00 , double= 1.20e+00 [fmt="double=%-9.2e, double=%9.2e"] [0] [fmt="double=%f"] does not accept float [0] TestDouble end [0] TestFloat start [0] float=1.200000 [fmt="float=%hf"] [0] float=1.20 , float= 1.20 [fmt="float=%-5.2hf, float=%5.2hf"] [0] float=1.200000e+00 [fmt="float=%he"] [0] float=1.20e+00 , float= 1.20e+00 [fmt="float=%-9.2he, float=%9.2he"] [0] [fmt="float=%hf"] does not accept double [0] TestFloat end [0] TestString start [0] string=bar [fmt="string=%s"] [0] string=bar , string= bar [fmt="string=%-4s, string=%4s"] [0] [fmt="string=%s"] does not accept const char* [0] TestString end [0] TestSize_t start [0] size_t=12 [fmt="size_t=%z"] [0] size_t=12 , size_t= 12 [fmt="size_t=%-3z, size_t=%3z"] [0] [fmt="size_t=%z"] does not accept int [0] TestSize_t end [0] TestAnyInteger start [0] bool=1 [fmt="bool=%?i"] [0] char=42 [fmt="char=%?i"] [0] signed char=-42 [fmt="signed char=%?i"] [0] unsigned char=65 [fmt="unsigned char=%?i"] [0] short=-134 [fmt="short=%?i"] [0] unsigned short=200 [fmt="unsigned short=%?i"] [0] int=-12345 [fmt="int=%?i"] [0] unsigned=12345 [fmt="unsigned=%?i"] [0] long=-54321 [fmt="long=%?i"] [0] unsigned long=54321 [fmt="unsigned long=%?i"] [0] Poco::Int64=-12345678 [fmt="Poco::Int64=%?i"] [0] Poco::UInt64=12345678 [fmt="Poco::UInt64=%?i"] [0] octal=012 [fmt="octal=%#?o"] [0] hex=0x7f [fmt="hex=%#?x"] [0] TestAnyInteger end [0] FormatTest end |
Downloads
・ここをクリックすると、makefile や VC++ プロジェクトなど一式がダウンロードできます。
(2013.05.31 updated)
・2010年5月9日からのダウンロード数:1236
Subversion
・フリーの Subversion ホスティングサービス Assemblaで、ソースコードを管理しています。
Reference
・http://pocoproject.org にある StringsAndFormatting のプレセンテーション。(PDF)
![]() |
Copyright © 2010 Round Square Inc. All rights reserved. |
---|
0 Comments.