イネマルのプログラミング備忘録

だいたい付箋。一口サイズのメモ帳

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

string wstring 相互変換 (Windows用)

stringとwstringの相互変換

stlを使用していると、文字列にstd::stringを使用する事が多いですが、
std::wstringに変換したいタイミングがあるかもしれません。
そんな時の対応方法の一例です。

注意

Windows環境で使える変換手法を覚書きします。
今回は
「MultiByteToWideChar/WideCharToMultiByte」
を使用した例です。
マルチプラットフォームな方法では、

  • C++11の「std::wsting_convert」
  • C/C++の「mbsrtowcs/std::mbsrtowcs」

があるらしいです。

確認用のプログラム

stringとwstringの文字列を
それぞれwstringとstringに変換してコンソールに表示してみる。

※動作確認は、VC2013で行いました

StringConverter.hpp

// StringConverter.hpp
#pragma once

#include <string>
#include <vector>
#include <locale>
#include <winstring.h>

namespace StringConverter
{
	// コードページ定義
	enum CodePageID : unsigned int
	{
		ANSI	= CP_ACP,	// ANSI
		OEM		= CP_OEMCP,	// OEM(依存)
		MAC		= CP_MACCP,	// MAC
		UTF7	= CP_UTF7,	// UTF-7
		UTF8	= CP_UTF8	// UTF-8
	};

	// ロケール設定
	static void SetGlobalLocale(const std::string localeName = "")
	{
		std::locale::global(std::locale(localeName));
		setlocale(LC_ALL, std::locale().c_str());
	}

	// string から wstring 変換
	static std::wstring StringToWString(const std::string& refSrc, unsigned int codePage = CodePageID::ANSI)
	{
		std::vector<wchar_t> buffer(MultiByteToWideChar(codePage, 0, refSrc.c_str(), -1, nullptr, 0));
		MultiByteToWideChar(codePage, 0, refSrc.c_str(), -1, &buffer.front(), buffer.size());
		return std::wstring(buffer.begin(), buffer.end());
	}

	// wstring から string 変換
	static std::string WStringToString(const std::wstring& refSrc, unsigned int codePage = CodePageID::OEM)
	{
		std::vector<char> buffer(WideCharToMultiByte(codePage, 0, refSrc.c_str(), -1, nullptr, 0, nullptr, nullptr));
		WideCharToMultiByte(codePage, 0, refSrc.c_str(), -1, &buffer.front(), buffer.size(), nullptr, nullptr);
		return std::string(buffer.begin(), buffer.end());
	}
}

main.cpp

// main.cpp
#include <iostream>
#include <string>
#include "StringConverter.hpp"

int main()
{
	using namespace std;

	// 適当に文字用意
	std::string msgA = "ABCアイウエオ@日本語";
	std::wstring msgW = L"ABCアイウエオ@日本語";

	// ロケール設定
	StringConverter::SetGlobalLocale();

	// 表示
	cout << StringConverter::WStringToString(msgW) << endl;
	wcout << StringConverter::StringToWString(msgA) << endl;

	// 入力待ち
	rewind(stdin), getchar();
	return 0;
}

あとがき

関数の引数が対応していなくてやむなく変換ってパターンもあるけど
本来は、関数側が対応するべきだと思う。
Win32APIは大体、
(関数名)Aと(関数名)Wを用意しているから問題は起こりにくいと思うけど、
外部ライブラリが何故かwchar_tしか対応してない状況があって変換方法を備忘録行き。

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;	// 純粋なCなら 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;
}

あとがき

記事にするほどのネタでもないけど入門時の自分が見たら
少しくらいは助かる気がしたので備忘録行き

Visual Studio 2010 C++ リファクタリング

f:id:inemaru:20150912215136p:plain
【目的】
今更だけど Visual Studio 2010 C++リファクタリング(自動リネーム)を使う

【達成条件】
・無料でやる

【注意】
・正攻法ではありません(故に↓)
・すべて個人の責任で行ってください(こちらは一切の責任を持ちません)
・VS2013(以降)は、公式で配布があるので無用の産物。

【環境】
・動作環境
  Windows 8.1 Pro
・開発環境
  Microsoft Visual Studio 2010 Professional

【方法】
1: Refactor!C++をダウンロード
2: RefactorCPP-11.2.7.exeをインストール
3: インストール完了後にシャットダウン(※この時、絶対にVSを起動しないこと)
4: CodeRushXpressをダウンロード
5: インストール完了後にシャットダウン(※この時、絶対にVSを起動しないこと)
6: PCを起動したらVSを起動し、エラーが出なければ成功!


【ちなみに】
・途中の工程で、もしVSを起動すると・・・
初回起動のみ正常に動作(してるように見える)、
2回目以降の起動で、ものすごい勢いでメモリを消費して
下手するとPCごと落ちます。

・ちなみに失敗したときのエラーはこんなの
devenv.exe Assert Failure
Expression: [mscorlib recursive resource lookup bug]
Description: Infinite recursion during resource lookup within mscorlib. This may be a bug in mscorlib, or potentially in certain extensiblity
points such as assembly resolve events or CultureInfo names. Resource name: Word_At

エラーの正体・・・拡張機能で使用されている実行ファイルのバグ

【あとがき】
約1年前に、身内で話題になり、結構頑張って調べた後
一過性の話題のまま忘れ去られようとされていた情報
データの整理中に当時自分が作った、インストールのメモが出てきたので備忘録行き決定
それが、今更VS2010のネタを書くに至った理由