Poco::format

今まで紹介してきたソースの中に何度も登場してはいましたが、改めて 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日からのダウンロード数:944

Subversion

・フリーの Subversion ホスティングサービス Assemblaで、ソースコードを管理しています。

Reference

http://pocoproject.org にある StringsAndFormatting のプレセンテーション。(PDF)

Powered by POCO Copyright © 2010 Round Square Inc. All rights reserved.


Leave a Comment


NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>