10.今後の記事内容について

今回から、具体的にコードの記述に入ります(やっと)。ここで、ちょっとこのブログの書き方のルールを設けたいと思います。今後、各記事の冒頭で、

この記事は、
コミット「Dx11、Dx12両環境設定とDirectXTexをプロジェクトに追加。でもまだまだただのWindowsアプリです。」
から、
コミット「Dx12版のウインドウ作成」
の間の作業です。
GitHubサイト

https://github.com/WiZFramework/BaseCross

のような記述で始まりたいと思います。
このブログは上記GitHubサイトと連動してますので、そのどの位置にあるコミットと関連してるのかがわかるようにしたいということです。
また、コミットとコミットの間の作業は、必ずしも1つの記事で完結できるとは限りません。複数回(ややもすると10回以上)同じコミット間の作業説明が入る場合もあります。
そんな場合は上記の記述は同じ内容がしばらく続くことになります。

さて、これから「Dx12版でのウインドウの作成」をすすめたいと思います。
しかしこの項ではまだ、ウインドウは作成しません。今後の記事中の説明方法や、説明が脱線する場合の脱線の仕方などを述べます。

いま作業しているのは、「シンプルバージョンDx12版」ですので、ソリューションは
「SimplSampleTest」ディレクトリ内の「BaseCrossDx12.sln」ということになります。ディレクトリやソリューション名など、複数出てきてかなりややこしくなりますので注意しましょう。(なるべく作業中のソリューションやファイルなどは記述するようにしますが・・・)。
また、このブログで「すべてのソースを表示する」ことは致しません。
これまでは記述も少なかったので、ある程度記述内容を紹介してましたが、今後はポイントだけ紹介する形になります、ですので、細かくソースをチェックする場合は、GitHubサイトを細かく調べてください。GitHubでは各コミット時の、追加変更の様子を表示できます。

上記ソリューションを開きます。
まず、stdafx.hに、ゲーム制作で使用するWindows関連、c言語、stl、Dx12関連のヘッダを並べます。

#pragma once

#include "targetver.h"

#define WIN32_LEAN_AND_MEAN // Windows ヘッダーから使用されていない部分を除外します。
// Windows ヘッダー ファイル:
#include <windows.h>

#include <wrl.h>
#include <initguid.h>

//中略

//C
#include <cassert>
#include <cwchar>

//中略

// STL
#include <vector>
#include <list>
#include <map>

//中略

using namespace std;

#include <d3d12.h>
#include <dxgi1_4.h>
#include <D3Dcompiler.h>

#include <d2d1_2.h>
#include <d2d1effects_1.h>
#include <dwrite_2.h>
#include <wincodec.h>

//中略

#include <DirectXTex.h>

#pragma comment( lib, "d3d12.lib" )
#pragma comment( lib, "dxgi.lib" )
#pragma comment( lib, "d2d1.lib" )
#pragma comment( lib, "d3dcompiler.lib" )
#pragma comment( lib, "dwrite.lib" )
#pragma comment( lib, "dxguid.lib" )
#pragma comment( lib, "winmm.lib" )
#pragma comment( lib, "comctl32.lib" )

//DirectXTx
#pragma comment( lib, "DirectXTex.lib" )

//中略


using namespace DirectX;

このように中略が多いですが、実際にstdafx.hを調べてみるとこのほかにも結構たくさんのヘッダがインクルードされているのがわかると思います。
stdafx.hについて一言。stdafx.hは「プリコンパイル済みヘッダ」を作るヘッダファイルです。なので僕はここで「STLやC言語ヘッダなど標準的なものはある程度たくさん」インクルードしてもいいと考えてます。
C/C++の参考書などでは、「ヘッダは使用するときだけインクルードする」となっているものもありますが、たとえば「std::vector」を常に「使用するときだけ」インクルードするのも結構面倒です。あらゆる局面で出てきますからね。
また、

using namespace std;

