0. 前回のあらすじ
自作の C プログラムのテストにカバレッジ測定が必要となった。オープンソースの OpenCppCoverage を使いたいが,HTML レポートを出力すると日本語が文字化けしてしまうらしい。文字化けを修正するためにもまず自力でソースコードからバイナリをビルドできるようにした。そして前回は文字化けの原因究明まで行った。
1. やりたいことの整理
- バージョンを 0.9.9.0 から 0.9.9.1 に変える。
- コマンドプロンプト実行時の日本語パス名の文字化け対策
- ソースコード中のタブ幅をコマンドラインオプションから指定できるようにする。
- HTML レポートの日本語文字化け対策
- ソースコードに BOM がある場合はそれを優先する。
- BOM が無い場合,コマンドラインオプションで指定したコードページが適用される。
デフォルトのコードページは CP_ACP(ANSI コードページ)とする。
2. 具体的な変更内容
2.1 バージョンの変更
HTML レポートに OpenCppCoverage のバージョンが記載されるので,世間一般に出回っているものとは違うものを使ったことを示すため(誠に勝手ながら)バージョンを書き換えることにした。Visual Studio での変更方法が分からんのでテキストエディタで以下のように直接プロパティシートを書き換えた。
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Label="PropertySheets" />
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>$(SolutionDir);$(ProjectDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
- <PreprocessorDefinitions>OPENCPPCOVERAGE_VERSION="0.9.9.0";_CRT_SECURE_NO_WARNINGS;PROJECT_DIR=R"|($(ProjectDir))|";SOLUTION_DIR=R"|($(SolutionDir))|";TARGET_FILE_NAME=R"|($(TargetFileName))|";OUT_DIR=R"|($(OutDir))|";NOMINMAX;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions>OPENCPPCOVERAGE_VERSION="0.9.9.1";_CRT_SECURE_NO_WARNINGS;PROJECT_DIR=R"|($(ProjectDir))|";SOLUTION_DIR=R"|($(SolutionDir))|";TARGET_FILE_NAME=R"|($(TargetFileName))|";OUT_DIR=R"|($(OutDir))|";NOMINMAX;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<TreatWarningAsError>true</TreatWarningAsError>
<UseFullPaths>true</UseFullPaths>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup />
</Project>
2.2. コマンドプロンプト実行時の日本語パス名の文字化け対策
OpenCppCoverage をコマンドプロンプトから使用すると,オプション設定や処理過程のログを表示してくれるのだが,困ったことにソースコードのパス名などに日本語を使うとログが文字化けしてしまう。
そもそもの話として,ソースコードのファイル名に日本語を使うな!というのはある。もちろん筆者はソースコードのファイル名に日本語を使ったこともないし見たこともないが,フォルダ名に日本語が使われていることは時々あるのだ。そうするとコンソールに出力されるログの文字が化けてしまう。ただ,ファイルに出力されるログファイルのほうは問題ないし,コマンドプロンプトのコードページをUTF-8 に切り替える(CHCP 65001
と打つ)ことによっても解決する。だが,直したい!
ログ出力の管理に Boost.Log を使っていることが分かったときは恐怖したが,幸いなことに Boost c++ ライブラリ本体ではなく,OpenCppCoverage 側の修正,それも僅かな修正で済んだ。
まず,コンソール出力のロケールを UTF-8 に変更していたので止めさせた。
void InitConsoleAndFileLog(const std::filesystem::path& logPath)
{
boost::log::add_common_attributes();
auto fileSink = logging::add_file_log(logPath.wstring(),
boost::log::keywords::format =
expr::stream
<< "[" << expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
<< "] [" << logging::trivial::severity
<< "] " << expr::message);
auto consoleSink = logging::add_console_log(std::clog,
boost::log::keywords::format =
expr::stream
<< "[" << logging::trivial::severity
<< "] " << expr::message
);
// Set correct endocing for special char
auto loc = boost::locale::generator()("en_US.UTF-8");
fileSink->imbue(loc);
- consoleSink->imbue(loc);
}
次に main()
の冒頭にロケール設定を追加した。これだけで解決できた。
int main(int argc, const char* argv[])
{
+ std::locale::global(std::locale(""));
Tools::CreateMiniDumpOnUnHandledException();
try
{
OpenCppCoverage::OpenCppCoverage openCppCoverage;
return openCppCoverage.Run(argc, argv, &std::wcerr);
}
catch (const std::exception& e)
{
std::cerr << "Error: " << e.what() << std::endl;
}
catch (...)
{
std::cerr << "Unknown error" << std::endl;
}
return OpenCppCoverage::FailureExitCode;
}
PowerShell とか Windows Terminal では動作未確認である。
2.3. ソースコードのタブ幅指定 ※コレはかなり大変!!
筆者はスペース幅4文字のハードタブ派であり,自分の書いたソースコードの大半はそうなっているのだが,HTML レポート出力させると8文字幅になっている。これを4文字幅に変更するには技術的にはさほど難しくはなく,HTML ファイルあるいは同時に出力されるスタイルシートにタブ幅指定を追加すれば良いだけだ。
まず HTML ファイルのテンプレートを次のように書き換えた。あとは指定したタブ幅に応じてテンプレートの TAB_STOPS
を書き変えれば良い。
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8"/>
<title>{{TITLE}}</title>
<link href="../../third-party/google-code-prettify/prettify-CppCoverage.css" type="text/css" rel="stylesheet" />
<script type="text/javascript" src="../../third-party/google-code-prettify/prettify.js"></script>
</head>
<body onload="{{BODY_ON_LOAD}}">
<h4>{{SOURCE_WARNING_MESSAGE}}</h4>
- <pre class="prettyprint lang-cpp linenums">{{CODE}}</pre>
+ <pre class="prettyprint lang-cpp linenums" style="tab-size: {{TAB_STOPS}};">{{CODE}}</pre>
<hr />
<table width="100%">
<thead>
<tr>
<th align="center">
<small>Generated by</small>
<a href="{{OCC_PROJECT_LINK}}">
<strong>OpenCppCoverage (Version: {{OCC_VERSION}})</strong>
</a>
</th>
</tr>
</thead>
</table>
</body>
</html>
コマンドラインオプションでタブ幅を変更できるよう,以下のコードを追加した。なお,説明の都合上,後で説明する HTML 出力の文字化け対策用のオプションも一緒に追加した。
static const std::string ExcludedLineRegexOption;
static const std::string SubstitutePdbSourcePathOption;
+ static const std::string EncodingOption;
+ static const std::string EncodingShortOption;
+ static const std::string TabStopsOption;
+ static const std::string TabStopsShortOption;
explicit ProgramOptions(const std::vector<std::unique_ptr<IOptionParser>>&);
コマンドラインオプションの管理には Boost C++ ライブラリの Boost.program_options を使用しており,オプション名(短縮形を含む)および説明文を追記した。
for (const auto& optionParser : optionParsers)
optionParser->AddOption(options);
+ options.add_options()
+ ((ProgramOptions::EncodingOption + "," + ProgramOptions::EncodingShortOption).c_str(),
+ po::value<int>()->default_value(0),
+ "Encoding of imported source files.\n 0:AP_ACP, 932:SHIFT_JIS, 51932:EUC-JP\n 1200:UTF16LE, 1201:UTF16BE, 65001:UTF8")
+ ((ProgramOptions::TabStopsOption + "," + ProgramOptions::TabStopsShortOption).c_str(),
+ po::value<int>()->default_value(8),
+ "Tabstops of imported source files.");
}
/* 中略 */
const std::string ProgramOptions::SubstitutePdbSourcePathOption = "substitute_pdb_source_path";
const std::string ProgramOptions::StopOnAssertOption = "stop_on_assert";
+ const std::string ProgramOptions::EncodingOption = "encoding";
+ const std::string ProgramOptions::EncodingShortOption = "e";
+ const std::string ProgramOptions::TabStopsOption = "tabstops";
+ const std::string ProgramOptions::TabStopsShortOption = "t";
上記の ProgramOptions
はあくまで Boost.program_options 用のクラスであり,本プログラムのオプション管理クラス Options
にも追加した。Setter/Getter を介してアクセスする作法のようなので,それに合わせた。
void SetLogLevel(LogLevel);
LogLevel GetLogLevel() const;
+ void SetCodePage(int);
+ int GetCodePage() const;
+ void SetTabStops(int);
+ int GetTabStops() const;
void EnablePlugingMode();
bool IsPlugingModeEnabled() const;
/* 中略 */
LogLevel logLevel_;
+ int codePage_;
+ int tabStops_;
bool isPluginModeEnabled_;
//-------------------------------------------------------------------------
LogLevel Options::GetLogLevel() const
{
return logLevel_;
}
+ //-------------------------------------------------------------------------
+ void Options::SetCodePage(int codePage)
+ {
+ codePage_ = codePage;
+ }
+ //-------------------------------------------------------------------------
+ int Options::GetCodePage() const
+ {
+ return codePage_;
+ }
+ //-------------------------------------------------------------------------
+ void Options::SetTabStops(int tabStops)
+ {
+ tabStops_ = tabStops;
+ }
+ //-------------------------------------------------------------------------
+ int Options::GetTabStops() const
+ {
+ return tabStops_;
+ }
//-------------------------------------------------------------------------
void Options::EnablePlugingMode()
{
isPluginModeEnabled_ = true;
}
Boost.program_options によるコマンドラインオプションの解析結果を本プログラムのオプション管理クラス Options
にも反映させた。
auto modulePatterns =
GetPatterns(variablesMap,
ProgramOptions::SelectedModulesOption,
ProgramOptions::ExcludedModulesOption);
auto sourcePatterns =
GetPatterns(variablesMap,
ProgramOptions::SelectedSourcesOption,
ProgramOptions::ExcludedSourcesOption);
+ int codePage = variablesMap.GetValue<int>(ProgramOptions::EncodingOption);
+ int tabStops = variablesMap.GetValue<int>(ProgramOptions::TabStopsOption);
auto optionalStartInfo = GetStartInfo(variablesMap);
/* 中略 */
if (isVerbose)
options.SetLogLevel(LogLevel::Verbose);
if (isQuiet)
options.SetLogLevel(LogLevel::Quiet);
+ options.SetCodePage(codePage);
+ options.SetTabStops(tabStops);
if (variablesMap.IsOptionSelected(ProgramOptions::CoverChildrenOption))
options.EnableCoverChildrenMode();
本来,カバレッジの測定結果を HTML 形式で出力する HtmlExporter
クラスのみにオプション情報を伝えれば良いのだが,出力形式を選択できるようになっており(複数選択も可),いったん抽象クラス IExporter
を継承する形で各形式のクラスが作られている。このため,大元の抽象クラスにオプション情報を伝えるメソッド Config()
を追加した。
public:
IExporter() = default;
virtual std::filesystem::path GetDefaultPath(const std::wstring& prefix) const = 0;
virtual void Export(const Plugin::CoverageData&, const std::filesystem::path& output) = 0;
+ virtual void Config(const CppCoverage::Options& options) = 0;
レポート出力(エクスポート)する直前にオプション情報を伝えるメソッド Config()
を呼び出す。
const auto& exporter = exporters.at(exportType);
auto output =
(parameter)
? fs::path{*parameter}
: exporter->GetDefaultPath(defaultPathPrefix);
+ exporter->Config(options);
exporter->Export(coverage, output);
バイナリ形式出力クラス BinaryExporter
では Config()
の中身は空である。
public:
BinaryExporter() = default;
std::filesystem::path GetDefaultPath(const std::wstring& prefix) const override;
void Export(const Plugin::CoverageData&, const std::filesystem::path& output) override;
+ void Config(const CppCoverage::Options& options) override;
+ void BinaryExporter::Config(const CppCoverage::Options& options)
+ {
+ }
同様に Cobertura 形式出力クラス CoberturaExporter
も Config()
の中身はない。
public:
CoberturaExporter() ;
std::filesystem::path GetDefaultPath(const std::wstring& runningCommandFilename) const override;
void Export(const Plugin::CoverageData&, const std::filesystem::path& output) override;
void Export(const Plugin::CoverageData&, std::wostream&) const;
+ void Config(const CppCoverage::Options& options) override;
+ void CoberturaExporter::Config(const CppCoverage::Options& options)
+ {
+ }
HTML 形式出力クラス HtmlExporter
だけは Config()
を真面目に実装する必要があり,コードページとタブ幅の設定値を受け取り,さらに HtmlExporter
のメンバである fileCoverageExporter_
と exporter_
に引き渡す。この頃にはすっかり面倒になっていて Setter/Getter 使わず,public メンバにして直接書き込んだ。
public:
explicit HtmlExporter(const std::filesystem::path& templateFolder);
std::filesystem::path GetDefaultPath(const std::wstring& prefix) const override;
void Export(const Plugin::CoverageData&, const std::filesystem::path& outputFolder) override;
+ void Config(const CppCoverage::Options& options) override;
+ void HtmlExporter::Config(const cov::Options& options)
+ {
+ fileCoverageExporter_.codePage = options.GetCodePage();
+ exporter_.tabStops = options.GetTabStops();
+ }
で,受け取ったタブ幅の情報を HTML ファイルのテンプレートに書き込む。
static const std::string OCCVersion;
static const std::string ActualProjectLink;
+ static const std::string TabStops;
+ int tabStops;
const std::string TemplateHtmlExporter::OCCVersion = "OCC_VERSION";
const std::string TemplateHtmlExporter::ActualProjectLink = "https://github.com/OpenCppCoverage/OpenCppCoverage/releases";
+ const std::string TemplateHtmlExporter::TabStops = "TAB_STOPS";
/* 中略 */
void TemplateHtmlExporter::GenerateSourceTemplate(
const std::wstring& title,
const std::wstring& codeContent,
bool enableCodePrettify,
const fs::path& output) const
{
auto titleStr = ToString(title);
ctemplate::TemplateDictionary dictionary(titleStr);
std::string bodyLoad = BodyOnLoadFct;
std::string warning = "";
if (!enableCodePrettify)
{
bodyLoad = "";
warning = SyntaxHighlightingDisabledMsg;
}
dictionary.SetValue(OCCVersion, OPENCPPCOVERAGE_VERSION);
+ dictionary.SetValue(TabStops, std::to_string(tabStops));
WriteTemplate(dictionary, fileTemplatePath_, output);
}
とタブ幅の変更はひたすら面倒なだけで,C++ 入門者レベルの筆者でもなんとか実装することができた。
2.4. HTML レポートの日本語文字化け対策
ここでの注意事項(要するに苦労したこと)を以下に示す。
- BOM を判別するため,いったん
char
型の配列で読み込んだ後wchar_t
型の配列に変換し,さらにstd::wstring
に変換する。このとき文字列の末尾を示すヌル文字\0
が必要なので,最初にファイルを読み込んだ際に末尾にヌル文字\0
を2個追加する。Unicode/UnicodeBig Endian の場合に備えて2個とした。その後,さらにストリーム型std::wistringstream
に変換する。これは既存のコードの変更量を抑えるため。 - 当初 C++ らしく C++ 11 で導入された
std::codecvt
を使用するつもりだったが,C++ 17 から非推奨とのことなので,わざわざ Windows API のMultiByteToWideChar()
を使うハメになった。しかし,MultiByteToWideChar()
は Unicode もしくは Unicode BigEndian から変換できないことが発覚して少し慌てた。そもそも Unicode は変換不要であり,Unicode BigEndian もバイトオーダを入れ替えるだけで良い。しかし,MultiByteToWideChar()
で変換できると他のコードページの処理と統合できるのでシンプルになる。だが,どうやらできないようである。このためバイトオーダを入れ替える処理をわざわざ書くハメになった。CRT のswab()
を使う手もあったが,いまさら CRT を使うのって抵抗あるよね。 - BOM を判別するためファイルをバイナリ形式で読んでいる。このため Unicode に変換した後,キャリッジリターン
\r
を取り除いている。これをしないとstd::getline()
で行を読み出すとき,行の末尾にキャリッジリターン\r
が付いてしまう。この結果,HTML レポートにも\r
が付いてしまうのだ。
変更箇所を以下に示す。既存のコードをできるだけ流用して変更箇所を少なくするため,指定されたファイル名の内容を読み取り,文字コードの変換を行って std::wistreamstring
型で返す関数 GetStringStream()
を新規作成し,これを呼び出すようにした。
bool HtmlFileCoverageExporter::Export(
const Plugin::FileCoverage& fileCoverage,
std::wostream& output) const
{
auto filePath = fileCoverage.GetPath();
- std::wifstream ifs{filePath.string()};
- if (!ifs)
- THROW(L"Cannot open file : " + filePath.wstring());
+ auto ifs = GetStringStream(filePath.wstring(), codePage);
std::wstring line;
const Plugin::LineCoverage* previousLineCoverage = nullptr;
int styleChangesCount = 0;
int lineCount = 0;
for (int i = 1; std::getline(ifs, line); ++i)
{
auto lineCoverage = fileCoverage[i];
line = boost::spirit::classic::xml::encode(line);
if (AddLineCoverageColor(output, line, lineCoverage, previousLineCoverage))
++styleChangesCount;
++lineCount;
previousLineCoverage = lineCoverage;
}
AddEndStyleIfNeeded(output, previousLineCoverage);
output.flush();
return MustEnableCodePrettify(lineCount, styleChangesCount);
}
新規作成した GetStringStream()
の内容を以下に示す。
std::wistringstream GetStringStream(std::wstring filename, int codepage)
{
const int CodePageDefault = 0;
const int CodePageUnicode = 1200;
const int CodePageBigEndian = 1201;
const int CodePageUTF8 = 65001;
// ファイルをバイナリ形式で一括読み込む
std::ifstream ifs(filename, std::ios_base::in | std::ios_base::binary);
if (ifs.fail())
THROW(L"Cannot open file : " + filename);
int size = (int)ifs.seekg(0, std::ios::end).tellg();
ifs.seekg(0, std::ios::beg);
char* buf = new char[size + 2];
ifs.read(buf, size);
ifs.close();
// 終端文字を書き込む
buf[size++] = '\0';
buf[size++] = '\0';
// BOM チェック
int start, encode;
if (size >= 3 && buf[0] == -17 && buf[1] == -69 && buf[2] == -65) {
start = 3; encode = CodePageUTF8;
} else if (buf[0] == -1 && buf[1] == -2) {
start = 2; encode = CodePageUnicode;
} else if (buf[0] == -2 && buf[1] == -1) {
start = 2; encode = CodePageBigEndian;
} else {
start = 0; encode = codepage;
}
// 文字コード変換
int wsize;
wchar_t* wbuf;
if (encode == CodePageUnicode) {
wsize = (size - start) / 2;
wbuf = new wchar_t[wsize];
char* p = (char*)wbuf;
for (int i = start; i < size; i += 2) {
*p++ = buf[i];
*p++ = buf[i + 1];
}
} else if (encode == CodePageBigEndian) {
wsize = (size - start) / 2;
wbuf = new wchar_t[wsize];
char* p = (char*)wbuf;
for (int i = start; i < size; i += 2) {
*p++ = buf[i + 1];
*p++ = buf[i];
}
} else {
wsize = MultiByteToWideChar(encode, 0, &buf[start], -1, nullptr, 0);
if (wsize == 0)
THROW(L"Error in MultiByteToWideChar.");
wbuf = new wchar_t[wsize];
if (!MultiByteToWideChar(encode, 0, &buf[start], -1, wbuf, wsize))
THROW(L"Error in MultiByteToWideChar.");
}
// キャリッジリターン \r を削除する
wchar_t* wp = wbuf;
for (int i = 0; i < wsize; i++)
if (wbuf[i] != L'\r') *wp++ = wbuf[i];
std::wistringstream wis(wbuf);
delete[] wbuf;
delete[] buf;
return wis;
}
いやあ・・・オレのコードって C++ らしくないなあ・・・と思った。
3. 実行結果
OpenCppCoverage をコマンドプロンプトから引数なしで実行すると下記のようにヘルプメッセージが表示される。ご覧の通り,バージョン 0.9.9.1 となっており,最後の2つのオプションが筆者が独自に追加したものである。
You must specify a program to execute or use --input_coverage
OpenCppCoverage Version: 0.9.9.1
Usage: [options] -- program_to_run optional_arguments:
Command line only:
-v [ --verbose ] Verbose mode.
-q [ --quiet ] Quiet mode.
-h [ --help ] Show help message.
--config_file arg Filename of a configuration file.
Command line and configuration file:
--modules arg (=*) The pattern that module's paths should
match. Can have multiple occurrences.
--excluded_modules arg The pattern that module's paths should NOT
match. Can have multiple occurrences.
--sources arg (=*) The pattern that source's paths should
match. Can have multiple occurrences.
--excluded_sources arg The pattern that source's paths should NOT
match. Can have multiple occurrences.
--input_coverage arg A output path of export_type=binary. This
coverage data will be merged with the
current one. Can have multiple occurrences.
--working_dir arg The program working directory.
--cover_children Enable code coverage for children
processes.
--no_aggregate_by_file Do not aggregate coverage for same file
path.
--stop_on_assert Do not continue after DebugBreak() or
assert().
--unified_diff arg Format: <unifiedDiffPath>?<rootFolder>
<unifiedDiffPath> path of the unified diff
file. Git users can use git diff output.
<rootFolder> (optional) root folder for
paths in the diff file.
See documentation for limitations.
--continue_after_cpp_exception Try to continue after throwing a C++
exception.
--optimized_build Enable heuristics to support optimized
build. See documentation for restrictions.
--excluded_line_regex arg Exclude all lines match the regular
expression. Regular expression must match
the whole line.
--substitute_pdb_source_path arg Substitute the starting path defined in the
pdb by a local path.
Format: <pdbStartPath>?<localPath>. Can
have multiple occurrences.
--export_type arg (=html) Format: <exportType>[:<parameter>].
<exportType> can be:
html: output folder (optional)
cobertura: output file (optional)
binary: output file (optional)
TestCoverageSharedLib: output file
(optional)
Example: html:MyFolder\MySubFolder
This flag can have multiple occurrences.
-e [ --encoding ] arg (=0) Encoding of imported source files.
0:AP_ACP, 932:SHIFT_JIS, 51932:EUC-JP
1200:UTF16LE, 1201:UTF16BE, 65001:UTF8
-t [ --tabstops ] arg (=8) Tabstops of imported source files.
続いて実行中の様子。ソースコードのフォルダ名に日本語が使われているが,文字化けは起こらない。
[info] Start Program:
Path:"TESTPRIME.EXE"
Arguments:634715672521
Working directory: not set.
Modules: Selected: * Excluded:
Sources: Selected: D:\Work\素因数分解のテスト Excluded:
Log Level: Normal
Cover Children: 0
Aggregate by file: 1
Continue after C++ exception: 0
Optimized build support: 0
Export: html
Input coverage:
Unified diff:
Excluded line regular expressions:
Substitute pdb source paths:
[info] Module: D:\Work\素因数分解のテスト\TESTPRIME.exe is selected because it matches selected pattern: *
[info] Module: C:\Windows\System32\ntdll.dll is selected because it matches selected pattern: *
[info] Module: C:\Windows\SysWOW64\ntdll.dll is selected because it matches selected pattern: *
[info] Module: C:\Windows\System32\wow64.dll is selected because it matches selected pattern: *
[info] Module: C:\Windows\System32\wow64win.dll is selected because it matches selected pattern: *
[info] Module: C:\Windows\System32\wow64cpu.dll is selected because it matches selected pattern: *
[info] Module: C:\Windows\SysWOW64\kernel32.dll is selected because it matches selected pattern: *
[info] Module: C:\Windows\SysWOW64\KernelBase.dll is selected because it matches selected pattern: *
整数 634715672521 は合成数です。
7,11887,7627969 を素因数に持ちます。
[info] Module: C:\Windows\SysWOW64\kernel.appcore.dll is selected because it matches selected pattern: *
[info] Module: C:\Windows\SysWOW64\msvcrt.dll is selected because it matches selected pattern: *
[info] Module: C:\Windows\SysWOW64\rpcrt4.dll is selected because it matches selected pattern: *
[info] ----------------------------------------------------
[info] Coverage generated in Folder D:\Work\素因数分解のテスト\CoverageReport-2023-09-24-08h46m07s
[info] ----------------------------------------------------
[info] The code coverage report is not what you expect? See the FAQ https://github.com/OpenCppCoverage/OpenCppCoverage/wiki/FAQ.
[error] Your program stop with error code: 1
HTML レポートを以下に示す。バージョン名が変更されたことが明示されている。ちなみに下記のソースコードは Shift_JIS,ハードタブ4文字幅で記述されている。

