WindowsでMinGWを使ったC++コンパイル環境構築手順

WindowsPCにC++コンパイル環境を作ったので手順をまとめておく。

WindowsC++コンパイル環境というとVisualStudioが有名だが、
今回は別のVisualStudioではなくMinGWを使いg++のコンパイラをインストールし環境をつくる。

手順①:ダウンロード

まずは以下のページにアクセスする。
osdn.net

ページにアクセスしたら以下のリンク先からmingw-get-setup.exeをダウンロードすればOK。
f:id:powerbombkun:20220412164047p:plain

手順②:インストール

ダウンロードしたインストーラを実行すると以下の画面になるので、
Installボタンを押してインストール作業開始する。
f:id:powerbombkun:20220412164323p:plain

インストールフォルダ指定する画面が表示されるので、
デフォルト(C:/MinGW)から変えたいなら変更しContinueボタンを押して手順進める。
f:id:powerbombkun:20220412164357p:plain

Continueボタンを押して進めていく。
f:id:powerbombkun:20220412164437p:plain

進めていくとこのPackage選択画面が表示されるので、
以下のPackageにチェックを入れて Installation > Apply Changes を選択。
<Package>

  • mingw-developer-toolkit-bin
  • mingw32-base-in
  • mingw32-gcc-g++-bin
  • msys-base-in

f:id:powerbombkun:20220412165630p:plain

手順③:環境パス設定

インストールしただけだと環境パスが通っていないので、
パスを通してあげれば設定は完了。

f:id:powerbombkun:20220412170126p:plain

一応コマンドラインを起動し、g++がちゃんとインストールされていることを確認する。
f:id:powerbombkun:20220412170801p:plain

テンプレートの実装はヘッダに書かないといけない

ここ10年ほどC++から離れていたので最近学びなおしをしている。

C++にはテンプレートという機能があるが、
これを学びなおすべくテスト用のクラスをテンプレートを使い実装してみた。

// Hoge.h
template <class T>
class Hoge
{
private:  
    T m_hoge;

public:
    T GetHoge();
    void SetHoge(T hoge);    
};
// Hoge.cpp
#include "./Hoge.h"

template< class T> 
T Hoge<T>::GetHoge()
{
     return m_hoge; 
}

template< class T> 
void Hoge<T>::SetHoge(T hoge)
{
     m_hoge = hoge;
}
// main.cpp
int main()
{
    Hoge<int> a;
    a.SetHoge(1);
    
    return 0;
}

実装が終わったので、さっそくメイクしてみたところ以下のようなエラーが出た。
どうやらコンパイルまでは通っているが、リンク時にメソッドの実態定義が見つからずにエラーになっているらしい。

c:/mingw/bin/../lib/gcc/mingw32/9.2.0/../../../../mingw32/bin/ld.exe: main.o:main.cpp:(.text+0x23): undefined reference to `Hoge<int>::SetHoge(int)'
c:/mingw/bin/../lib/gcc/mingw32/9.2.0/../../../../mingw32/bin/ld.exe: main.o:main.cpp:(.text+0x30): undefined reference to `Hoge<int>::GetHoge()'
collect2.exe: error: ld returned 1 exit status
Makefile:2: recipe for target 'all' failed
mingw32-make: *** [all] Error 1

原因について調べてみるとこちらのブログで非常に参考になる内容が紹介されていた。
pknight.hatenablog.com

どうやらテンプレートを使用する場合、
実装については基本的にヘッダに書くのが流儀らしい。

ということでヘッダに定義内容を移したらすんなりリンクまで通った。

// Hoge.h
template <class T>
class Hoge
{
private:  
    T m_hoge;

public:
    T GetHoge() { return m_hoge; } // 定義もここに書く。
    void SetHoge(T hoge) { m_hoge = hoge; } // 定義もここに書く。  
};

VisualStudioCodeで定義先へ移動&元の位置へカンタンに戻る方法

コードを書いていると関数の定義先へ移動し内容を確認した後、
元の位置(関数の呼び出し元)へ戻るということをよくやる。

VisualStudioCodeにもこのショートカットキーあるはずだと思い調べたら見つかったので、
備忘録として残す。

定義元へ移動:F12
元の位置へ戻る:Alt + ←

asと()キャストの正しい使い分け方法

asキャストとは

