ぷろみん

プログラミング的な内容を扱ってます

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);
    }));
}

参考

クロージャとラムダ式をザックリ解説してみる | No Creativity, No Life!

C++の標準描画ライブラリに入るかもしれないCairoのサンプルをwindows向けにビルドしてみた

cairoとC++

本の虫: Herb SutterがCairoのMLにC++標準規格にCairoを入れられないか打診中

環境

依存関係

$ 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

サンプル

パブリックドメイン

gist.github.com

ビルド

$ x86_64-w64-mingw32-g++ -I/mingw64/include main.cpp -L /mingw64/lib -lcairo -lgdi32

上記ソースとはパスが違うのでcairo-win32.hcairo/cairo-win32.hとしました。

f:id:torini:20160105193421j:plain

基本クラスの継承を考えたデストラクタの属性設定

概要

この記事は間違った情報が多分に含まれるため

https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#c35-a-base-class-destructor-should-be-either-public-and-virtual-or-protected-and-nonvirtual

https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#discussion-make-base-class-destructors-public-and-virtual-or-protected-and-nonvirtual

の翻訳記事に書き換える予定です。

以下元の記事。

実行時の型とポインタの型が違う場合、ただ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_ptrtype 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.hSDKに含まれていないので注意が必要です。

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.libdxgi.libを加えるとビルドできます。

ようやくWindows10にしたのでDX12プログラミングを始めてみた

概要

Windows10にしたらまず最初にすべき事といえば・・・
そう、DirectX12プログラミングですよね。
今回はその導入をやってみたのでメモしておきます。

Visual Studioのアップグレード

始めから入ってない人もここで入れるので一緒です。

Direct3D12 開発環境構築 -プレビュー版- :: ☆PROJECT ASURA☆

ここの手順に従ってインストーラーをDLして実行し、アップグレードが終わった後に変更からWindows SDK10を導入します。

アップグレードの場合は(もしくは最新のビルドでは不要)Windows 10 Developer Preview Toolsはインストール済みでした。

動作確認

Git for WindowsCygwinMinGW等で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

x64だとリンカエラー出ました。

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されました。

参考

git-flow cheatsheet

( WIP: 更新済み ) Ubuntu + Vagrant + Ansible + Serverspec + Capistrano 3 + Git-Flow を使って Nginx + Rails + Unicorn で走るサービスの面倒な作業を自動化しよう! - コードレシピ

terminal - git auto-complete for *branches* at the command line? - Ask Different

licenses/lice · GitHub