C++ バージョン番号を比較する方法
はじめに
複数の値から形成されているバージョン番号などの値を比較する際、
一つずつ比較するのは、手間ですが、std::lexicographical_compare を使用すると、一行で書けます。
なお、std::tuple でより短くスマートに実装できるので、追記要参照。
実装
#include <algorithm> // lexicographical_compare #include <iostream> // cout #include <cstdint> // int32_t #include <array> // array // バージョン比較用共用体 union CompVersion { struct { int32_t minor; int32_t major; int32_t revision; int32_t build; }; std::array<int32_t, 4> data; }; int main() { // 適当なバージョンデータ CompVersion appA = { 1, 0, 100, 2 }; CompVersion appB = { 2, 0, 100, 3 }; // 比較(appA < appB) bool result = std::lexicographical_compare(appA.data.begin(), appA.data.end(), appB.data.begin(), appB.data.end()); // 結果 if (result) { std::cout << "appAの方が古い" << std::endl; } else { std::cout << "appBの方が古い" << std::endl; } system("pause"); return 0; }
あとがき
ショートコーディングとしては、かなり有用なので覚書。
元ネタは、下記から。
stackoverflow.com
追記
この記事を公開直後に、バンビちゃんさんに高速カウンターをいただきました。
std::tuple を使用すると、もっとスマートに実装できるとのこと。
tuple便利!
さっきのやつをささっと
— バンビちゃん@実際いません (@pink_bangbi) March 5, 2018
C++ で std::tuple を使ってバージョン番号を比較する - Secret Garden(Instrumental) https://t.co/nYr0xrfS8z
std::tuple を使えば1発で… https://t.co/HVV3qwq1pd https://t.co/kalcnzr1jd
— バンビちゃん@実際いません (@pink_bangbi) March 5, 2018
Windows C++ 実行ファイルからバージョン情報を取得する
メモ
バージョンリソースが含まれるファイルからバージョン情報を取得する方法。
Windows API の GetFileVersionInfo を使って取得する。
実装
#include <windows.h> #include <iostream> #include <vector> #include <string> #pragma comment(lib, "version.lib") // バージョン取得用の構造体 struct Version final { WORD minor; WORD major; WORD revision; WORD build; }; // バージョン取得関数 bool GetFileVersion(const std::string& path, Version& version) { DWORD infoHandle = 0; const auto size = GetFileVersionInfoSizeA(path.c_str(), &infoHandle); std::vector<char> buffer(size); if (buffer.empty()) { return false; } else if (GetFileVersionInfoA(path.c_str(), infoHandle, size, buffer.data()) == 0) { return false; } VS_FIXEDFILEINFO* pInfo; UINT verLen; if (VerQueryValueA(buffer.data(), "\\", reinterpret_cast<LPVOID*>(&pInfo), &verLen) == 0) { return false; } union { struct { DWORD fileVersionMS; DWORD fileVersionLS; }; Version info; } ver = { pInfo->dwFileVersionMS, pInfo->dwFileVersionLS }; version = ver.info; return true; } int main() { const char* filePath = R"(C:\Windows\System32\notepad.exe)"; // バージョン取得 Version ver = {}; if (GetFileVersion(filePath, ver)) { std::cout << "Major:" << ver.major << std::endl; std::cout << "Minor:" << ver.minor << std::endl; std::cout << "Build:" << ver.build << std::endl; std::cout << "Revision:" << ver.revision << std::endl; } else { std::cout << "エラー" << std::endl; } system("pause"); return 0; }
以上。
C++ 環境変数を含むパスをstd::stringに展開する方法
はじめに
パスに限らず、環境変数を含む文字列は、
WindowsAPIの ExpandEnvironmentStrings で展開できます。
std::string に展開する方法をメモ。
実装
#include <windows.h> #include <iostream> #include <string> // 環境変数を含むパスを展開する bool GetExpandEnvironmentPath(const std::string& path, std::string& dest) { dest.resize(ExpandEnvironmentStringsA(path.c_str(), nullptr, 0)); if (path.empty()) { return false; } ExpandEnvironmentStringsA(path.c_str(), &dest.front(), dest.size()); return true; } int main() { // 元のパス const char* basePath = "%tmp%\\data.bin"; // 展開 std::string resultPath; if (GetExpandEnvironmentPath(basePath, resultPath)) { std::cout << "展開前:" << basePath << std::endl; std::cout << "展開後:" << resultPath << std::endl; } else { std::cout << "失敗" << std::endl; } system("pause"); return 0; }
あとがき
今回は、stringに展開しましたが、
wstring なら ExpandEnvironmentStringsW で展開できます。
C++ で、コマンドの標準出力を取得する方法(CreateProcessなし)
はじめに
Windows でコマンドを実行する際、標準出力を取得する方法です。
コンソール画面が表示されても問題ない場合、
もしくは、CUIプログラムで使用できる方法です。
前提
system 関数だけでは、標準出力が簡単に取得できません。
Windows 環境では、APIの CreateProcess を使用する方法が良く使用されますが、popen を使用した方がシンプルに実装できます。
標準エラーの対応は、行っていませんが、
画面に表示したくない場合は、実行コマンド末尾に「 2>&1」を付けて標準出力にリダイレクトする等
適宜対応しましょう。
実装
#include <array> // array #include <cstdio> // _popen #include <iostream> // cout #include <memory> // shared_ptr #include <string> // string bool ExecCmd(const char* cmd, std::string& stdOut, int& exitCode) { std::shared_ptr<FILE> pipe(_popen(cmd, "r"), [&](FILE* p) {exitCode = _pclose(p); }); if (!pipe) { return false; } std::array<char, 256> buf; while (!feof(pipe.get())) { if (fgets(buf.data(), buf.size(), pipe.get()) != nullptr) { stdOut += buf.data(); } } return true; } int main(int argc, char** argv) { // 適当なコマンドを用意する const char* cmd = "dir"; std::string stdOut; int exitCode; if (ExecCmd(cmd, stdOut, exitCode)) { std::cout << stdOut << std::endl; } else { std::cout << "標準出力の取得に失敗しました。" << std::endl; } system("pause"); return 0; }
あとがき
Windows では、popen が使用できないと思い込んでいましたが、
_popen の名前で、使用できるので、実装して備忘録行き。
C++ コマンドライン引数をstd::vector<std::string>に展開する方法
ちょっとしたメモ
コマンドライン引数をstd::vector<std::string>に展開する方法です。
パース自体は、コンストラクタのみで実現できます。
実装
#include <iostream> #include <vector> #include <string> int main(int argc, char** argv) { using namespace std; vector<string> args(argv, argv + argc); for (const auto& arg : args) { cout << arg << endl; } return 0; }
あとがき
エントリーポイントを int main(std::vector<std::string> args)
みたいに書けたら楽ですが、そういう横着はできません。
元ネタは、下記を参照。
stackoverflow.com
C / C++ の共用体(union)を初歩的に使う
入門時にイマイチ使いどころがわからんかった共用体
プログラミングを初めて超初期のころ入門書で出会った
”union” ”共用体” のキーワード
自分の使っていた入門書では
構造体の直後位に解説されていて
いったいどこで使うの?
といった感じで放置していたが
最近使う機会があったので、一応覚書
(自分なりの)結論
キャストとかを駆使せずに”データにアクセスする手段”
記述法だけ似ている構造体とは別物と考えて良し
目的
構造体にオペレーターやキャストを使用せずに配列のようにアクセスする
確認用のプログラム
Vector3型風の構造体pos{ x y z } の各要素に”[ ]”でアクセスする。
本来ならposは配列でないので”[ ]”でアクセスできないが
共用体の機能でアクセスを可能にする
※動作確認は、VC2013
#include <iostream> #include <array> int main() { using namespace std; union testUnion{ struct { float x, y, z; } pos; array<float, 3> v; // もしくは、 float v[3]; }; testUnion uni; uni.v[0] = 100.0f; // uni.pos.x = 100.0; と同じ uni.v[1] = 200.1f; // uni.pos.y = 200.1; と同じ uni.v[2] = 300.2f; // uni.pos.z = 300.2; と同じ // つまりこう書ける for (int id = 0; id < 3; ++id){ uni.v[id] += 0.5f; } // 値表示 cout << uni.pos.x << endl; cout << uni.pos.y << endl; cout << uni.pos.z << endl; // 入力待ち rewind(stdin), getchar(); return 0; }
あとがき
記事にするほどのネタでもないけど入門時の自分が見たら
少しくらいは助かる気がしたので備忘録行き
ブログはじめました。
はじめまして、こんにちは。
技術的なことを中心に、
備忘録的な感じで更新して行きたいと思ってます。
がんばるぞ!