C#ではCやC++にはなかったas演算子を使ってキャストをする機能が追加された。
使い方としてはキャストしたい対象のオブジェクトの後ろにasを付けるだけ。

SampleClass temp = object as SampleClass

キャスト後のふるまいは()でもasでも変わらない。

asと()キャストの違い

書き方の違いだけならどちら使ってもよいのでは?と思うかもしれないが、仕様も違うので正しく理解しておく必要がある

違い①:キャスト失敗時のふるまい

()キャストで失敗した場合、InvalidCastExceptionの例外が発生する。
そのためキャスト失敗する可能性があるのであればcatch処理を実装しておく必要がある。

try
{
SampleClass temp = object as SampleClass
}
catch (InvalidCastException)
{
  // エラー処理
}

ではasキャストの場合はどうかというと、例外は発生しない。
失敗した場合キャスト後のオブジェクトがnullになる仕様なのでnullチェックをすれば良い。

SampleClass temp = object as SampleClass
if (temp == null)
{
  // エラー処理
}

違い②:キャストできる対象のオブジェクト

もう1つの違いとしてasキャストは参照型のオブジェクトに対してしか適用できない。
そのためintやlong等の値型のオブジェクトではasは使えないので、キャスト時は()を使うしかない。

正しい使い分け方法

キャスト仕様の違いをふまえた上でではどのように使い分けたらよいのかというと、以下のように使い分けるのが良いと思う。

  • 値型の場合:()キャストを使う
  • 参照型の場合:asキャストを使う

理由としてはコードがシンプルになるからだ。
キャストというのは頻繁に使う処理なので、参照型に()キャストをしてすべてのキャストしている箇所に例外補足処理を入れるのはかなり面倒。しかも例外補足処理というのはよく忘れがちになることが多い。
それならasキャストでnullチェックをするだけの方がシンプルでラクだからだ。

SMB2.0が原因で共有フォルダアクセスが遅い場合の対処法

Windows10ではSMB2.0というファイル転送プロトコルが使われている。
このSMB2.0だがネットワークの負荷を軽減させるためにクライアント側にキャッシュを持たせる機能が入っており、共有フォルダへのアクセスが遅くなる場合がある。

デフォルトは10s毎にサーバーと同期を取りキャッシュ更新するので、最大10s程度は共有フォルダへのアクセスが遅くなる場合があるらしい。

<対処法>
レジストリエディタを開き以下の設定をする。
キー : HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters
タイプ : DWORD

FileInfoCacheLifetime 0
FileNotFoundCacheLifetime 0
DirectoryCacheLifetime 0


詳細は以下のサイト
support.microsoft.com

Roundメソッドを使った正しい四捨五入の実装方法

C#の標準クラスではMath.Roundという数値の四捨五入に使えるメソッドがある。
docs.microsoft.com

このMath.Roundメソッドだがそのまま何も考えずに四捨五入で使うと問題が起きる。
例えば以下のようなケース。

Console.WriteLine(Math.Round(1.5));
Console.WriteLine(Math.Round(2.5));
Console.WriteLine(Math.Round(3.5));
Console.WriteLine(Math.Round(4.5));

Microsoftのページをよく読んでいないとすべて繰り上げされると思いがちだが、実際は以下のような結果になる。

2
2
4
4

2.5は四捨五入したら3、4.5は5で出力されてほしいが意図通りならない。
なぜこのような結果になるかというと、Math.Roundに第2引数を指定しない場合のデフォルトの動作は
「10 進数の値は最も近い整数値に丸められ、中間値は最も近い偶数値に丸められる」という仕様だからだ。
小学校の時に習った単純な四捨五入とは違う動作がデフォルトということだ。


これを正しく実装するには第2引数にMidpointRounding.AwayFromZeroを追加しメソッドを使う必要がある。

Console.WriteLine(Math.Round(1.5, MidpointRounding.AwayFromZero));
Console.WriteLine(Math.Round(2.5, MidpointRounding.AwayFromZero));
Console.WriteLine(Math.Round(3.5, MidpointRounding.AwayFromZero));
Console.WriteLine(Math.Round(4.5, MidpointRounding.AwayFromZero));

これで実装すると以下のように全て繰り上げて処理される。

2
3
4
5

Enumの要素数を1行で取得する方法

実装時にEnumの要素数を使って何かをしたいケースがよくある。
Enum自体は配列等ではないので、そのままでは要素数にあたるメソッド等は持っていない。
でも1行でカンタンに要素数取得する方法がある。