4. おまけ
テストしているときに気づいたこと。
今回,テスト対象となるソースコードの文字コードとして Shift JIS,UTF-8 BOM 無し,UTF-8 BOM 付きなどを試した。このうち UTF-8 BOM 無しをコンパイルするとそのままではエラーになるが,コンパイルオプションで使用言語として UTF-8 を指定する,具体的には /source-charset:utf-8
を追加することでコンパイルエラーを回避できる。
で,あれば前回の記事では日本語環境下で文字化けする一部のソースコードをわざわざ Western(Windows 1252)で開いて UTF-8 で保存し直すという変更作業を施したが,コンパイルオプションで使用言語を指定できるのであれば,このような変更作業は不要である。そのことに気づき,前回の記事に修正を加えた。
なお本プログラムのライセンスは GPLv3 なのでソースコード一式を公開する。
https://drive.google.com/file/d/1_Do5eoiJEY-Kf9uk9sIgN4vR6VWcPS9Z/view?usp=sharing
2023-09-24 リリーズ版は Unicode/Unicode BigEndian 時のバイトオーダ交換処理に不具合があります。2023-09-25 リリース版以降ではこの不具合は修正されています。
5. 参考情報
- Boost勉強会#9つくばでの発表スライド - slideshare
- boost::program_optionsでは短いオプション名の指定に注意 - Qiita
- C++ Boostによるコマンドライン引数処理 - トライフィールズ
- C++でShift-JIS, UTF-8, UTF-16 BOM有無とエンディアンを考慮してファイルの読み込みをする - はてなブログ
- VC++でマルチバイト文字列とワイド文字列を簡単に変換するライブラリを作ってみた - Qiita
- C++(Visual Studio)でUTF-8を扱うための試行錯誤のメモ - Qiita
- utf8 のファイルの読み込み - Qiita
- C++のstreamでbom skipする方法をまた忘れないうちに書き留める - Qiita