C++で無理矢理クロージャを使ってみる
クロージャ
現在クロージャという表現を使う場合状態を持ったラムダを指す場合が多い気がします。 つまり、ワンライナーで書けるクラスといった感じです。これはC++では関数オブジェクト的な手法で似たようなことができますが、javascript程普通なものではありません。
// javascript const twice = (action) => { action(); action(); }; const counter = () => { let count = 0; return () => { count++; console.log(i); return i; }; }; const a = counter(); twice(a); // 1, 2 const b = counter(); twice(b); // 1, 2
C++だと外部ストレージを用いなければできないと思っていましたが、内部ローカル変数を参照でバインドし延命させることでできました。
// C++ auto twice = [](auto action) { action(); action(); }; auto counter = [] { int count = 0; auto increment = [](int& count) { count++; std::cout << count; }; return std::bind(increment, count); }; auto a = counter(); twice(a); // 1, 2 auto b = counter(); twice(b); // 1, 2
応用
これを利用するとfor_each
中にコンテキストを保存することができます。
今回は前回のループの値を利用してみました。
素直にローカル変数をキャプチャした方が良いような気もしますが、内部で完結しているのも1つのメリットではないでしょうか。
#include <algorithm> #include <vector> #include <functional> #include <iostream> namespace { auto context = [](auto function) { auto context = std::make_pair(false, 0); auto action = [](decltype(context)& context, auto& function, auto& value) { function(value, context); }; return std::bind(action, context, function, std::placeholders::_1); }; } int main() { std::vector<int> vec = { 5, 10, 15, 20, 25 }; std::for_each(vec.cbegin(), vec.cend(), context([](auto& value, auto& context) { if(context.first) { std::cout << value - context.second << std::endl; } context = std::make_pair(true, value); })); }
参考
C++の標準描画ライブラリに入るかもしれないCairoのサンプルをwindows向けにビルドしてみた
cairoとC++
本の虫: Herb SutterがCairoのMLにC++標準規格にCairoを入れられないか打診中
環境
- windows
- msys2
依存関係
$ pacman -S mingw-w64-x86_64-zlib $ pacman -S mingw-w64-x86_64-libpng $ pacman -S mingw-w64-x86_64-pixman $ pacman -S mingw-w64-x86_64-cairo
サンプル
ビルド
$ x86_64-w64-mingw32-g++ -I/mingw64/include main.cpp -L /mingw64/lib -lcairo -lgdi32
上記ソースとはパスが違うのでcairo-win32.h
をcairo/cairo-win32.h
としました。
基本クラスの継承を考えたデストラクタの属性設定
概要
この記事は間違った情報が多分に含まれるため
と
の翻訳記事に書き換える予定です。
以下元の記事。
実行時の型とポインタの型が違う場合、ただnewするだけでは実行時の型情報がロストするので、ちゃんとtype erasure
するかvirtualを付けましょう。
型情報がロストして困る例
#include <iostream> struct Foo{ ~Foo(){ std::cout << "Foo" << std::endl; } }; struct Bar: public Foo{ ~Bar(){ std::cout << "Bar" << std::endl; } }; int main(){ Foo *bar = new Bar(); delete bar; }
このコードではBarのデストラクタが実行されません。何故ならBarの型情報をどこにも保持していないからです。
virtualで解決する
struct Foo{ virtual ~Foo(){ std::cout << "Foo" << std::endl; } };
そこでデストラクタにvirtualを付けてやるとBarの型情報が(正確にはオーバーライドされた関数へのポインタ)が保持されるのでBarのデストラクタが実行されるようになります。
type erasureで解決する
int main(){ std::shared_ptr<Foo> bar(new Bar); }
std::shared_ptr
はtype erasure
というテクニックを使い型を内部で保持します。なのでBarのデストラクタにvirtualが付いていなくてもBarのデストラクタが実行されます。
どっちを使うべきか
Foo
がインターフェイスの場合継承される意図を明確にする為にvirtualを付けるのが良いと思います。反対にFoo
がただの一般クラスで同じ機能を流用したくて継承する場合はvirtualを付ける事を推奨しません。こういう場合は使いたい機能をインターフェイスに分離すべきです。
インターフェイスが何らかの事情で用意できない場合ですらstd::shared_ptr
で解放するのは意図が明白にならないのでオススメしません。継承先解放用のクラスを作成するのが本来なら良いのですが、このような複雑なテンプレートはしっかりとテストできる環境でのみ作成すべきです。なので、妥協案として継承先解放用のクラスをshared_ptrのエイリアスとして定義すると良いと思います。
余談
std::unique_ptr
は型情報を内部で保存していません。なので、継承先のデストラクタは実行されません。
シングルトンを使うのはやめよう
概要
この記事はC++の作者Bjarne Stroustrup
等によって書かれたコーディングガイドラインのAvoid singletons
の箇所の翻訳です。
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Ri-singleton
シングルトンを使うのはやめよう
理由
シングルトンは基本的に変装したグローバルオブジェクトに過ぎないため。
例
class Singleton { // シングルトンオブジェクトが確実に1つだけ作られて // プロパティの初期化が行われるようにする };
シングルトンという発想には多くの亜種がある。 それが問題の1つである。
メモ
あなたがグローバルオブジェクトを変更したくない場合、const
またはconstexpr
を宣言する。
例外
最初の使用での初期化を行うための純粋なシングルトン(設計を考えなくて良いような)の場合は使用できる。
X& myX() { static X my_x {3}; return my_x; }
これは初期化順を制御する最も効果的な解決策の1つだ。 マルチスレッド環境において静的オブジェクトはレースコンディションを引き起こさない。(うっかりそのコンストラクタ内から共有オブジェクトにアクセスしない限り)
もし、あなたが他の大勢のようにシングルトンをオブジェクトを1つだけ作成するためのクラスと定義するのなら、myX
のような関数はシングルトンではない。そして、この便利なテクニックはシングルトンを無くすルールの例外ではない。
実施
一般化することは非常に難しい。
singleton
を含む名前を持つクラスを探す。- 作成された単一オブジェクトを探す。(オブジェクトを数えたりコンストラクタを調査する)
- クラスXの持つpublic static関数が内部に静的なクラスXを持ち、それのポインタや参照を返している場合、それを禁止する。
DirectX12をビルド可能なプロジェクトを作る
前回
ようやくWindows10にしたのでDX12プログラミングを始めてみた - ぷろみん
ビルド
New Project -> Win32 Project -> Empty Project
新しく生成されたフォルダに前回ビルドに成功したサンプルの.cppと.hをコピーしてきます。
Solution Explorerのファイルが並んでいるアイコン(Show All File)をクリックし、先ほどコピーしたファイルを出現させます。
そして、全選択してから右クリックInclude In Projectします。
1からソースを書く場合は省略できますが、d3dx12.h
はSDKに含まれていないので注意が必要です。
How to: Use the Windows 10 SDK in a Windows Desktop Application
を参考にRetarget SDK Version
すると前回設定した項目が入力可能になります。
プロジェクトのプロパティを開き利用したいバージョンを指定しましょう。
そして、プロジェクトのプロパティの右上にあるConfiguration ManagerからActive solution platformの箇所をx64に変更します。
最後にLinker -> Input -> Additional Dependenciesにd3d12.lib
とdxgi.lib
を加えるとビルドできます。
ようやくWindows10にしたのでDX12プログラミングを始めてみた
概要
Windows10にしたらまず最初にすべき事といえば・・・
そう、DirectX12プログラミングですよね。
今回はその導入をやってみたのでメモしておきます。
Visual Studioのアップグレード
始めから入ってない人もここで入れるので一緒です。
Direct3D12 開発環境構築 -プレビュー版- :: ☆PROJECT ASURA☆
ここの手順に従ってインストーラーをDLして実行し、アップグレードが終わった後に変更からWindows SDK10を導入します。
アップグレードの場合は(もしくは最新のビルドでは不要)Windows 10 Developer Preview Toolsはインストール済みでした。
動作確認
Git for WindowsやCygwin、MinGW等でgitを入れます。
$ git clone https://github.com/Microsoft/DirectX-Graphics-Samples $ cd DirectX-Graphics-Samples/Samples/D3D12HelloWorld/src
D3D12HelloWorld.sln
からVSを起動させます。
そのままではビルドに失敗するので
Solutin Explorerのプロジェクトを右クリックしてプロパティを選び、
GeneralにてTarget Platform Versionを新しい物へ変更します。
私の場合は10.0.10240.0
から10.0.10586.0
へ変更しました。
HelloWindowのプロジェクトが生成した実行ファイルをビルドしてウィンドウが表示されたらOKです。
プロジェクトの作成下見
new projectするとDirectX 12 Appといういかにもなテンプレートが追加されていますが、C++/CLIぽかったのでスルーします。
いつも通りのWin32 Projectで作成します。
パス
DirectX12関連のパスをメモしておきます。
Solutin Explorerのプロジェクトを右クリックしてプロパティを選び
VC++DirectoriesにてIncludeとLibraryを追加します。
Include Directories
C:\Program Files (x86)\Windows Kits\10\Include\10.0.platform番号\um C:\Program Files (x86)\Windows Kits\10\Include\10.0.platform番号\shared
Library Directories
C:\Program Files (x86)\Windows Kits\10\Lib\10.0.platform番号\um\x86
git-flowとhubコマンドを使ってCUIでgithubを活用する
github/hubコマンドを使ってみた - ぷろみんの続きです
概要
git-flowとhubを使います。
インストール
$ sudo apt-get install -y git-flow
初期化
$ mkdir foo $ cd foo # hub導入済み $ git init -g $ git flow init # 質問には全部エンター $ git create $ echo '# foo' >> README.md $ git add . $ git commit -m 'Initial commit' $ git push --all
開発
# issue create [-m <MESSAGE>|-f <FILE>] [-l <LABEL-1>,<LABEL-2>...,<LABEL-N>] $ git issue create -m 'LICENSEを追加する' # issueを解決するブランチを作成する $ git flow feature start add_license
licenseの追加にはliceを使ってみようかと思います。
$ sudo pip install lice $ lice mit > LICENSE
featureブランチでissueを解決し、プッシュします。
$ git add . $ git commit -m 'close #1' $ git flow feature publish add_license
そして、その変更を取り込んでもらえるようにpull-requestを送ります。
$ git pull-request -m "Add license" -b develop -h feature/add_license
問題無ければマージします。
$ git checkout develop
$ git merge https://github.com/YOUR_USER/CURRENT_REPO/pull/2
$ git push origin develop
ここもhubさんの力でgit pr-merge 2
とかできたら良かったんですけどねー。
issueが全て片付いたので、masterへマージします。
$ git checkout master $ git merge develop $ git push origin master
githubで確認すると無事にissueがcloseされました。
参考
terminal - git auto-complete for *branches* at the command line? - Ask Different