例えばこんなenumの要素数を取得する場合

public enum FruitsEnum
{
    Apple,
    Banana,
    Strawberry
}

これでカンタンに取得できる。

int length = Enum.GetValues(typeof(FruitsEnum)).Length;

DataGridViewTextBoxにテキストと画像を表示させる方法

DataGridViewTextBoxはテキストだけ表示するのが普通だが、セル内にテキストに加えて画像も一緒に描画したりすることができる。
DataGridViewのCellPaintingイベントで自力描画させれば割とカンタン。
セルを選択時は晴れ、非選択時は曇り画像を表示させるようにしてみた。

まずは画像ファイルをリソースに登録。

f:id:powerbombkun:20220106222605p:plain
表示画像

※使用したのは縦横64pixelの画像ファイル

次にCellPaintingのイベントを以下のように実装。

private void _dataGridView_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
    if ((0 <= e.RowIndex) && (0 <= e.ColumnIndex))
    {
        Image image;
        if ((e.State & DataGridViewElementStates.Selected) == DataGridViewElementStates.Selected)
        {
            image = Resources.fine;
        }
        else
        {
            image = Resources.rain;
        }

        Rectangle drawBounds = new Rectangle(e.CellBounds.Right - e.CellBounds.Height,
                                                e.CellBounds.Top,
                                                e.CellBounds.Height,
                                                e.CellBounds.Height);
        e.Paint(e.CellBounds, DataGridViewPaintParts.All);
        e.Graphics.DrawImage(image, drawBounds);

        //描画完了通知
        e.Handled = true;
    }
}

これで実行してみる。
選択したセルは晴れ画像が表示されて、非選択は曇り画像がセルの右側に表示される。

f:id:powerbombkun:20220106222934p:plain
セル選択時の表示

DataGridViewComboBoxCellを1クリックでドロップダウンさせる方法

DataGridViewの実装でDataGridViewComboBoxCellを使うことがたまにある。
このコンボボックスは普通のSystem.Windows.FormsのComboBoxと仕様が違い、デフォルトだと1クリックではリストがドロップダウンしてくれないのだ。

f:id:powerbombkun:20220106205948p:plain
ドロップダウンしない

2回クリックして初めてドロップダウンするのがデフォルトの仕様らしい。
これだとユーザーはとってもめんどくさいので、これを1クリックでドロップダウンするよう実装する。

まずはDataGridViewのイベントに以下を追加。

_dataGridView.CellEnter += _dataGridView_CellEnter;

その後にイベント処理で以下のように実装する。

private void _dataGridView_CellEnter(object sender, DataGridViewCellEventArgs e)
{
    if (_dataGridView[e.ColumnIndex, e.RowIndex].GetType().Equals(typeof(DataGridViewComboBoxCell)))
    {
        _dataGridView.BeginEdit(false);
        ((DataGridViewComboBoxEditingControl)_dataGridView.EditingControl).DroppedDown = true;
    }
}

これで1クリックでコンボボックスのリストがドロップダウンしてくれるようになった。

数字以外が入力で色が変わるTextBoxの実装方法

テキストボックスに指定した文字(数字等)が入力されたら背景色を変えて、ユーザーにルール違反していることを通知したいことがある。
テキストボックスのイベントに少し処理追加すればカンタンに実装することができる。

public partial class Form1 : Form
{
    private readonly Color _defaultBackColor;

    public Form1()
    {
        InitializeComponent();

        _defaultBackColor = textBox1.BackColor;
    }

    private void textBox1_TextChanged(object sender, EventArgs e)
    {
        bool error = false;
        foreach(char c in textBox1.Text)
        {
            if (!char.IsDigit(c))
            {
                error = true;
                break;
            }
        }
        if (error)
        {
            textBox1.BackColor = Color.Red;
        }
        else
        {
            textBox1.BackColor = _defaultBackColor;
        }
    }
}

こんな感じで実装すると数字以外が入力された時に背景色が赤くなってお知らせできる。

f:id:powerbombkun:20211227232706p:plain
数字無し
f:id:powerbombkun:20211227232727p:plain
数字有り

数字以外そもそも入力されたくないならNumericUpDownコントロールを使うという手もあるが、数字だけじゃなく小文字英数字だけ許可という場合でも少し処理を変えるかけでカンタンに実装できる。