読者です 読者をやめる 読者になる 読者になる

ムラサメ研究ブログ

主にゲームやプログラミングのログ

右辺値参照~完全転送まで100%理解するぞ! part2 VisualStudioの罠

C++ rvalue ref

はじめに

おまけ。 右辺値参照を理解するうえで大きな妨げになった VisualStudioでの挙動(バグ?? 詳細希望)

左辺値は暗黙で右辺値参照にキャスト出来ない

バグ? の前に基礎から 左辺値(参照)は 左辺値にのみ 右辺値(参照)は 右辺値にのみ 暗黙キャストがゆるされる

#include<iostream>

void foo1(int& i) {}
void foo2(int&& i) {}


auto main() -> int{

    int i;
    int &i1(i);                        // lvalue -> lvalue ref         well-formed
    int &i2(i1);                   // lvalue ref -> lvalue ref     well-formed
// int &i3(1);                     // rvalue -> lvalue ref          ill-formed

    int &&ii(1);                  // rvalue -> rvalue ref         well-formed
// int &&ii2(ii);                  // lvalue ref -> rvalue ref      ill-formed    iiは右辺値を代入しているがそれ自体は左辺値(アドレスが存在する)
    int &&ii3(std::move(ii));      // rvalue ref -> rvalue ref     well-formed


    foo1(i);                        // lvalue -> lvalue ref         well-formed
    foo1(i1);                       // lvalue ref -> lvalue ref     well-formed
// foo1(1);                        // rvalue -> lvalue ref          ill-formed

    foo2(1);                       // rvalue -> rvalue ref         well-formed
// foo2(ii);                       // lvalue ref -> rvalue ref      ill-formed    iiは右辺値を代入しているがそれ自体は左辺値(アドレスが存在する)
    foo2(std::move(ii));            // rvalue ref -> rvalue ref     well-formed
}

上記の通り、右辺値参照と左辺値参照は 暗黙で変換できないので std::moveでキャストする必要がある

VCの不具合??

stringやvector等で試すと不思議なことがおこった

#include<iostream>

void Str(std::string& s) {
}

auto main() -> int{
  Str( std::string("right value"));  // rvalue -> lvalue ref   ill-formだがVisualStudioでは通る・・
}

clanやgccで試したが 当然エラーになるし VisualStudioでも int等で試すとエラーになるが stringやvector等のコンテナだと いずれも上記のコードが通るので 移植性を考える場合は 上記のコードを書かないようにすべきと思われる

右辺値参照~完全転送まで100%理解するぞ! part1 概要

C++ rvalue ref

TL;DR

はじめに、私が右辺値参照を調査したのは、何番煎じかわからない 解説をしたいわけではなく VisualStudioでの動作の違い、universal referenceの特殊化したときの動き、複数の引数をuniversal referenceしたとき デフォルト引数・・・ など、応用したときに色々と悩んだから それを自分の中でまとめようとおもう

とはいっても説明

右辺値参照は、実はそんなにも難しくないと思う 難しいと思う人は、名前に騙されて勘違いされているだけで たった3つの事柄を知れば 理解が一気に深まるとおもう 自分はそうだった。

とりあえず 最初にその一言をいうと

右辺値は名前が悪い。 一時オブジェクト あるいはアドレスを持たないものと考えると良い

std::moveは名前が悪い。移動ではなく右辺値へのキャスト演算子である

関数の仮引数で右辺値参照(hoge&&) で受けても左辺値(hoge&)であるので右辺値にするには std::move()でキャストする

私の場合はこの3つで一気に80%まで理解できた

何番煎じかわからないが 右辺値参照について調査する

右辺値とは

一時オブジェクトだが 細かいのは他の人のブログみてもらいたい

std::string s("hoge");   // &sとアドレスが取れる=実態があるので左辺値
foo( s );               // 左辺値を関数に渡している

str::string("hoge");    // アドレス取れない一時オブジェクト
foo( str::string("hoge") ) // 右辺値を関数に渡している

右辺値の考えが重要になったのは、例えば上記の場合、右辺値を関数に渡すときにオブジェクトのコピーが走る 一時オブジェクトは コピーせず破壊しても良いので無駄である ので、渡すときにコピーを行わず所有権を渡す(ポインタをすげかえる)事が出来れば パフォーマンスがあがる