のように、STLを「std::」で始まらなくても使用できるようになってます。これも異論があるところです。これもコードが助長的になるのを防いでいます。STLのクラスや関数はコード中たくさん出てきますので。
このほかには、

//DirectXTx
#pragma comment( lib, "DirectXTex.lib" )

のような記述ですが、これはリンカに、該当ファイルとリンクするよう指示を与える記述です。プロパティシートのリンクの設定でもできるのですが、例えば、ゲーム制作側が、外部のDirectXTex以外のlibとリンクしたいと思ったとき、プロパティシートをいちいち設定するのも面倒です。コンパイラの設定はしっかり設定設定すべきと思いますが、リンカは上記構文で記述でもいいかな、と思っています。

using namespaceの話が出てきたので、ついでに、この項には関係ないですが、ちょっと脱線します。
僕は、using namespaceは限定的には使用してもかまわないと思ってます。「std」や「DirectX」です。
そのかわり、よく参考書に出てくる、ローカルな「typedef」はできるだけ使わないほうがいいのかなと思っています。以下のようなものです。

#include "stdafx.h"
#include <windows.h>
#include <iostream>
#include <vector>
#include <memory>

using namespace std;

//単純な構造体
struct Data{
    int Number;
};

//2次元配列をtypedef
typedef vector<vector<Data>> MyDataList;

int _tmain(int argc, _TCHAR* argv[])
{
    MyDataList MyData =
    { 
        { { 1 }, { 2 }, { 3 } },
        { { 3 }, { 5 }, { 7 } },
        { { 2 }, { 4 }, { 6 } },
    };

    for (auto& v : MyData){
        for (auto& v2 : v){
            cout << v2.Number << ' ';
        }
        cout <<  endl;
    }
    return 0;
}

こういった単純なコンソールアプリのサンプルは、今後記事中いたるところに出てくると思います。
これは、「コンソールアプリケーション」として別にソリューションプロジェクトを作成して実行してみるとよいでしょう。(このサンプルにもstdafx.hはあるので気を付けてください。その部分は記述しません)

ここで、「MyDataList」はtypedefされた型です。このtypedef句がないと、MyDataがどんなオブジェクトなのか直感的には誰にもわかりません。この例では、typedefは同じファイル内なのでわかりますが、ファイルをまたいで関数受け渡しなどに使う場合は、特にそうです。以下のように記述したほうがいいと思います。

int _tmain(int argc, _TCHAR* argv[])
{
    vector<vector<Data>> MyData =
    { 
        { { 1 }, { 2 }, { 3 } },
        { { 3 }, { 5 }, { 7 } },
        { { 2 }, { 4 }, { 6 } },
    };

    for (auto& v : MyData){
        for (auto& v2 : v){
            cout << v2.Number << ' ';
        }
        cout <<  endl;
    }
    return 0;
}

では、shared_ptrの3次元配列なんかの場合もそうかと言われれば、「うーむ」となってしまいますね。
以下のようなものです。

#include "stdafx.h"
#include <windows.h>
#include <iostream>
#include <vector>
#include <memory>

using namespace std;

//単純な構造体
struct Data{
    int Number;
};

//shared_ptr作成ヘルパー
shared_ptr<Data> MakeDataPtr(int Num){
    auto Ptr = make_shared<Data>();
    Ptr->Number = Num;
    return Ptr;
}

