右辺値参照~完全転送まで100%理解するぞ! part1 右辺値参照とは
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
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
解せないかもしれないが、仮引数の時は Hoge&&で受け取っていても左辺値参照になっている これを解決するためには、左辺値参照で入ってきた仮引数をふたたび右辺値参照にキャストする必要がある
先ほどの foo(string&&)からfoo2を呼ぶときに std::move()を使い右辺値参照にキャストすればよい
void foo( string&& s) {
foo2(std::move(s));
};
rvalue
lvalue
rvalue
これで、仮引数の時は左辺値参照になっているというのが実証できた
とりあえず、ざっと一通りの右辺値参照の基礎をかいた 多分 この程度知っているという人が多いと思うので 読み飛ばしてください
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++との比較 JavaやPythonと比べたらどうなんだろうか Pythonとあまり変わりないきがする
Rails Console
便利ですね Pythonにもあると思うけど
総合評価
私がRailsにあまり好意的でないのは上でもかいたが とにかく 実行速度が遅く、その他はPythonと変わらない PythonにもDjangoというWebフレームワークもあるし コードの難易度も変わらないと思う
よくRailsは開発速度が速いというが、実際は Pythonで書いても同じだと思う Rubyの方がコード量が少ないというが、タイピングよりアルゴリズムを考える方が重要なので たいしたメリットではない Javaのようにコード記述量はIDEでサポートすればいいわけだし
ただ 現実では日本ではスタートアップのモックアップとしてRailsが好まれるので モックアップスピードとしては速いのだろう ただし、海外ではPythonが多いので、たいして変わらないと思われる しかしRubyは実行速度が遅いため、スケールしたら他の言語で書き直す事が多い さらに Pythonは同じ機能は一つの書き方しか出来ないのでメンテナンスに有利 そういうのを考えると、たいして開発速度かわらないのなら Pythonで作るべきだと思うし GoやJava、C#を極めて ライブラリを作り開発した方が良いと思う
それに そもそも BaaSサービスを使って作る方がモックアップ速いので 作り直し前提であれば BaaSを使いこなすのも良いと思う
そんなわけで、やはり 私はあまりRailsに対して必要性をあまり感じていない PythonとかGoでいいじゃん!
ただ、mrubyなど速度の速いRubyにたいしては期待しているが、使ってないからまだわからず
#キャッシュレス社会を#求めて#クレジットカード整理し#ブラックカード目指す
はじめに
中国の人から、中国ではほとんどのものがウェイボー決済で買える。QRコード読み取って決済すると自動販売機から商品が出てくる ときき 驚いた 日本ではスマホを使った決済の場合は SUICAやEDY、Felicaのような Bluetooth的なもので行うが、iPhoneにはその機能がなく 独自でカード発行して自動チャージにするとか行う しかしQRコードならiPhoneでも出来る ローテクノロジーで解決させる さすが中国流と思った
そんなわけで日本のお店はカード払い出来ないところも多い 10年ほど前に一度 現金をほぼ持たず カードだけで生活を試みたが 高級なデパートでしか使えず苦労した覚えがあるが 今は SUICAなどで比較的小さなお店でもチャンスがある
キャッシュレス社会を作るために日本の弱点をさぐるために 再び キャッシュレスを目指すことにした
10年前より圧倒的に簡単なはずだ
それとついでに クレジットカードをまとめて、ついでなので ブラックカードを目指そうと思う
ブラックカード
とは、各クレジットカードで最高級のものである アメックスセンチュリオンは年会費30万、その他15万とか10万。そこそこの年会費がかかる ただし 完全招待制で、クレジットカードをよく使い 金持ち認定されると招待される サービスとしては コンシェルジュサービスで、例えば 六本木で8名でフランス料理ディナーの予約など頼むと いい店を見つけて予約してくれたり 上手く使えば 十分元とれる
しかし、楽天のブラックカードは 実質はプラチナカードレベルなんだが 見た目はブラックで、みんなから尊敬されかねない 年会費も3万程度と ネタに手に入れるには十分だろう いちおうコンシェルジュサービスもあるようだ
カード整理
わたしは 無料有料ふくめ カードをいっぱいもっている。これでも沢山解約したんだけどね・・
セゾンが 無料、クラブオン、アメックスプラチナ エポス ゴールド 楽天 ゴールド ライフ ノーマル ビックカメラ ・・・
フリーランス時代に作れるうちに作っておけと いっぱい作ったが整理しきらん・・ 無料カードはそのまま残すとして アメックスプラチナは解約することにした。
そして 主に使うカードは3枚にした
楽天 プレミアムカード JCB、Edy
ブラックにするために これは必須。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秒後に終了するように作っている
おそらくは、Windows、Android、Linuxに対応するために、レンダーループを共通化するのが面倒だったのでは? と思うが 真意不明
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ほんと入門!
はじめに
3D APIは複数あります
OpenGL系
OpenGL、OpenGLES、WebGL等 色々と出ているがどれもがクロスプラットフォーム 現状 Windows、Linux、Mac、Android、iOS、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 APIは Apple製品でしか使えない Vulkanから分裂しただけあって、Vulkanとかなり似ている
Vulkan API
クロノスグループにより、OpenGLの後継APIとして標準化される MetalAPIを基本にした高速APIである Windows、Linux、Androidで動く。 また Webで動く WebVulkanも開発されているらしい??
Apple製品以外はすべて対応していて、OpenGLとは比べ物にならない速度である DirectX12よりほんの少し遅いらしいが 誤差範囲
ということで、これからVulkanを勉強しようとおもう
環境構築
まず 私の環境は RazerBladeというノートPC ビデオは GeForce1060 Windows10である WindowsかLinuxで、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入れなおしたが 上記エラーが続き とりあえずあきらめた・・・
WindowsのPython どうすればいいんだろうか・・・
解決
CMakeCacheにごみが残っていたので、削除してビルドしなおしたらうまくいきました。
Samples\build以下に プロジェクトが作成されるのでサンプルをビルドできます
ただし、VK_LayerOverlayだけはビルド出来ませんでした でも現状問題ないので このままサンプル見て勉強です!
boost::bind (std::bind)を理解する
はじめに
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);} );
WebAssembly Windows環境構築
Windows環境を作るにあたって
公式ページでは下記のように http://webassembly.org/getting-started/developers-guide/
The instructions on this page are applicable to Linux and Mac OS X systems. Similar instructions for Windows systems are forthcoming.
Windows環境の構築については まだ手順がなさそうなので 挑戦してみます
構築
基本的には公式の Linux、Mac環境構築を参考にします http://webassembly.org/getting-started/developers-guide/
ChromeのWebAssembly有効化
chrome://flags/#enable-webassembly と Chromeで入力し WebAssemblyを有効にし 再起動させる!
emsdkをインストール
https://kripken.github.io/emscripten-site/index.html
ダウンロードページよりWindowsバイナリを入手しインストール インストール時にパスも通してくれるので 私は通した。
emsdkのパッケージ更新
Linuxの手順で行うと、gitがないと怒られたので、gitも入れます
> emsdk update > emsdk install git-1.9.4 > emsdk install clang-incoming-64bit emscripten-incoming-64bit sdk-incoming-64bit > emsdk activate clang-incoming-64bit emscripten-incoming-64bit sdk-incoming-64bit
一通り環境整ったはずなので テストします
emsdk テスト
上記の公式をもとに、Cでソースをかきます たとえば hello.c を作成
#include<stdio.h> int main(int argc, char ** argv ){ printf("Hello, world!\n"); }
ビルドします
> emcc hello.c -s WASM=1 -o hello.html
結構コンパイル時間かかったし Warningもいっぱい出ましたが無事ファイルが複数できました before.js、hello.html、hello.js、hello.js.temp.js の4ファイルが私の環境ではできました。 このうちの hello.htmlが目的のものなので ブラウザで表示しましょう。
Apache等あればそこで表示してもいいです 公式では pythonを使って
> python -m SimpleHTTPServer 8080
ブラウザより localhost:8080/hello.html
をたたくと、Emscriptenウインドウに Hello,World! が表示されるはず。 表示されたら成功! おめでとう!
とのことらしいですが、期待していたバイナリが出ていないような・・・
> emcc -O2 hello.c -s WASM=1 -o hello.html
と、最適化オプションつけると hello.wasm、load-wasm-worker.js、hello.html.mem などの 怪しいものが出てきます・・
このあたり 一度調査必要ですね
とりあえず公式的には環境完成のようです