同様に左辺値も、所有権を渡したいシーンもあるだろう。そんな時は std::move()を使い 右辺値にキャストし所有権を移動出来るようにする

ムーブコンストラクタ

class Hoge{
public:
  Hoge(Hoge &obj){} // コピーコンストラクタ
  Hoge(Hoge &&obj){} // ムーブコンストラクタ
};

書式は上のように hoge&& と、&を2個重ねる

実際に実装しようとするとなかなかに面倒くさい。 移動元のdestructorも呼ばれるためデータが消されないように注意する 自分のムーブを禁止しなければならない 移動代入演算子 operator=(Hoge&&)も実装してね noexpectに気をつけろ ・・・

Hoge(Hoge&&) = default; と、デフォルトのムーブコンストラクタも作成可能だが 条件次第では作れない事もあるので自分で実装する方法を知らねばならない

Microsoftにとても良いページがあったので これを見て設定してほしい https://msdn.microsoft.com/ja-jp/library/dd293665.aspx

std::move()

最初に std::move()は 右辺値へのキャストのみ(移動はしない)と書いた 右辺値参照にキャストするだけだと それが上記のムーブコンストラクタと関係する

#include<iostream>

using namespace std;

void foo( string& s) {
  cout << "lvalue" << endl;
};
void foo( string&& s) {
  cout << "rvalue" << endl;
};


auto main() -> int{
  string s("s");

  foo(string("hoge"));
  foo(s);    
  foo(std::move(s));
}



rvalue
lvalue
rvalue