//出力用関数
void Display(const vector<vector<vector<shared_ptr<Data>>>>& DataVec){
    for (auto& v : DataVec){
        for (auto& v2 : v){
            for (auto& v3 : v2){
                cout << v3->Number << ' ';
            }
            cout << endl;
        }
        cout << endl;
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    vector<vector<vector<shared_ptr<Data>>>> MyDataPtrVec =
    {
        {
            { { MakeDataPtr(1) }, { MakeDataPtr(2) }, { MakeDataPtr(3) } },
            { { MakeDataPtr(3) }, { MakeDataPtr(5) }, { MakeDataPtr(7) } },
            { { MakeDataPtr(2) }, { MakeDataPtr(4) }, { MakeDataPtr(6) } },
        },
        {
            { { MakeDataPtr(10) }, { MakeDataPtr(20) }, { MakeDataPtr(30) } },
            { { MakeDataPtr(30) }, { MakeDataPtr(50) }, { MakeDataPtr(70) } },
            { { MakeDataPtr(20) }, { MakeDataPtr(40) }, { MakeDataPtr(60) } },
        },
        {
            { { MakeDataPtr(-1) }, { MakeDataPtr(-2) }, { MakeDataPtr(-3) } },
            { { MakeDataPtr(-3) }, { MakeDataPtr(-5) }, { MakeDataPtr(-7) } },
            { { MakeDataPtr(-2) }, { MakeDataPtr(-4) }, { MakeDataPtr(-6) } },
        },
    };
    //出力用関数を呼ぶ
    Display(MyDataPtrVec);

    return 0;
}

出力は以下のようになります。

1 2 3
3 5 7
2 4 6

10 20 30
30 50 70
20 40 60

-1 -2 -3
-3 -5 -7
-2 -4 -6

これでもtypedefは使わないほうがいいかと思います。
複雑な型になればなるほど、それを関数受け渡しなどに使用する場合、注意を払う必要があります。この場合、Display()関数に渡される引数がtypedefされる形になると思いますが、Display()関数が別ファイルにあった場合、そのファイルからのtypedefが見えなければならないので、共通で使うヘッダに書く必要があります。
それにC++11は「auto」や「拡張for文」が使えるので、複雑な型でも、何回も記述する必要がないと思います。(この機能が付いたのが大きいかな。この機能のおかげで、typedefは、「記述のめんどうくさいだけの理由」で書く必要がなくなったといえます)。

これはあくまで僕の考えなので、これまでの膨大な量のソース資産を否定するものではありません。C/C++はいろんな記述方法があります。
コンパイラが通り、思ったように動くのであればそれでプログラムとしては成立してます。ただ、今後記述の見直しや、あるいは新規に記述する場合、上記の意見も参考にして記述したらいかがでしょう?ということです。

どうしても使用したい局面(関数のポインタなど)では、C++11では、typedefは使わずに、usingを使ったほうがいいでしょう。usingはテンプレートにも使えます(usingのこれらの使用法は後ほどどこかで書きます)

C++は進化する言語です。それも多言語に比べて、多少強引かと思われるほど、進化し続けてます。テンプレートや例外が実装される前のC++と比べたら、現在(C++11以降)のC++の記述はまるで別世界です。それでも「同じ言語」なのです。これがC++が「難解だ」と言われるゆえんなのではないかと思います。僕はC++は「難解」なのではなく「多機能すぎて収集がつかない(もしくはつけるのが大変)」なのではないかと思うのです。
だからこそ「記述スタイル」が重要だと思っています。そのプロジェクトに合った「記述スタイル」を決めて、チームや組織で共有する。あるいは今後のC++進化の都度、記述スタイルを見直して、それに合わせて修正をかけていく、そんなC++との付き合い方がベターではないかと考えています。

今回は、今後の記述方法を含め、別の話題も書いてみました。
開発ブログの紹介とはいえ、コードを記述する際は、C++11の文法やコンパイラやリンカの仕組みなどを総合的に考え記述しているつもりなので、このような直接関係ないサンプルなども登場してきます。
まあ、ブログなので(マニュアルではないので)、そんなのもありかなと思っています。
それでは、次回こそ「ウインドウの作成」を行います。

 

カテゴリー

ピックアップ記事

  1. 2016092201
    今回は前回のサンプルを少し機能を追加しまして、いろんなオブジェクトを追加しています。FullTuto…
  2. 2016092001
    前回更新から時間がたってしまいましたが、今回はフルバージョンチュートリアル003をアップしました。内…
  3. eyecatch
    前回更新から時間がたってしまいましたが、今回はフルバージョンチュートリアル002で懸案となっていまし…
PAGE TOP