1. はじめに
どこから拾ってきたのか忘れてしまいましたが、それを元に std::string に対して printf みたいな書式付き出力をするクラス(?) を作りました。format は C++20 から使えるようですが、こちらは C++11 くらいから使用でき、既存の使い方に慣れている方ならすぐ使えます。wchar_t版も用意しました。
2. ということで(Windows限定)コード
StringUtil.h
// StringUtil.h
#pragma once
#include <stdexcept>
#include <stdarg.h>
#include <vector>
#include <string>
class CStringUtil
{
public:
static std::string format(const char *format, ...)
{
va_list marker;
va_start(marker, format);
size_t nSize = getFormatSize(format, marker);
va_end(marker);
if (nSize <= 0)
{
throw std::runtime_error("Failed to calculate format size.");
}
// 終端文字分を確保
std::vector<char> vStr(nSize + 1, '\0');
va_start(marker, format);
int result = vsnprintf(vStr.data(), vStr.size(), format, marker);
va_end(marker);
if (result < 0)
{
throw std::runtime_error("String formatting failed.");
}
return std::string(vStr.data());
}
static std::wstring format(const wchar_t *format, ...)
{
va_list marker;
va_start(marker, format);
size_t nSize = getFormatSize(format, marker);
va_end(marker);
if (nSize <= 0)
{
throw std::runtime_error("Failed to calculate format size.");
}
// 終端文字分を確保
std::vector<wchar_t> vStr(nSize + 1, L'\0');
va_start(marker, format);
int result = vswprintf(vStr.data(), vStr.size(), format, marker);
va_end(marker);
if (result < 0)
{
throw std::runtime_error("String formatting failed.");
}
return std::wstring(vStr.data());
}
private:
static int getFormatSize(const char *format, va_list args)
{
int size = vsnprintf(nullptr, 0, format, args);
return size;
}
static int getFormatSize(const wchar_t *format, va_list args)
{
// note: this is for Windows only
int size = _vscwprintf(format, args);
return size;
}
CStringUtil() {}
~CStringUtil() {}
};
3. Windows以外はどうする?
wchar_t を与えた場合に 文字数を数える関数が見当たりません。
ということで /dev/null が使える環境限定になりますけれども、こんなコードはいかがでしょうか(一部抜粋)。
static int getFormatSize(const wchar_t *format, va_list args)
{
FILE* fp = fopen("/dev/null", "wb");
if(fp == nullptr) {
return -1;
}
int result = vfwprintf(fp, format, args);
fclose(fp);
return result;
}