[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ

sは左辺値なので、string& の方が呼ばれる。コンストラクターでいうと コピーコンストラクタである が、std::move()をつかうと string&&になり 右辺値参照となっている コンストラクターでいうと ムーブコンストラクタになる

仮引数では左辺値参照である

ここは解せないと最初に感じるポイントだろう string&& s(“hoge”); のようなコードを考えてみる sには右辺値参照の値が入っている 一見 sは右辺値参照であると思うが、思い出してほしい 右辺値というのは アドレスがとれない実体のない一時オブジェクトということだ しかしこの sには 実体がある ので、 右辺値参照をもった左辺値である ややこしい。 ので 関数のプロトタイプ void hoge(int &&i) のiは 左辺値なのである

#include<iostream>

using namespace std;

void foo2( string& s) {
  cout << "lvalue" << endl;
};
void foo2( string&& s) {
  cout << "rvalue" << endl;
};

void foo( string& s) {
  foo2(s);
};
void foo( string&& s) {
  foo2(s);
};

auto main() -> int{
  string s("s");

  foo(string("hoge"));
  foo(s);    
  foo(std::move(s));

}



lvalue
lvalue
lvalue

[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ

解せないかもしれないが、仮引数の時は Hoge&&で受け取っていても左辺値参照になっている これを解決するためには、左辺値参照で入ってきた仮引数をふたたび右辺値参照にキャストする必要がある

先ほどの foo(string&&)からfoo2を呼ぶときに std::move()を使い右辺値参照にキャストすればよい

void foo( string&& s) {
  foo2(std::move(s));
};


rvalue
lvalue
rvalue

[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ

これで、仮引数の時は左辺値参照になっているというのが実証できた

とりあえず、ざっと一通りの右辺値参照の基礎をかいた 多分 この程度知っているという人が多いと思うので 読み飛ばしてください

Railsはじめました

はじめに

私 みやたけゆき がRailsを始めるなんて冗談だろ?? 確かに私は なんでもかんでもRailsの風潮には否定的であるが 正しくRailsを使うのは ありと思ってる

RailsというかRubyには根本的な弱点 遅いという問題があるのに 速度が必要なリアルタイム処理にRailsを使ったり 遅いならサーバ台数を増やせばいいじゃない? それランニングコスト増えてますよ! など 気に入らない事も多い

サーバ台数が増えれば当然マシン代も増えるが、管理コストも増える事を忘れてはいけない 例えば以前 Railsで100台のサーバが必要だったサービスを C++で書き替えて 1台で余裕になった事もある

そんなわけで Rails2日目ですが Railsの良しあしを今の段階でかいていく

論じない事

Rubyは書いていて楽しい言語

楽しさは人それぞれであるし、私はC++が一番手になじみ楽しい それになにより、プログラマが楽しいからという理由で言語を選択すべきではない

Rubyは日本発だ

私は人並み以上に日本が好きだし愛国心はあるので、Rubyが世界一の言語になってほしいと願う ただ、もしRubyが世界一の言語でない場合は、その言語にこだわり 世界一のサービスを作れないというのは 日本を愛するものとしては 悲しい 世界一のサービスを作るためには、国産だからという理由で使うのは 真の愛国心ではない

日本語情報が多い

英語読め。 PythonだってJavaだって日本語で十分情報がある

Railsエンジニアは多いから集めやすい

今回は 派遣会社目線ではないし、それを言うならJavaエンジニアの方が集めやすいとおもわれる

スクリプト言語の功罪

私はコンパイルできる言語の方が事前にエラーを検知できるので好きだが 単体テストかけばいいじゃん という宗派もあったりで スクリプトの功罪については直接は語らない 同じく GCについても

デメリット

あえてデメリットから書く

遅い

Rubyの最大の弱点で、言語仕様みるかぎりどうしようもない。。。 mrubyとか方法はあるんだろうけど 根本的に遅い スレッドセーフになってないのでネイティブスレッド基本使えない 動的型付け言語 演算子オーバーロードできたり 最適化しにくい などなど 私にとっては この時点でかなり厳しい

コードに柔軟性が高い

シンタックスシュガーが多く、同じ処理を何通りにも書くことができる これをコード書くのが楽しいと表現する人もいるが ビジネスで考えるとPythonのように、一通りの書き方しかない方が良いにきまっている 特に Perl時代のPerl風構文が残っていると殺意がおこるひとも多いだろう

Railsの学習コスト

Railsはかなり大きなフレームワークのため学習コストが高い

メリット

言語が比較的簡単

といってもC++との比較 JavaPythonと比べたらどうなんだろうか Pythonとあまり変わりないきがする

Rails Console

便利ですね Pythonにもあると思うけど

総合評価

私がRailsにあまり好意的でないのは上でもかいたが とにかく 実行速度が遅く、その他はPythonと変わらない PythonにもDjangoというWebフレームワークもあるし コードの難易度も変わらないと思う

よくRailsは開発速度が速いというが、実際は Pythonで書いても同じだと思う Rubyの方がコード量が少ないというが、タイピングよりアルゴリズムを考える方が重要なので たいしたメリットではない Javaのようにコード記述量はIDEでサポートすればいいわけだし

ただ 現実では日本ではスタートアップのモックアップとしてRailsが好まれるので モックアップスピードとしては速いのだろう ただし、海外ではPythonが多いので、たいして変わらないと思われる しかしRubyは実行速度が遅いため、スケールしたら他の言語で書き直す事が多い さらに Pythonは同じ機能は一つの書き方しか出来ないのでメンテナンスに有利 そういうのを考えると、たいして開発速度かわらないのなら Pythonで作るべきだと思うし GoやJavaC#を極めて ライブラリを作り開発した方が良いと思う

それに そもそも BaaSサービスを使って作る方がモックアップ速いので 作り直し前提であれば BaaSを使いこなすのも良いと思う

そんなわけで、やはり 私はあまりRailsに対して必要性をあまり感じていない PythonとかGoでいいじゃん!

ただ、mrubyなど速度の速いRubyにたいしては期待しているが、使ってないからまだわからず

#キャッシュレス社会を#求めて#クレジットカード整理し#ブラックカード目指す

はじめに

中国の人から、中国ではほとんどのものがウェイボー決済で買える。QRコード読み取って決済すると自動販売機から商品が出てくる ときき 驚いた 日本ではスマホを使った決済の場合は SUICAEDYFelicaのような Bluetooth的なもので行うが、iPhoneにはその機能がなく 独自でカード発行して自動チャージにするとか行う しかしQRコードならiPhoneでも出来る ローテクノロジーで解決させる さすが中国流と思った

そんなわけで日本のお店はカード払い出来ないところも多い 10年ほど前に一度 現金をほぼ持たず カードだけで生活を試みたが 高級なデパートでしか使えず苦労した覚えがあるが 今は SUICAなどで比較的小さなお店でもチャンスがある

キャッシュレス社会を作るために日本の弱点をさぐるために 再び キャッシュレスを目指すことにした

10年前より圧倒的に簡単なはずだ

それとついでに クレジットカードをまとめて、ついでなので ブラックカードを目指そうと思う

ブラックカード

とは、各クレジットカードで最高級のものである アメックスセンチュリオンは年会費30万、その他15万とか10万。そこそこの年会費がかかる ただし 完全招待制で、クレジットカードをよく使い 金持ち認定されると招待される サービスとしては コンシェルジュサービスで、例えば 六本木で8名でフランス料理ディナーの予約など頼むと いい店を見つけて予約してくれたり 上手く使えば 十分元とれる

しかし、楽天ブラックカードは 実質はプラチナカードレベルなんだが 見た目はブラックで、みんなから尊敬されかねない 年会費も3万程度と ネタに手に入れるには十分だろう いちおうコンシェルジュサービスもあるようだ

ということで 楽天ブラックカードを手に入れるプランを考える

カード整理

わたしは 無料有料ふくめ カードをいっぱいもっている。これでも沢山解約したんだけどね・・

セゾンが 無料、クラブオン、アメックスプラチナ エポス ゴールド 楽天 ゴールド ライフ ノーマル ビックカメラ ・・・

フリーランス時代に作れるうちに作っておけと いっぱい作ったが整理しきらん・・ 無料カードはそのまま残すとして アメックスプラチナは解約することにした。

そして 主に使うカードは3枚にした

楽天 プレミアムカード JCBEdy

ブラックにするために これは必須。JCBカードにしたのは nanacoへのポイントをするため。 nanacoで税金などを払うと楽であろう 下記の戦略のため 今年はあまり使わない プレミアムカードの特典として ファーストパスが利用できるので、空港でVIPラウンジいける ブラックにするインビテーション条件は謎で 年500万クレジットカード払っても来ないという人もいたり 挑戦してみようとおもう

Eposゴールド VISA

年50万程の買い物をすると 一生ゴールドが無料になる 多分招待されたので無料のはずだが 一度確認してくる もし無料になっていなければ 今年無料枠にするため 買い物がんばる たしか 持っているだけで旅行保険入るので便利

イオン ゴールド Master?VISA WAON

年会費無料。ただしインバイト必要 ノーマルカード(SUICAダメ)を作り 年100万等の条件をみたせばインバイトくる 年会費無料のゴールドなので取っておこう

その他

nanacoカード作る Suicaはとりあえず モバイルで、カードで払う。本当は ビックSuicaオートチャージしたいが・・

ロードマップ

楽天をプレミアムにするためには いったんノーマルを解約必要なので解約する nanaco契約 イオンカード契約 Eposカード 無料になっているか確認 定期的な支払いをまとめる。とりあえず楽天カード

Eposとイオンカードでそれぞれ 50万、100万 以上の買い物をがんばる 税金は楽天カード nanacoで払う

イオンとEposをゴールドにしたら 楽天カードに集約し ブラック目指す

Vulkan LunarG-SDK Sampleで勉強する 01-init_instance

あらすじ

前回 あらたにVulkanAPIを勉強する事にし、ドライバのインストールと 現在オフィシャルの LunarGという VulkanSDKをインストールし Sampleのビルドまで行った

ので、Sampleを参考に Vulkanの勉強をする

サンプル一覧

Samplesの下だけでも46個あった。 非常に多いのだが1つだけ注意点

なぜかこのサンプルたち、見た限りでは レンダーループが作られておらず 1秒後に終了するように作っている

おそらくは、WindowsAndroidLinuxに対応するために、レンダーループを共通化するのが面倒だったのでは? と思うが 真意不明

46個のサンプルのうち チュートリアルになっている15個から見てみよう

チュートリアル01は init_instanceで、Vulkanのインスタンスを初期化するサンプルのようである チュートリアルを順にならべると

init_instance Vulkanインスタンスの初期化 enumerate_device デバイスの列挙 init_device デバイス初期化 init_command_buffer コマンドバッファの初期化 init_swapchain スワップチェインの初期化 init_depth_buffer 深度バッファの初期化 init_uniform_buffer Uniformバッファの初期化 init_pipeline_layout パイプラインレイアウト初期化 init_descriptor_set でスクリプタセット初期化 init_render_pass レンダーパス初期化 init_shaders シェーダー初期化 init_frame_buffers フレームバッファ初期化 init_vertex_buffer 頂点バッファ初期化 init_pipeline パイプライン初期化 draw_cube キューブ描画

やれやれ・・ たかがキューブ描画のために、初期化こんなにあるのか・・

01-init_instance

#include <iostream>
#include <cstdlib>
#include <util_init.hpp>

#define APP_SHORT_NAME "vulkansamples_instance"

int sample_main(int argc, char *argv[]) {
    struct sample_info info = {};
    init_global_layer_properties(info);

    /* VULKAN_KEY_START */

    // initialize the VkApplicationInfo structure
    VkApplicationInfo app_info = {};
    app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
    app_info.pNext = NULL;
    app_info.pApplicationName = APP_SHORT_NAME;
    app_info.applicationVersion = 1;
    app_info.pEngineName = APP_SHORT_NAME;
    app_info.engineVersion = 1;
    app_info.apiVersion = VK_API_VERSION_1_0;

    // initialize the VkInstanceCreateInfo structure
    VkInstanceCreateInfo inst_info = {};
    inst_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
    inst_info.pNext = NULL;
    inst_info.flags = 0;
    inst_info.pApplicationInfo = &app_info;
    inst_info.enabledExtensionCount = 0;
    inst_info.ppEnabledExtensionNames = NULL;
    inst_info.enabledLayerCount = 0;
    inst_info.ppEnabledLayerNames = NULL;

    VkInstance inst;
    VkResult res;

    res = vkCreateInstance(&inst_info, NULL, &inst);
    if (res == VK_ERROR_INCOMPATIBLE_DRIVER) {
        std::cout << "cannot find a compatible Vulkan ICD\n";
        exit(-1);
    } else if (res) {
        std::cout << "unknown error\n";
        exit(-1);
    }

    vkDestroyInstance(inst, NULL);

    /* VULKAN_KEY_END */

    return 0;
}

短いので全部乗せた。 初回なので少しだけ丁寧に

vkで始まる関数がVulkanのAPIである かなりC言語臭いが、それをラップしてC++っぽくしたライブラリもあるので今度紹介する

#include <util_init.hpp>

これはサンプルの独自のUtilであるが、大したことをしていない。今回で言えば sample_mainの定義がある程度

ここで少し気になる sample_main() という関数 そう、エントリーポイント関数がないやん? きっとこれがエントリーポイントだろ? すぐわかるが、一応調べると

#ifndef __ANDROID__
int main(int argc, char **argv) { sample_main(argc, argv); }
#endif

Android以外は単純に sample_mainを呼ぶだけである

Androidの場合は Android_handle_cmdで sample_mainを登録したり NDKから呼ぶため特別処理になっている

とりあえずは sample_mainはエントリーポイントだと思って良い

レイヤー列挙

init_global_layer_properties(info); の中身は

    do {
        res = vkEnumerateInstanceLayerProperties(&instance_layer_count, NULL);
        if (res)
            return res;

        if (instance_layer_count == 0) {
            return VK_SUCCESS;
        }

        vk_props = (VkLayerProperties *)realloc(
            vk_props, instance_layer_count * sizeof(VkLayerProperties));

        res =
            vkEnumerateInstanceLayerProperties(&instance_layer_count, vk_props);
    } while (res == VK_INCOMPLETE);

    /*
     * Now gather the extension list for each instance layer.
     */
    for (uint32_t i = 0; i < instance_layer_count; i++) {
        layer_properties layer_props;
        layer_props.properties = vk_props[i];
        res = init_global_extension_properties(layer_props);
        if (res)
            return res;
        info.instance_layer_properties.push_back(layer_props);
    }
    free(vk_props);

    return res;

となっている。 まずレイヤーとは何かだが、まだ詳細につかめていないが Vulkanのコマンドに割り込んで色々なことをするものらしい 例えば デバッグに使う

vkEnumerateInstanceLayerProperties の2番目の引数にnullptrを入れると、取得可能なレイヤー数が取れるので レイヤー数を取得したあと reallocで個数分確保している まことにC言語である

列挙中に極まれにレイヤーが追加されることがあるらしく VK_INCOMPLETE が返ってきたら、カウントからやり直すようになっている

インスタンス作成

vkCreateInstanceを呼ぶことで、Vulkanのインスタンスを作成する これが チュートリアル01のゴールだ。

インスタンス作成は単純で、VkApplicationInfoとVkInstanceCreateInfo を設定して渡せばよい 第一引数に VkInstanceCreateInfo  第二引数は 通常はnullptrでよい。必要があれば下記 MemoryAllocationを読む事 第三引数に Vulkanのインスタンスが入る

MemoryAllocation https://vulkan.lunarg.com/doc/view/1.0.33.0/linux/vkspec.chunked/ch10.html

vkDestroyInstance でインスタンスを解放すれば終了

初回なので丁寧に見たが、面倒なだけで そこまで難しくはなさそうだ

Vulkanほんと入門!

C++ Vulkan

はじめに

3D APIは複数あります

OpenGL

OpenGL、OpenGLES、WebGL等 色々と出ているがどれもがクロスプラットフォーム 現状 WindowsLinuxMacAndroidiOS、Web が動くのはこれしかない コンシュマーゲーム機でも採用されている 現在 WebGL1.0(GLES2.0)が主流だが、WebGL2.0(GLES3.0)も 動くブラウザが増えている

非常に簡単にコーディング出来るし、各種ラッパーライブラリもあり便利な反面 APIが高レイヤーすぎてパフォーマンスに問題がある 今後は速度を劇的に改善したVulkanへ移行されていくと思われる

その為、私は選択肢から外した

DirectX

Windows OS上で動くグラフィックAPI 現在は バージョン12まで出ている 非常に低レイヤーで最も速度が速いし さすがMicrosoft様、ライブラリも速度のわりに使いやすい またC++でラップされている。当然すべてスマートポインタ(COMポインタ)になっており メモリ開放も比較的容易 問題点は Windowsでしか動かない事

Mantle API

AMDの提唱した低レイヤーAPI 速度はかなり高速らしいが、普及率で下火になり Vulkanへと引き継がれる

Metal API

最初AppleはVulkanプロジェクトに入っていたが、Appleが脱退し 独自のAPIを作る そのため Metal APIApple製品でしか使えない Vulkanから分裂しただけあって、Vulkanとかなり似ている

Vulkan API

クロノスグループにより、OpenGLの後継APIとして標準化される MetalAPIを基本にした高速APIである WindowsLinuxAndroidで動く。 また Webで動く WebVulkanも開発されているらしい??

Apple製品以外はすべて対応していて、OpenGLとは比べ物にならない速度である DirectX12よりほんの少し遅いらしいが 誤差範囲

ということで、これからVulkanを勉強しようとおもう

環境構築

まず 私の環境は RazerBladeというノートPC ビデオは GeForce1060 Windows10である WindowsLinuxで、Kepler以降のGPUが必要だ。 以前はGPUの乗ってないWindowsとMacbookProしか持っておらず開発出来なかった RazerBladeを手に入れた今は 誰も邪魔するものはいない!

NVidiaより Vulkanドライバをインストール Vulkan Driver Support | NVIDIA Developer

Valveが開発協力した?という LunarG SDKを使う。私の知る限りこのSDKしか存在しない https://vulkan.lunarg.com/

サンプルがいっぱいついているが、実行するためにはCMakeなどが必要 VisualStudioさえインストールされてあれば Demos以下は実行可能である CubeにLunarGと書かれたテクスチャがはられたデモが表示されるはずだ

プロジェクトTemplate作成

VisualStudioのテンプレートも用意されている プロジェクト新規作成時にVulkanのパスの通ったプロジェクトを作成できる 便利なのでぜひ行っておきたい

SDKPATH/Templates/Visual Studio 201x にあるZIPファイルを ドキュメント\Visual Studio 201x\Templates\ProjectTemplates\Visual C++ Project に全部コピーしてVisualStudioを立ち上げなおすと New Projectを作った際にVulkanがテンプレートに表示される

サンプルを実行する

https://vulkan.lunarg.com/doc/sdk/1.0.39.1/windows/getting_started.html

に書いてあるように CMake 3.0 以降、Python3.0以降、VisualStudio2013 Update4以降 が必要

CMake

https://cmake.org/download/ ここから Windows用をダウンロードしインストール CMakeのパスを設定するか聞かれるので、設定しておこう

Python

Windowsは下記より。くれぐれも 3.0以降を入れる事 Python Releases for Windows | Python.org

ビルド

上記をインストールしたあとは VisualStudioとCMake、Pythonにパスを通したコマンドプロンプト 具体的には VisualStudioに VS20xx用MSBuildコマンドプロンプト 等を実行し SDKPATH/Samplesへいき build_windows_samples.bat を実行する

エラー!!

CMake Error at C:/Program Files/CMake/share/cmake-3.8/Modules/FindPackageHandleStandardArgs.cmake:137 (message): Could NOT find PythonInterp: Found unsuitable version “1.4”, but required is at least “3” (found C:/Program Files/Emscripten/python/2.7.5.3_64bit/python.exe) Call Stack (most recent call first): C:/Program Files/CMake/share/cmake-3.8/Modules/FindPackageHandleStandardArgs.cmake:375 (_FPHSA_FAILURE_MESSAGE) C:/Program Files/CMake/share/cmake-3.8/Modules/FindPythonInterp.cmake:149 (FIND_PACKAGE_HANDLE_STANDARD_ARGS) Sample-Programs/Hologram/CMakeLists.txt:1 (find_package)

Emscriptenを入れていてそっちのパスを見に行っている EmscriptenをアンインストールしCMake入れなおしたが 上記エラーが続き とりあえずあきらめた・・・

WindowsPython どうすればいいんだろうか・・・

解決

CMakeCacheにごみが残っていたので、削除してビルドしなおしたらうまくいきました。

Samples\build以下に プロジェクトが作成されるのでサンプルをビルドできます

ただし、VK_LayerOverlayだけはビルド出来ませんでした でも現状問題ないので このままサンプル見て勉強です!

boost::bind (std::bind)を理解する

C++

はじめに

C++で仕事をしていますが、C++って人によって理解度が違います 今回の案件はBoost.Asioで作れという指定なので、どうしてもC++色が強くなります

stream、string、functional、bind、rambda はたまた triboolやtuple、各種コンテナと

他の作業員がC++読めないので 事細かに説明していますが その中でも 最も説明が難しいものの1つが bind等の functionalでしょうか

ってことで、他の作業員への説明資料作成の下書きに ブログにはりつける

bindって?

大雑把にいえば 指定された関数から std::function を作るもの ですがよくわかりませんね 色々便利な関数ポインタ(実際は関数オブジェクトだが)を作るものということです

std::bind と boost::bindがありますが、似たようなものです 元々boost::bindがあり、とても有用なため標準ライブラリ入りしました 大きく違うのは、引数が stlだと std::placeholders::1 と長いですが boostだと 1 と、グローバルネームスペースにあり短いです

大きく2つのメリットを書いておきます

引数を束縛する

bindの名前の由来です

例えば int 2個の引数からなる関数の 第一引数を 2で固定するとか、引数の順番を変えたりとかできます

void hoge( int a, int b ){ std::cout << "arg1=" << a << "   arg2=" << b << "\n" }

auto fn1=boost::bind(hoge, 2, _2); // 第一引数を 2に束縛
auto fn2=boost::bind(hoge, _2, _1); // 引数の順番を逆に!

fn1(5);  // arg1=2  arg2=5  と出力
fn2(1,2);  // arg1=2  arg2=1  と出力

関数型言語のカリー化風なことができましたね

クラスのメンバ関数のポインタを グローバルスペースで使える

何を言っているかわかりませんね クラスのメンバ関数のプロトタイプには、クラススコープが付きます

例えば

struct C {
    void hoge() {}
};

この hogeのプロトタイプは void()() ではなく、void(C::)() です。

なので 下記

struct C {
    void hoge(int n) {}
};
struct C2 {
    void hoge(int n) {}
};

void(*fn)(int) = C::hoge; // Error
void(C::*fn2)(int) = C::hoge; // OK
void(C::*fn3)(int) = C2::hoge; // NG

クラスが固定ならば、上記の クラススコープ入りの関数ポインタで問題ないけど 例えばコールバック等で汎用的なインタフェース作ると困りますね そんな時に bindを使えば、引数が同じであれば クラススコープが異なっていても 同等に扱うことができます ただし、thisポインタが誰であるかを知る必要があるので 第二引数にそのクラスのthisポインタが必要です

struct C {
    void hoge(int n) {}
};
struct C2 {
    void hoge(int n)  {}
};

void hoge(int n){}

C c;
C2 c2;

std::function<void(int)> fn1,fn2,fn3;   // 全部同じ型
fn1 = boost::bind(hoge, _1); // OK  グローバルのhoge関数
fn2 = boost::bind(&C::hoge, c, _1); // OK
fn3 = boost::bind(&C2::hoge), c2, 5); // OK  当然引数も束縛出来る

std::function は、関数オブジェクトのラッパーである。関数オブジェクトは 大雑把にいうと関数ポインタの高機能版である

とりあえず これにより、異なるクラススコープのオブジェクトであっても、引数が同じであれば同じに扱うことが出来る

bindを使わない実装例

例えば Threadの作成などで、グローバル関数から クラスのメンバを呼ぶことがある この時 thisポインタが不明になるので色々と工夫をする必要がある global変数にthisを入れると、オブジェクト1つしか扱えないので却下。では リストで管理する? 面倒で不具合のもと ということで よく、staticのコールバック関数に void*型の引数を作り thisポインタを渡し Castして使う

struct Hoge{
  static void callback(void *self, int n){ static_cast<Hoge*>(self)->hoge(n); };
  void hoge(int n){};
};

setCallback( void(*fn)(void*,int), void* self){
  *fn(self, 5);
}

Hoge h;
setCallback( Hoge::callback, &h );

なんとなくダサいと思ったらその感覚は正しい 上記は function、bindでは こう書ける

struct Hoge{
  void callback(int n){ hoge(n); };
  void hoge(int n){};
};

setCallback( std::function<void(int)> fn){
  *fn(5);
}

Hoge h;
setCallback( boost::bind(&Hoge::callback, &h ));

thisポインタをコールバック側で受けてCastする事もなく、static関数を使うこともなく きれいに書ける

関数オブジェクトとは

functionを理解するには必要

要約すると クラスにoperator()をオーバーロードし、あたかも関数のように見えるオブジェクトである

struct FUNC {
    int operator ()(int n) {
        cout << "operator " << n << endl;
        return n;
    };
};

FUNC f;

f(2); // 関数呼び出しっぽく見えるがメンバ変数の operator(int)を呼び出している

この目の錯覚をつかい 人間にはあたかも関数ポインタだと思わせるのが関数オブジェクトである

その関数オブジェクトを使い、bindっぽい事をしてみる

struct C {
    void hoge(int n) {
        cout << "C::hoge  " << n << endl ;
    }

};

template<class T>
struct FUNCOBJ {
    T *self_;
    FUNCOBJ(T* self) : self_(self) {};

    int operator ()(int n) {
        self_->hoge(n);
        return n;
    };
};

C c;

std::function<int(int)> fn1 = FUNCOBJ<C>(&c);

fn1(5);

このように、一度 C::hogeを呼び出す関数オブジェクトを作り、thisを関数オブジェクトのメンバとしてキャプチャすれば 関数ポインタのように関数オブジェクトを扱うことが出来る。

bindは、引数のテンプレートを処理したり、非常に複雑で中身を理解しきれないが大雑把には 関数オブジェクトを作成し メンバ変数をWrapしている

また、C++11では関数オブジェクトを簡単に書くことが出来る ラムダ関数があるので上記は こう書ける

C c;

std::function<int(int)> fn1 = [&c](int n) { return(c.hoge(n)); };

fn1(5);

ちなみに上のコールバックはラムダだと

struct Hoge{
  void callback(int n){ hoge(n); };
  void hoge(int n){};
};

setCallback( std::function<void(int)> fn){
  *fn(5);
}

Hoge h;
setCallback( [&h](int n){ h.callback(n);} );