2014年10月26日日曜日

Fabricはexecute APIで自由度の高いデプロイツールに

FabricはPythonの関数を直接呼び出す機能とSSH経由のコマンドを複数のホストに対して楽に発行する機能を持つライブラリです。
簡単な使い方はこちらのページで確認できます。(最近はFabricで検索すると日本語の紹介記事もヒットするようになりました。)
http://docs.fabfile.org/en/latest/tutorial.html

初歩的な使い方だとすぐに使えるようになるのがFabricのメリットです。
また結局はPythonスクリプトなので、サーバー障害時のサービスアウトからパッケージ更新等のデプロイまで幅広く使えます。

しかしある程度大規模で複雑なシステムが対象になると初歩的な使い方では間に合わなくなります。
たとえば、複数のDBサーバをサービスアウトするためには複数のAPサーバーの設定ファイルを書き換える必要がある時はどうすれば良いでしょうか?
あるいは、サービスアウト、デプロイ、サービスインの一連の手順を1台ずつ行いたいという場合はどう記述すれば良いでしょうか?

これらはfab -H [ホスト名] [タスク名]という単純なfab実行方法では不可能で、env.hostsを書き換えるのもタスク実行前に行わなければならないので難しいです。
そこで、fabric.apiのexecuteを使用します。
公式ドキュメントにはこのようなコードが載っています。

from fabric.api import run, execute, task

from mylib import external_datastore

def do_work():
    run("something interesting on a host")

@task
def deploy(lookup_param):
    host_list = external_datastore.query(lookup_param)
    execute(do_work, hosts=host_list)

http://docs.fabfile.org/en/latest/usage/execution.html#intelligently-executing-tasks-with-execute
Using execute with dynamically-set host listsより

host_listに対象となるホストのリストを与えてexecuteすることで、タスクごとに異なるホスト群を指定できることに注目してください。
また、タスク内でこのexecuteは何度も実行できますので、サービスイン・デプロイ・サービスアウトを順に行うタスクも記述可能です。(後述)
これを使えば、1つのタスク内で異なるホスト群に対していろいろなコマンドを発行することができるようになります。
例として、複数台のWebサーバ、Applicationサーバ、Databaseサーバで構成されるシステムについて以下の様なFabricスクリプトを書いてみました。(あくまでFabricのために書いたもので、サーバに対して発行するコマンドはechoのみにしてます。)
ファイル構成は以下の通り(fabfileディレクトリと__init__.pyはデフォルト設定では必須です。)

--fabfile
  |-- __init__.py
  |-- web.py
  |-- ap.py
  |-- db.py
  |-- common.py
  |-- conf.py

この例では各コンポーネントに発行するコマンドがだいたい同じだと想定してComponentという抽象クラスを用意し、各コンポーネントクラスはComponentを継承してservice_in, service_outなどのメソッドをオーバーライドしています。

実行例は以下の通り。(実行結果は長いので省きます)

# webサーバ1台ずつサービスアウト・パッケージ更新・サービスインの一連のタスクを行います
$ fab release:web,update_pkgs
# 障害が起きたwebサーバを1台指定してサービスアウトします
$ fab service_out:web01.example.com
# DBサーバそれぞれに対してバックアップを取ります
$ fab backup_db

__init__.py

# -*- coding: utf-8 -*-

import web, ap, db
from common import get_hosts, get_role
from fabric.api import sudo, execute
from fabric.decorators import task
from functools import partial

components = {
    'web': web.Web(),
    'ap': ap.Application(),
    'db': db.Database()
}

# デプロイ名を指定して各種デプロイを行う関数
def deploy(name):
    if name == 'update_pkgs':
        sudo('echo "Update packages"')
    else:
        raise Exception('Unknown deploy command %s' % name)

# 任意個のデプロイをFabricタスクとして呼び出す関数
def do_deploys(deploys, hosts):
    for d in deploys:
        execute(partial(deploy, d), hosts=hosts)

# 障害時などにホスト名を指定してサービスアウトを行うタスク
@task
def service_out(hostname):
    components[get_role(hostname)].service_out(hostname)

# 障害復旧時などにホスト名を指定してサービスインを行うタスク
@task
def service_in(hostname):
    c = components[get_role(hostname)]
    c.restart(hostname)
    c.service_in(hostname)

# パッケージ更新などのデプロイをコンポーネント指定でまとめて行うタスク
# サービスアウト、デプロイ、プロセス再起動、サービスインを順に行います
@task
def release(target, *deploys):
    c = components[target]
    # 一連のプロセスはサーバ1台毎に行います
    for host in get_hosts(target):
        c.service_out(host)
        do_deploys(deploys, host)
        c.restart(host)
        c.service_in(host)

@task
def backup_db():
    db.Database().backup(get_hosts('db'))

db.py

# -*- coding: utf-8 -*-

from common import Component, get_hosts
from fabric.api import run, execute
from functools import partial

def service_out(hostname):
    run('echo "service out %s"' % hostname)

def service_in(hostname):
    run('echo "service in %s"' % hostname)

def restart():
    run('echo "restart"')

def backup():
    run('echo "Backup database"')

class Database(Component):
    def service_out(self, *hosts):
        for host in hosts:
            execute(partial(service_out, host), hosts=get_hosts('ap'))
    def service_in(self, *hosts):
        for host in hosts:
            execute(partial(service_in, host), hosts=get_hosts('ap'))
    def restart(self, hosts):
        execute(restart, hosts=hosts)
    def backup(self, hosts):
        execute(backup, hosts=hosts)

web.pyとap.pyはdb.pyと同じような内容なので下記を参照
https://github.com/muumu/fabric-sample/blob/master/fabfile/web.py
https://github.com/muumu/fabric-sample/blob/master/fabfile/ap.py

common.py

# -*- coding: utf-8 -*-

import conf
from fabric.api import env
from abc import ABCMeta, abstractmethod

# 各コンポーネントが持っているべきメソッドを宣言します
# 実際のシステムでは全プロセスを止めるstopとかもあるべきかと思います
class Component:
    __metaclass__ = ABCMeta
    @abstractmethod
    def service_out(self, hosts): pass

    @abstractmethod
    def service_in(self, hosts): pass

    @abstractmethod
    def restart(self, hosts): pass

# 大規模システムだとサーバー情報を入れたDBからホスト名を引いてくるかと思いますが
# ここでは簡単のため@rolesデコレータとして利用可能なenv.roledefsを使用します
def get_hosts(role_name):
    if role_name not in env.roledefs:
        raise Exception('Invalid role name: %s' % role_name)
    return env.roledefs[role_name]

# ホスト名からロール名を取得する関数です
# これも簡単のため単にenv.roledefsから逆引き
def get_role(hostname):
    for role, hosts in env.roledefs.items():
        if hostname in hosts:
            return role
    raise Exception('Invalid hostname: %s' % hostname)

conf.py

# -*- coding: utf-8 -*-

from fabric.api import env

env.roledefs = {
    'web': ['web01.example.com', 'web02.example.com'],
    'ap': ['ap01.example.com', 'ap02.example.com'],
    'db': ['db01.example.com', 'db02.example.com']
}

# 1つのホストにタスク実行が終わる毎に切断して接続リソースを節約します
env.eagerly_disconnect = True
# ssh_configの設定内容を使用してssh接続を行います
env.use_ssh_config = True
https://github.com/muumu/fabric-sample
このようにFabricの自由度はかなり高いので、大規模で複雑なシステムに対して柔軟なコマンドを発行したい、という場合に手早く実装できてとても便利です。

2014年10月13日月曜日

Pythonのmap関数をC++で作る試み

Pythonのmapが便利なので、同じように使えそうなmapをC++14で作ってみました。
ソースはこちら。(勉強がてらなので、雑然としてます。)
サンプル: https://github.com/muumu/lib/blob/master/sample/sample_functional.cpp
ライブラリ: https://github.com/muumu/lib/blob/master/functional.h
以下、作ったfn::mapのだいたいの使用感です。

それぞれの要素に関数やメンバ関数を適用したコンテナを作る

{
    vector<string> source = {"a?", "ab?", "abc?", "abcd?"};
    auto output = fn::map(source, fn::init);
    print(output); // {"a","ab","abc","abcd"}
}
{
    list<string> source = {"", "occupied", "", "occupied"};
    auto output = fn::map(source, &string::empty);
    print(output); // {true,false,true,false}
}
{
    array<string, 4> source = {"add", "sub", "mul", "div"};
    auto output = fn::map(source, util::upper);
    print(output); // {"ADD","SUB","MUL","DIV"}
}

違うデータ型になる関数はそのデータ型のコンテナに変換

{
    set<string> source = {"tea", "wine", "milk", "coffee"};
    auto output = fn::map(source, &string::size);
    print(output); // {3,4,6}
}
{
    multiset<string> source = {"tea", "wine", "milk", "coffee"};
    auto output = fn::map(source, &string::size);
    print(output); // {3,4,4,6}
}

キーとバリューを逆転したマップの作成

{
    map<string, int> source_map = {{"RED", 0}, {"GREEN", 1}, {"BLUE", 2}};
    auto reversed_map = fn::map(source_map, fn::swap<string, int>);
    print(reversed_map); // {{0,"RED"},{1,"GREEN"},{2,"BLUE"}}
}

vectorの出力を違うコンテナで受け取る

{
    multimap<string, int> src_map = {{"RED", 0}, {"RED", 0}, {"GREEN", 1}};
    auto func = [](const pair<string, int>& pair) {
        return pair.first + ": " + util::to_string(pair.second);};
    auto result = fn::map(src_map, func);
    print(result); // {"GREEN: 1","RED: 0","RED: 0"}
    set<string> result_set = fn::map(src_map, func);
    print(result_set); // {"GREEN: 1","RED: 0"}
}

2014年8月18日月曜日

【読書ノート】Effective Modern C++ - Item 7

O'Reilly Early Releaseで購入したEffective Modern C++のドラフト版の読書記録です。
※この記事はあくまで私の備忘録です。(断りなく自分の解釈や考え・感想を入れたり、理由もなく内容を省略したり、間違いや曲解もあると思います。誤りの指摘は歓迎です。)

Item 7: オブジェクトを生成する時、()と{}を区別しよう

以下の文は全てint型変数を0で初期化する

int x(0);
int y = 0;
int z{0};
int z = {0};

初期化と代入の区別

Widget w1; // デフォルトコンストラクタが呼ばれる
Widget w2 = w1; // コピーコンストラクタが呼ばれる
w1 = w2; // 代入(operator=が呼ばれる)

C++11で追加された{} (braced initialization) はuniform initializationの実装。
{}を使うと、C++11以前はできなかったコンテナの初期化が可能。

std::vector<int> v{1, 2, 3};

初期化方法に違いが生じるケース

C++11では、非staticデータメンバの初期化が可能になったが、()ではできない

class Widget {
private:
    int x{0}; // OK
    int y = 0; // OK
    int z(0); // エラー!
}

std::atomicsなどのコピー不可能なオブジェクトは=で初期化できない

std::atomic<int> ai1{0}; // OK
std::atomic<int> ai2(0); // OK
std::atomic<int> ai3 = 0; // エラー!
{}は組み込み型のnarrowing conversionを禁止する
double x, y, z;
...
int sum1{x + y + z}; // エラー! doubleからintへの変換に非対応
int sum2(x + y + z); // OK double型の値がint型に丸められる
int sum3 = x + y + z; // OK 同上
{}はmost vexing parseを起こさない
Widget w1(10); // 10を引数に取るWidgetのコンストラクタが呼ばれる
Widget w2(); // most vexing parse! Widget型を返す関数の宣言と解釈される
void f(const Widget& w = Widget()); // Widgetのデフォルトコンストラクタが呼ばれる

Widget w1{10}; //コンストラクタが呼ばれる
Widget w2{}; // デフォルトコンストラクタが呼ばれる
void f(const Widget& w = Widget{}); //デフォルトコンストラクタが呼ばれる

{}の直感に反する動作

autoは、{}で初期化されるオブジェクトをstd::initializer_list型と型推定する

auto v1 = -1; // v1はint型
auto v2(-1); // v2はint型
auto v3{-1}; // v3はstd::initializer_list<int>型
auto v4 = {-1}; // v4はstd::initializer_list<int>型

std::initializer_listの無いコンストラクタは通常の動作

class Widget {
public:
    Widget(int i, bool b);
    Widget(int i, double d);
    ...
};

Widget w1(10, true); // 1番目のコンストラクタが呼ばれる
Widget w2{10, true}; // 同上
Widget w3(10, 5.0); // 2番目のコンストラクタが呼ばれる
Widget w4{10, 5.0}; // 同上

しかしstd::initializer_listがある場合、{}は常にそちらを優先する

class Widget {
public:
    Widget(int i, bool b);
    Widget(int i, double d);
    Widget(std::initializer_list<long double> il);
    ...
};

Widget w1(10, true); // 1番目のコンストラクタが呼ばれる
Widget w2{10, true}; // std::init_listのコンストラクタが呼ばれる
                     // 10とtrueはlong doubleに変換される
Widget w3(10, 5.0); // 2番目のコンストラクタが呼ばれる
Widget w4{10, 5.0}; // std::init_listのコンストラクタが呼ばれる
                     // 10と5.0はlong doubleに変換される

narrowing conversionが生じてコンパイルエラーになってもstd::initializer_listを優先する

class Widget {
public:
    Widget(int i, bool b);
    Widget(int i, double d);
    Widget(std::initializer_list<bool> il);
    ...
};

Widget w{10, 5.0}; // narrowing conversionによるコンパイルエラー!

しかし変換が存在しない場合は他の候補を読みに行く

class Widget {
public:
    Widget(int i, bool b);
    Widget(int i, double d);
    Widget(std::initializer_list<std::string> il);
    ...
};

Widget w1(10, true); // 1番目のコンストラクタが呼ばれる
Widget w2{10, true}; // 1番目のコンストラクタが呼ばれる
Widget w3(10, 5.0); // 2番目のコンストラクタが呼ばれる
Widget w4{10, 5.0}; // 2番目のコンストラクタが呼ばれる

空の{}は空のstd::initializer_listではなく引数が無いことを表す

class Widget {
public:
    Widget();
    Widget(std::initializer_list<int> il);
    ...
};

Widget w1; // デフォルトコンストラクタが呼ばれる
Widget w2{}; // デフォルトコンストラクタが呼ばれる
Widget w3(); // most vexing parse! 関数が宣言される
Widget w4({}); // std::init_listのコンストラクタが呼ばれる(リストは空)
Widget w5{{}}; // 同上

コピー・ムーブコンストラクタは通常通り呼ばれる
(Widget型であれば、たとえint型への変換が定義されていても、コピーコンストラクタ・ムーブコンストラクタが呼ばれる)

class Widget {
public:
    Widget(const Widget& rhs);
    Widget(Widget&& rhs);
    Widget(std::initializer_list<int> il);
    operator int() const;
    ...
};

auto w6{w5}; // コピーコンストラクタが呼ばれる
auto w7{std::move(w5)}; // ムーブコンストラクタが呼ばれる

これらのような難解なルールは、std::vectorという身近な例にも影響を与える。

std::vector<int> v1(10, 20); // 20を値とする要素を10個持つvector
std::vector<int> v2{10, 20}; // 10と20の2つの要素を持つvector

クラスデザインのガイドライン

ユーザーが()を使っても{}を使っても同じコンストラクタが呼ばれるデザインが良い。(その点においてstd::vectorは悪い例。)

std::initializer_listを引数に取るコンストラクターが無い状態からそれを新たに加える場合、既存のコードがその新しいコンストラクターを呼び出すように変化する危険性が生じる。
そのような改変は、十分に熟考してから行うべきである。

()と{}の利用ガイドライン

選択肢1: 原則{}を使う

{}は最も広範囲に適用でき、narrowing conversionを防ぎ、C++のmost vexing parseを起こさないという理由で{}を使用する。 ただし、先述のように{}を使用できないいくつかの場合において()を使用する。

選択肢2: 原則()を使う

()はC++98の文法と互換性があり、autoによるstd::initializer_list型推定の問題を回避し、std::initializer_listコンストラクタが意図せず呼び出されることが無いということを重んじて、()を使用する。 ただし、vectorの初期化などのように{}が必要な場面では{}を使用する。

片方の選択肢がもう片方より優れているということはない。
どちらかを選び、一貫した姿勢でコードを書くと良いだろう。

template関数やクラスを設計する場合はさらに事情が複雑になる。

template<typename T, typename... Args>
void doSomeWork(const T& obj, Args&&.. args)
{
    T localObject(std::forward<Args>(args)...);
    T localObject{std::forward<Args>(args)...};
}

これにたとえばユーザーがvectorを入れると、先述のように異なる結果になってしまう。

std::vector<int> v;
...
doSomeWork(v, 10, 20);

std::make_uniqueとstd::make_sharedはこの問題に直面し、()を採用してその旨をインターフェースに記述することで解決した。 他には、タグディスパッチによる解決方法もある。(Item 29)

Things to Remember

  • {}による初期化は最も広く適用可能な初期化であり、narrowing conversionを防ぎ、most vexing parseを起こさない
  • Item 2の通り、autoは{}で初期化されたオブジェクトをstd::initializer_list型と型推定する
  • {}による初期化は、std::initializer_listをパラメータに持つコンストラクタがある場合、たとえ他のコンストラクタの方がマッチしててもそちらが呼ばれる。
  • ()と{}のどちらで初期化するかで違いが生じる例はstd::vectorの2引数コンストラクタ
  • template内のオブジェクト生成において()と{}のどちらを選ぶかという決断は困難になりうる

2014年8月9日土曜日

【読書ノート】Effective Modern C++ - Item 6

O'Reilly Early Releaseで購入したEffective Modern C++のドラフト版の読書記録です。
※この記事はあくまで私の備忘録です。(断りなく自分の解釈や考え・感想を入れたり、理由もなく内容を省略したり、間違いや曲解もあると思います。誤りの指摘は歓迎です。)

Item 6はproxy classに対して使用するtyped initializerイディオムが紹介されていました。 どちらも初見の用語でした。 このイディオムを使用するのは一見無駄に見えましたが、確かにコードに意図を込めるには必要なのかなと思いました。

Item 6: typed initializerイディオムに気をつけよう

autoが意図しない型推定をしてしまうケース

std::vector<bool> features(const Widget& w);

// OK
auto highPriority = features(w)[5];
processWidget(w, highPriority);

// 未定義動作に!
auto highPriority = features(w)[5];
processWidget(w, highPriority);

autoにするとhighPriorityはboolではなくstd::vector<bool>::reference型になる。 bool&型にならない理由は、std::vector<bool>はビットで真偽値を格納するように特殊化されており、bool&のように振る舞う型を返すため。 features(w)はstd::vector<bool>の一時オブジェクトを返すことに注意。 この一時オブジェクトはhighPriorityの初期化の命令文が終わった時点で破棄され、std::vector<bool>::referenceは無効なポインタとなってしまう。

proxy class

std::vector<bool>::referenceは、他の型をエミュレートしたり増強したりする"proxy class"の例。 スマートポインタもproxy class。 proxy classの有用性はデザインパターンの"Proxy"パターンに由来する。 スマートポインタはstd::shared_ptrのように目に見えるproxy、std::vector<bool>::referenceは目に見えないproxy。

以下はExpression templatesもproxy classを使用した例。

Matrix sum = m1 + m2 + m3 + m4;

operator+がMatrixオブジェクトではなくSum<Matrix, Matrix>というproxy classを返すことで無駄な処理を省いて高速化する。 結果として右辺はSum<Sum<Sum<Matrix, Matrix>, Matrix>, Matrix>型となり、それがMatrix型に暗黙の型変換がされる。

一般的に、目に見えないproxy classはautoと相性が悪い。 proxy classのオブジェクトはたいてい一時オブジェクトで、文が終わる時に寿命も尽きてしまうためだ。

目に見えないproxy classが使われているかどうかは、そのライブラリのドキュメントやヘッダーファイルを読めば分かる。

template <class Allocator>
class vector<bool, Allocator> {
    public:
    class reference{ ... };

    reference operator[](size_type n);
    ...
};

typed initializerイディオム

proxy classに対してはtyped initializerイディオムを使うと良い。

auto highPriority = static_cast<bool>(features(w)[5]);
auto sum = static_cast<Matrix>(m1 + m2 + m3 + m4);

このメリットは以下の例のように、戻り値と異なる型を指定しているのが意図的であることをはっきり示す点である。

double calcEpsilon();
float ep = calcEpsilon(); // 誤ってfloatを指定したのかと疑われる
auto ep = static_cast<float>(calcEpsilon()); //意図的にfloatにしたのだと分かる

// dは0.0と1.0の間の小数だとする
int index = d * c.size(); // doubleからintへの変換の意図が弱い
auto index = static_cast<int>(d * c.size()); // こちらの方が意図が伝わる

Things to Remember

  • 目に見えないproxy classはautoに「誤った」型推定をさせてしまう
  • typed initializerイディオムでautoに意図した型を推定させることができる

2014年8月2日土曜日

【読書ノート】Effective Modern C++ - Item 5

O'Reilly Early Releaseで購入したEffective Modern C++のドラフト版の読書記録です。
※この記事はあくまで私の備忘録です。(断りなく自分の解釈や考え・感想を入れたり、理由もなく内容を省略したり、間違いや曲解もあると思います。誤りの指摘は歓迎です。)

第2章はautoについてです。
item 5では、変数宣言にautoを使うとどのようなメリットがあるのかが書かれていました。

Chapter 2 auto

autoのアイデア自体はC++の作者であるBjarne Stroustrupが持っていたが、当時としては冒険的すぎたので眠っていた。
それが30年近く経って、C++11に登場した。
おそらくC++11で最も使用される機能だが、時に直感に反する挙動をするので正しく使用するように注意する必要がある。

Item 5: 目地的な型宣言よりもautoを使おう

メリット1. 変数の初期化忘れを防げる

int x; // 変数が初期化されていない
auto x; // イニシャライザが無いので型推定できずコンパイルエラー
auto x = 0; // OK。xは正しく定義されている

メリット2. ローカル変数の宣言が簡潔に書けるように

// autoを使わない場合
template<typename IT>
void dwim(It b, It e)
{
    while (b != 0) {
        typename std::iterator_traits<It>::value_type
            currValue = *b;
        ...
    }
}

// autoを使った場合
template<typename IT>
void dwim(It b, It e)
{
    while (b != 0) {
        auto currValue = *b;
        ...
    }
}

メリット3. ラムダ式を格納する最も効率的な型として振る舞う

ラムダ式はコンパイラしか知らない型だが、autoを使えばそれを表現できる。

auto derefUPLess =
    [](const std::unique_ptr<Widget>& p1,
       const std::unique_ptr<Widget>& p2)
    { return *p1 < *p2; }

C++14では、パラメータもautoにすることが可能。

auto derefUPLess =
    [](auto& p1,
       auto& p2)
    { return *p1 < *p2; }

std::functionを使って下記のように記述することも可能だが、autoに比べて記述が長くなる上、std::functionには実行速度や使用メモリのオーバーヘッドがある。
また、ヒープを使用する場合もあるので、out-of-memory例外が発生する可能性もある。
autoはstd::bindの結果を格納するのにも有効である。(ただしitem 36にあるように、std::bindよりもラムダ式の方が好ましい。)

std::function<bool(const std::unique_ptr<Widget>&,
                   const std::unique_ptr<Widget>&)>
    derefUPLess = [](const std::unique_ptr<Widget>& p1,
                     const std::unique_ptr<Widget>& p2)
                    { return *p1 < *p2; }

メリット4. 小さい型への代入を防げる

std::vector<int> v;
unsigned sz = v.size();
auto sz = v.size(); // std::vector<int>::size_typeとなる

32bit環境では問題ないが、64bit環境では(32bitである)unsigned型だと問題が起こる可能性がある。
64bit環境ではstd::vector<int>::size_typeは64bit整数であり、unsigned型に入れると32bit整数の最大値を超える整数はオーバーフローにより不正な値になってしまう。

メリット5. 明示的な型指定で起こりやすいミスを防げる

std::unordered_map<std::string, int> m;
...
for (const std::pair<std::string, int>& p : m)
{
    ... // pで何かをする
}

std::unordered_mapのkeyはconstであるという仕様のため、std::pair<const std::string, int>が正しい。
上記の例のようにconst std::pair<std::string, int>&としてしまうと、コンパイラはstd::pair<const std::string, int>からstd::pair<std::string, int>の一時オブジェクトを生成し、それをconst参照にバインドする、という無駄な処理を行ってしまう。
以下のように記述すればこのミスを防げる。

for (const auto& p : m)
{
    ...
}

メリット4とメリット5はどちらも暗黙の型変換ため気づきにくいミスをautoが防いでくれるという例。

以上のようにautoを使用するメリットは複数あるが、いつでもautoさえ使っておけば安心というわけではない。
その例は、Item 2 とItem 6で記述されている。

中にはautoが変数の型を分かりにくくすると主張する人がいる。
しかし、IDEを使う場合は簡単に型を表示できるし、だいたいの型でよければ、変数名を賢く設定すれば分かるものだ。
また、ソフトウェア開発コミュニティではC++以外の型推定を持つ言語や動的型付け言語の実績が数多くあり、商業規模のコードの信頼性や保守性にも十分耐えられると言える。

メリット6. リファクタリングが容易になる

autoを使用している場合、初期化時の型を変更すればautoの部分は自動的に変更が伝播するので、1つの型を変更することで何箇所も修正するという手間が無くなる。

Things to Remember

  • autoは初期化忘れを防ぎ、型指定のミスによる移植性や効率の問題を解消し、リファクタリングを用意し、タイプ数を削減する。
  • ただし、autoの落とし穴には注意する必要がある(Item 2と Item 6で記述)

2014年7月26日土曜日

【読書ノート】Effective Modern C++ - Item 4

O'Reilly Early Releaseで購入したEffective Modern C++のドラフト版の読書記録です。
※この記事はあくまで私の備忘録です。(断りなく自分の解釈や考え・感想を入れたり、理由もなく内容を省略したり、間違いや曲解もあると思います。誤りの指摘は歓迎です。)

Item 4では推定された型を表示するテクニックが書かれていました。 コンパイルエラーメッセージを利用したアイデアは単純で面白いです。 実行時は、プラットフォーム依存であっても__PRETTY_FUNCTION__ or __FINCSIG__を使うやり方が楽だと思いました。

Item 4: 推定された型を見る方法を知ろう

デバッグや実験、単純な疑問、それらのために型推定された型を知りたいことがある。

IDEを使う

auto x = theAnser;
auto y = &theAnser;

autoのところにカーソルを持っていけば表示されるというやつ。IDEのいいところ。

コンパイラの出力

コンパイルエラーで型の情報を見る方法。 定義の書かれていないこんなクラスを宣言し、実体化させる。

template<typename TD>
class TD; // TDは"Type Displayer"の略

TD<decltype(x)> xType;
TD<decltype(y)> yType;

するとTDは定義が無いのでコンパイルエラーになり、その時xとyの型がエラーメッセージの中に表示される。 TD<int>、TD<const int>を実体化できないという文脈で表示される。

実行時の出力

まず思いつくのはtypidからstd::type_info::nameを取り出す方法。(しかし後述の通りこの方法は誤り)

std::cout << typeid(x).name() << '\n';
std::cout << typeid(y).name() << '\n';

しかし読みやすいものが返されることは保証されていない。
GNUとClangはxの型をi、yの型をPKiと表示する。
iはint、Pはポインタ、Kはconstを意味する。
Microsoftのコンパイラはxの型をint, yの型をint const *と表示する。

template<typename T>
void f(const T& param)
{
    using std::cout;
    cout << "T = " << typeid(T).name() << '\n';
    cout << "param = " << typeid(param).name() << '\n';
}

const auto vw = createVec(); // std::vector<Widget>を返すファクトリ関数
if (!vw.empty()) {
    f(&vw[0]); // &vw[0]の型はconst Widget *
}

// 出力結果 (gcc)
// T = PK6Widget
// param = PK6Widget
// ※6はクラス名の文字数を示す

// 出力結果 (MSVC)
// T = class Widget const *
// param = class Widget const *
// "Widget const *"は"const Widget *"と同じ

Tは合っているが、paramは本来const Widget * const &のはずである。
これは、std::type_info::nameが型を値渡しされた時のように扱うため。
参照とconstが取り除かれるため、const Widget *になってしまう。

また、この場合、残念なことにIDEも解読しづらい結果を返してくる。

// Tの型
const
std::_Simple_types<std::_Wrap_alloc<std::_Vec_base_types<Widget,
std::allocator<Widget> >::_Alloc>::value_type>::value_type *

// paramの型
const std::_Simple_types<...>::value_type *const &

typedefを追っていけば実際に指している型が分かるが、長い上にparamの方は...と省略されてしまっている。

一方、コンパイラのエラーメッセージは信頼できる。

TD<T> TType;
TD<decltype(param)> paramType;

とすればコンパイル時に正しい型を表示してくれる。

実行時に正しい型情報を表示するため、std::is_constやstd::is_pointer, std::is_lvalue_referenceなどとtypeidを組合せて自作の表示関数を作るのも手だ。
しかし、プラットフォーム依存であることを許せば簡単な方法がある。

template<typename T>
void f(const T& param)
{
    #if defined(__GNUC__)
        std::cout << __PRETTY_FUNCTION__ << '\n'; // GNUとClang用
    #elif defined(_MSC_VER)
        std::cout << __FUNCSIG__ << '\n'; // Microsoft用
    #endif
}
// 出力結果
// GNU
// void f(const T&) [with T = const Widget*]
// Clang
// void f(const Widget *const &)
// Microsoft
// void __cdecl f<const classWidget*>(const class Widget *const &)

Things to Remember

  • 推定された型はIDEのエディタ上か、コンパイルのエラーメッセージか、typeidか、__PRETTY_FUNCTION__や__FUNCSIG__などの言語拡張で見ることができる。
  • しかしそれらの表示は有用でなかったり正確で無かったりするので、C++標準規格の型推定ルールを理解していることは相変わらず重要。

2014年7月24日木曜日

【読書ノート】Effective Modern C++ - Item 3 [2 / 2]

Item 3の続きです。
以下のC++14の関数にまだ改善の余地があるというお話から。

universal referenceでrvalueをサポートする

template<typename Container, typename Index>
decltype(auto) authAndAccess(Container& c, Index i);

この関数にはrvalueは渡せない。(Container&がconst参照でないため)
rvalueを渡すのはマイナーなケースだが、意味のある例は存在する

std::deque<std::string> makeStringDeque();
// dequeの5番目の要素のコピーを得る文
auto s = authAndAccess(makeStringDeque(), 5);

これをサポートするにはuniversal reference(詳細はItem 26)を使用すれば良い。

template<typename Container, typename Index>
decltype(auto) authAndAccess(Container&& c, Index i);

なお、Indexは標準ライブラリの例(std::string, std::vector, std::dequeなど)が合理的だと思えるため値渡しにしている。
加えてItem27の勧告に従ってuniversal referenceにstd::forwardを適用する。

template<typename Container, typename Index> // C++14の最終版
decltype(auto)
authAndAccess(Container& c, Index i)
{
    authenticateUser();
    return std::forward<Container>(c)[i];
}

template<typename Container, typename Index> // C++11の最終版
auto
authAndAccess(Container& c, Index i)
-> decltype(std::forward<Container>(c)[i])
{
    authenticateUser();
    return std::forward<Container>(c)[i];
}

decltypeの僅かな特殊なケース

decltypeは、変数名に関してはその変数の型を返すが、変数名以上に複雑なlvalueは全てlvalue referenceとして返す。
つまり、変数名以上に複雑な式のT型はT&型と型推定する。
しかしそのようなケースは少ない。(たとえば関数が返すlvalueはlvalue referenceである。さもなくばrvalue)
変数名以上に複雑なlvalueの例は、(x)だ。

int x = 0;
// decltype(x)はint
// decltype((x))はint&

decltype(auto) f1()
{
    int x = 0;
    ...
    return x; // f1の戻り値はint
}

decltype(auto) f2()
{
    int x = 0;
    ...
    return (x); // f2の戻り値はint&
}

f2の戻り値はローカル変数の参照なので、戻り値を受け取ったら未定義動作となってしまう!

注意点はあるが、通常の場合はdecltype(auto)は直感的な動作をする。
特に変数名に適用する場合は、decltypeという名前通り、変数の宣言された型(declared type)を返す。

Things to Remember

  • decltypeはほぼ常に変数や式の型を修正無しに生成する
  • 変数名より複雑なlvalue(たとえば"(x)")のTに対してdecltypeは常にT&を返す
  • C++14はdecltype(auto)をサポートし、decltypeのルールに従って型推定する

2014年7月23日水曜日

C++で数値を文字列に変換する最も高速な方法はBoost.Spirit.Karma

C++で数値を文字列に変換する方法は、sprintf, iostreamのstd::stringstream, boost::lexical_cast, boost::formatなどが有名ですが、最も実行速度の速い方法は他にあり、Boost.Spirit.Karmaを使用する方法です。
Boost.Spirit.Karmaの初歩的な使用方法は以下のソースコードを参考にしてください。
https://gist.github.com/muumu/d8e399c544076301e94d
また、boost公式のパフォーマンス計測が自分にとって不十分だったので、下記の改変したソースコードでパフォーマンス計測を実行しました。
https://gist.github.com/muumu/3b1801456481b447f3a2
ltoaは自分の環境に無いためコメントアウト、代わりにsnprintfを追加。(snprintfは、sprintfより安全で、同程度か僅かに速い関数です。)

パフォーマンス計測結果

前回、小数のフォーマット出力が最も高速なのがBoost.Spirit.Karmaであることを紹介したので、今回は整数値の計測を示します。
処理内容は、ランダム値を表示するものです。
(実行環境: boost: 1.54.0, gcc: 4.8.2 コンパイルオプション: -O3, -std=c++1y, CPU: Core i3 4130T)

Converting 10000000 randomly generated int values to strings.
snprintf (std::string):                 1.06302 [s]
snprintf (char[]):                      0.834953 [s]
iostreams (std::string):                1.23543 [s]
iostreams (std::stringstream):          0.673501 [s]
Boost.Format (std::string):             4.26696 [s]
boost::lexical_cast (std::string):      0.806607 [s]
Karma int_ (std::string):               0.326372 [s]
Karma int_ (char[]):                    0.131378 [s]
Karma (std::string):                    0.254512 [s] // 直接std::stringに出力するkarma
// ()内は出力形式。
// char配列型で()内がstd::stringなものは、出力結果を改めてstd::string型に代入してます

Karmaはsnprintfの実に5倍前後高速な結果となりました。 Boost.Spirit.Karmaが何故こんなに高速なのか、興味が出たので今後調査したいと思っています。

2014年7月20日日曜日

Boost.Spirit.Karmaのフォーマット出力が異常に早い

Comparing the performance of a sequence of several generatorsを見てBoost.Spirit.Karmaのフォーマット出力が優秀だと知ったのですが、最近のgccとboostだとどうなるかと思って自分のLinux環境でも実行してみました。
(実行環境: boost: 1.54.0, gcc: 4.8.2 コンパイルオプション: -O3, -std=c++1y, CPU: Core i3 4130T)
ソースは上記ページのものにsnprintfも加えたものを実行しました。(最近はセキュルティの観点でsprintfよりsnprintfが推奨されるため)
処理内容は、12345.12345という小数2つから[12345.123 12345.123 ]という文字列(小数点以下3桁、14文字分のスペース)を繰り返し生成するものです。
すると、Spirit.Karmaがsprintfの4倍近く速いことが分かりました。(-O2オプションでもほとんど同じ結果でした。)

sprintf:        1.13721
snprintf:       1.12995
iostreams:      1.39023
format:         1.99459
karma:          0.231521
karma (string): 0.318302
karma (rule):   0.294326

※iostremasはstd::stringstream、formatはboost::formatを指しています。

また、元々のソースでは12345.12345が直に書かれていたので、定数の最適化が行われている可能性を考えて標準入力から数値を与えるようにソースを改変してみましたが、結果はほとんど変わりませんでした。
疑いようもなくSpirit.Karmaが速いです。

input a decimal
12345.12345
input one more decimal
12345.12345
sprintf with args:      1.14024
snprintf with args:     1.12748
iostreams with args:    1.38619
format with args:               2.01176
karma with args:                0.228151
karma (string) with args:       0.314551
karma (rule) with args: 0.293565

なお、数値の桁数が小さい時は差が縮まりました。

input a decimal
1.0
input one more decimal
2.0
sprintf with args:      0.412689
snprintf with args:     0.393523
iostreams with args:    0.640817
format with args:               1.2292
karma with args:                0.263695
karma (string) with args:       0.327404
karma (rule) with args: 0.307239

Boost.Spirit.Karmaはコードが複雑でコンパイル時間も増えるのでカジュアルに使うのには向かないですが、処理速度を求める時には最適なようです。
カジュアルに使う場合は、処理速度は遅いですがsprintf風の記法で型安全でもあるboost::formatが使いやすいですね。
std::stringstreamは、数値を入れる時に余計なバッファを確保するので遅いようです。(boost::formatよりは速いですが。)

ちなみに、int型をstd::string型に変換するのみの場合でもSpirit.Karmaは速いようです。
C++ Convert Int to String Speed
これを見てもうひとつ分かるのは、boost::lexical_castがsprintf以上に速いということです。
カジュアルに数値を文字列に変換するだけなら、boost::lexical_castがベストチョイスになりそうです。
(C++11以降ではstd::to_stringという選択肢もありますが、こちらの処理速度等の詳細は未確認です。)

【読書ノート】Effective Modern C++ - Item 3 [1 / 2]

O'Reilly Early Releaseで購入したEffective Modern C++のドラフト版の読書記録です。
※この記事はあくまで私の備忘録です。(断りなく自分の解釈や考え・感想を入れたり、理由もなく内容を省略したり、間違いや曲解もあると思います。誤りの指摘は歓迎です。)

Item 3はdecltypeについてです。
自分はC++11の関数の戻り値を後ろに書く記法とC++14のdecltype(auto)をちゃんと知らなかったので、学ぶところが多かったです。

Item 3: decltypeを理解しよう

decltypeは名前や式の型を教えてくれる。
典型的の場合は予想通りの動きをしてくれるが、時には予想に反する動きをする。

典型的な場合

const int i = 0; // decltype(i)はconst int
bool f(const Widget& w); // decltype(w)はconst Widget&
    // decltype(f)はbool(const Widget&)

struct Point {
    int x, y; // decltype(Point::x)はint。Point::yも同様。
};

Widget w; // decltype(w)はWidget

if (f(w)) ... // decltype(f(w))はbool

template<typename T>
class vector {
public:
    ...
    T& operator[](std::size_t index);
    ...
};

vector v; // decltype(v)はvector
...
if (v[0] == 0) ... // decltype(v[i])はint&

関数の戻り値の推定

C++11の場合

主なdecltypeの使用場所はtemplate関数で、テンプレートパラメータに依存する型を返したい時だ。

template<typename Container, typename Index>
auto authAndAccess(Container& c, Index i) // 改善の余地あり(後述)
    -> decltype(c[i])
{
    authenticateUser();
    return c[i];
}

これにより、c[i]が何の型を返そうともそれをそのまま関数の戻り値とすることができる。
なお、この関数名の前のautoは型推定とは関係ない。
このautoは単にC++11のtrailing return type syntax(戻り値を後置する記法)が使用されることを示す。
この後置のメリットは、戻り値の型の計算に関数パラメータを使用できること。
(上記例ではcとi。後置を使わないと、cとiは利用できない。)

C++14の場合

C++14では、以下のようにautoだけで戻り値の型推定が行われるようになった。
(この場合はもちろん、autoは型推定を示すキーワードとなる。)

template<typename Container, typename Index> // C++14の記法 + 改善の余地あり
auto authAndAccess(Container& c, Index i)
{
    authenticateUser();
    return c[i]; // 戻り値の型がc[i]から推定される
}

しかしこの場合、型推定のルールは?autoとdecltypeのどちらかのもの?
驚くかもしれないが、答えはテンプレートの型推定のルールになる。
ただ、初期化リスト{}を除けばautoとテンプレートの型推定のルールは同じだ。
しかし今回の場合はこのルールだと問題になる。
operator[]は大抵参照を返すが、テンプレートの型推定では参照を取り除いてしまうからだ。
従って、以下のコードはコンパイルエラーになる。

std::deque d;
...
authAndAccess(d, 5) = 10; // コンパイルエラー

これは、d[5]はint&を返すがテンプレートの型推定によって戻り値の型はintになるため。
すると、rvalueに10を代入しようとしていることになるため、コンパイルエラーとなる。
この問題は、decltype(auto)指定子によってdecltypeの型推定をさせるようにすれば解決する。

template<typename Container, typename Index> // C++14の記法 + 改善の余地あり
decltype(auto)
authAndAccess(Container& c, Index i)
{
    authenticateUser();
    return c[i];
}

変数宣言にdecltype

decltype(auto)は変数宣言にも有効な場面がある。

Widget w;
const Widget& cw = w;
auto myWidget1 = cw; // myWidget1の型はWidget
decltype(auto) myWidget2 = cw; // myWidget2の型はconst Widget&

authAndAccessで改善の余地があるとされていることと、decltypeの型推定の典型的でない場合については、次回の記事で。

2014年7月17日木曜日

【読書ノート】Effective Modern C++ - Item 2

O'Reilly Early Releaseで購入したEffective Modern C++のドラフト版の読書記録です。
※この記事はあくまで私の備忘録です。(断りなく自分の解釈や考え・感想を入れたり、理由もなく内容を省略したり、間違いや曲解もあると思います。誤りの指摘は歓迎です。)

Item 2は、Item 1を理解していればすぐ読み終わる内容でした。
ただ、auto x = {27};でxがinitializer_list型になるのは知らなかったので新鮮でした。

Item 2: autoの型推論を理解しよう

autoの型推論は、1つの例外をのぞいてテンプレートの型推定と同じ。

autoをテンプレート関数のパラメータの型だと思えば同じだと分かる。

auto x = 27; // case 3 (xはポインタでも参照でもない)、xはint
const auto cx = x; // 同上、cxはconst int
const auto& rx = x; // case 1 (rxはuniversalでないreference)、rxはconst int&
auto&& uref1 = x; // xはintでlvalueなので、uref1の型はint&
auto&& uref2 = cx; // cxはconst intでlvalueなので、uref2の型はconst int&
auto&& uref3 = 27; // 27はintでrvalueなので、uref3の型はint&&

const char name[] = "R. N. Briggs"; // nameの型はconst char[13]
auto arr1 = name; // arr1の型はconst char*
auto& arr2 = name; // arr2の型はconst char(&)[13]

void someFunc(int, double); // someFuncは関数で、型はvoid(int, double)
auto func1 = someFunc; // func1の型はvoid (*)(int, double)
auto func2 = someFunc; // func2の型はvoid (&)(int, double)

ここまではItem 1と本質的に同じ。

autoとテンプレートの型推定のただ1つの違い

以下の4つは皆int型変数を27で初期化する文。
(下の2つはC++11で導入されたuniform initialization)

int x1 = 27;
int x2(27);
int x3 = {27};
int x4{27};

しかし、autoを使うと同じでなくなる。

auto x1 = 27; // int型で、値は27
auto x2(27); // 同上
auto x3 = {27}; // std::initializer_list<int>型で、値は{27}
auto x4{27}; // 同上

以下の文はstd::initializer_list<T>のTを推定できないためコンパイルエラーになる。

auto x5 = {1, 2, 3.0};

まずx5は{}の存在によりstd::initializer_listと推定される。
次に、std::initializer_listはテンプレートなのでstd::initializer_list<T>のTが決まらなければならない。
しかし、{}内の数値型が同じではないのでTが推定できずコンパイルエラーとなる。
この{}による初期化のみがテンプレートとautoの型推定の違い。

なぜこのような仕様になっているか、説得力のあるな説明はない。ルールである。
この落とし穴のため、{}による初期化はそうしなければならない時のみに使用する開発者もいる。
({}を使わなければならない場面はItem 7に説明がある。)

C++11の範囲では話は以上だが、C++14では続きがある。
C++14ではラムダ式でautoが使える。
そしてこのautoはテンプレートの型推定を行う。({}をstd::initializer_listとは推定しない。)
従って、下記のコードは{ 1, 2, 3 }の型を推定できないのでコンパイルに失敗する。

auto createInitList()
{ return { 1, 2, 3 }; // コンパイルエラー } std::vector v; ... auto resetV = [&v](const auto& newValue) { v = newValue; } ... resetV( { 1, 2, 3 } ); // コンパイルエラー

Things to Remember

  • autoの型推定は通常テンプレートの型推定と同じ
  • 唯一の例外はautoと{}による初期化を使用した変数宣言で、そこでautoはstd::initializer_list型を推定する
  • テンプレートの型推定は{}による初期化リストを与えられると失敗する

【読書ノート】Effective Modern C++ - Item 1 [2 / 2]

今回はItem 1の残り、配列型と関数ポインタ型の型推定です。
配列型は、自分でも関数に渡すとポインタ型に退化してしまって要素数が取れなくなるのを不便に感じたことがあるのですが、テンプレートで参照渡しにするとその問題が解決するのを不思議に思っていましたが、今回の説明を読んでそれが仕様通りの現象であることを理解できました。

配列引数

配列型とポインタ型は同一視されがちだが、実は違う
配列のポインタ型は、配列型が配列の先頭要素へのポインタに壊変したもの

const char name[] = "J. P. Briggs"; // nameの型はconst char[13]
const char * ptrToName = name; 配列型はポインタ型に壊変した

しかし、パラメータ型は強制的にポインタ型に壊変してしまう
void myFunc(int param[]);
これは以下の宣言と同じになってしまう
void myFunc(int* param);

これはC++がC言語を元としているため生じたもの。
下記のように値渡しのテンプレート関数では、配列を渡すとポインタ型になる。
template <typename T>
void f(T param); // 値渡しのテンプレート関数

f(name); // nameはconst char[13]型だが、const char *型と型推定される
しかし、参照渡しのテンプレート関数では、配列の参照型にできる

template <typename T>
void f(T& param); // paramはconst char (&)[13]型となる

f(name); // nameはconst char [13]型
これを使うと、コンパイル時定数として配列の要素数を返すconstexpr関数を定義できる
template<typename T, std::size_t N>
constexpr std::size_t arraySize(T (&)[N]) {
    return N;
}

int keyVals[] = { 1, 3, 7, 9, 11, 22, 35 }; 

int mappedVals[arraySize(keyVals)]; 

関数引数

配列型と同じように、関数型も関数ポインタ型に壊変する

void someFunc(int, double); // 型はvoid(int, double)

template<typename T>
void f1(T param); // 値渡し

template<typename T>
void f2(T& param); // 参照渡し

f1(someFunc); // 値渡しは関数ポインタ型void (*)(int, double)に。

f2(someFunc); // 参照渡しは関数の参照型void (&)(int, double)に。

Things to Remember

  • ポインタ型とUniversa Referenceでない型のパラメータでは、引数が参照型かどうかは無視される
  • パラメータがUniversal Referenceの場合は、lvalueはlvalue referenceに、rvalueはrvalue referenceになる
  • パラメータが値渡しの場合、引数が参照型かどうか、constかどうかは無視される
  • 配列型と関数型の引数は、パラメータが参照型でない限り、ポインタ型に壊変する

2014年7月16日水曜日

【読書ノート】Effective Modern C++ - Item 1 [1 / 2]

O'Reilly Early Releaseで購入したEffective Modern C++のドラフト版の読書記録です。

※この記事はあくまで私の備忘録であり、本の内容のまとめではないことに注意してください!(断りなく自分の解釈や考え・感想を入れたり、理由もなく内容を省略したり、間違いや曲解もあると思います。誤りの指摘は歓迎です。)

Chapter 1では型推定、Item 1ではテンプレートの型推定が出てきました。直感的に使えますが、ちゃんとしたルールは知らなかったので勉強になりました。また、Universal Referenceという用語は初耳で、挙動も知らなかったのでこちらも勉強に。

Chapter 1 型推定

C++98から関数テンプレートでは型推定が行われていた。
C++11ではautoとdecltypeが加わり、C++14ではその2つを使用できる文脈が増えた。
型推定は、型を変更する時に1箇所変えるだけで他の部分にも伝播するため、手で複数箇所を変更する手間から解放してくれる。
この章は型推定の基本的な情報を提供する。

Item1: テンプレートの型推定を理解しよう

テンプレートの型推定は、詳細な仕組みを知らなくても直感的に使える優れたシステムの一例。
autoで宣言された変数の型推定は、テンプレートの型推定と本質的には同じ。
しかし、仕組みを理解していないと面食らうこともあるだろう。
template <typename T>
void f(const T& param); // パラメータの型はconst T&

int x = 0;
f(x); // 引数型はint
ここではTはint型と推定されているが、パラメータの型はconst int&と推定されている
※復習: paramはパラメータ、xは引数(パラメータと引数の区別)

Case 1: パラメータの型がポインタ/参照だが、Universal Referenceではない場合

  • 引数の型が参照の場合、参照を無視する 
  • 引数の型をパラメータの型とパターンマッチすることでTを決定する
template<typename T>
void f(T& param);

int x = 27;
const int cx = x;
const int& rx = x;

f(x); // Tはint、パラメータの型はint&
f(cx); // Tはconst int、パラメータの型はconst int&
f(rx); // Tはconst int、パラメータの型はconst int&
注目すべき点
  • 引数のconst性は保たれるので安全に使える
  • 参照を渡しても、(C++では禁止されている)参照の参照にはならない(自動的に余計な参照が除かれる)
パラメータがconst参照になった場合
template<typename T>
void f(const T& param);

f(x); // Tはint、パラメータの型はconst int&
f(cx); // Tはint、パラメータの型はconst int&
f(rx); // Tはint、パラメータの型はconst int&
自然なものとして納得できる
パラメータ型がポインタ型の場合も本質的に同様。
template<typename T>
void f(T* param);

int x = 27;
const int *px = &x;

f(&x); // Tはint、パラメータの型はint*
f(px); // Tはconst int、パラメータの型はconst int*

Case2: パラメータの型がUniversal Referenceの場合

Universal Referenceというのは初耳だったのでググったところ一番上に著者自身の説明が出てきました。
http://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers
Item26でも説明があるようです。
rvalue referenceと同じ見た目ですが、とりあえず型推定の時のみ登場するのでT&&とかauto&&がUniversal referenceだと当面の間は思っておけばいいっぽいです。
それで、その具体的な挙動については以下の通り、lvalueを引数として受けたっ時に挙動が直感的でないので要注意。

ルール
  • 引数がlvalueの時、Tとパラメータの型はlvalue referenceとなる
  • 引数がrvalueの時、通常の型推定のルールが適用される
template<typename T>
void f(T&& param);

int x = 27;
const int cx = x;
const int& rx = x;

f(x); // xはlvalueなので、Tはint&、パラメータの型もint&
f(cx); // cxはlvalueなので、Tはconst int&、パラメータの型もconst int&
f(rx); // rxはlvalueなので、Tはconst int&、パラメータの型もconst int&
f(27); // 27はrvalueなので、Tはint、パラメータの型はint&&

Case 3: パラメータ型がポインタでも参照でも無い場合

つまり、値渡しになる場合。

template<typename T>

void f(T param);

paramは引数のコピー、完全に新しいオブジェクトとなる。

ルール
  • 引数の型が参照の時、参照部分は無視される
  • 引数の型(参照の場合は参照を除去されたのち)constもvolatileも無視される
int x = 27;
const int cx = x;
const int& rx = x;

f(x); // Tもパラメータもint
f(cx); // Tもパラメータもint
f(rx); // Tもパラメータもint
パラメータは引数とは独立な新しいオブジェクトなので、const性は無視しても問題ないためこうなっている。
ただし、constが無視されるのは値渡しのパラメータ(今回のケース)の場合のみであることは重要。
template<typename T>
void f(T param);

const char* const ptr = "Fun with pointers";

f(ptr);

ptrはconstオブジェクトに対するconstポインタなので、アドレスも変更できないし値をnullにすることもできない。
これをfに渡した時、ポインタが値渡しとなるためTはconst char*となる。
つまり、ポインタに対するconstは失われ、ポインタの先のオブジェクトのconstは残る。

【読書ノート】Effective Modern C++ - Item 1 [2 / 2]

2014年7月14日月曜日

【読書ノート】Effective Modern C++ - Introduction

O'Reilly Early Releaseで購入したEffective Modern C++のドラフト版の読書記録です。

※この記事はあくまで私の備忘録であり、本の内容のまとめではないことに注意してください!(断りなく自分の解釈や考え・感想を入れたり、理由もなく内容を省略したり、間違いや曲解もあると思います。誤りの指摘は歓迎です。)

Introductionでは、C++11で新たに登場した概念、特に(私のような)初心者がつまずきやすいとこを中心に用語説明が書かれていました。

C++11での大きな変更

  • auto
  • range-based for文
  • lambda式
  • rvalue reference
  • 新しい非同期処理
さらに他にも
  • 0がnullptrに
  • typedefがalias declarationsに
  • Enums should now be scoped
  • スマートポインタが生ポインタより好ましいものに
  • 通常、オブジェクトのmoveがcopyよりもいいように

とにかくC++11には学ぶべきものがたくさんある。さらにC++14まで取り入れたらより多くなる。
そしてそれ以上に重要なことは、新しい機能を効果的に使うために学ぶべきことがたくさんあることだ。
C++11、C++14の機能・特徴については既に情報が溢れている。
この本の目的は、それらの機能を正しく、効率的に、保守性・移植性を保つように使いこなすことだ。
注意点として、この本の各itemはガイドラインであってルールではない。
ガイドラインに例外はつきものなので、最も重要なのはアドバイスの背後にある論理。

用語と慣例

ISO標準規格のバージョン: C++98, C++03, C++11, C++14
C++03はC++98のマイナーバージョンアップなので、この本では同一視する
C++14はC++11の上位互換なので、C++11と言ったらC++14の内容も指すことにする
C++14と書いた場合はC++14のみを指す
単にC++と書いた場合は全てのバージョンで成立することを指す

lvalueとrvalue

lvalueとrvalueを見分ける経験則はアドレスを取れるかどうか
Widget(Widget&& rhs); // rhsはrvalue reference型を持っているが自身でアドレスを持っているのでlvalue

ムーブセマンティクスの登場で変わった初期化コスト

void someFunc(Widget w);
この関数宣言を見ただけでは、Widget wの初期化にどの程度計算コストがあるかは分からない
lvalueが渡されたらコピーコンストラクタによって初期化されるし、rvalueならムーブコンストラクタで初期化される

パラメータと引数の区別

void someFunc(Widget w); // wは関数のパラメータ (parameter)
Widget wid;
someFunc(wid); // widは関数の引数 (argument)
someFunc(std::move(wid)); // std::move(wid)は関数の引数
パラメータはlvalue、引数はlvalueかもしれないしrvalueかもしれない

例外安全(exception-safety)の概念

よく設計された関数はexception-safeである。
basic guaranteeでは、例外が発生してもデータ破壊やリソースリークが生じないことを保証する
strong guaranteeでは、例外が発生しても(その関数が)呼び出される前と状態が変わらないことを保証する

callable entity

callable entityとは、非メンバ関数呼び出しの形式で呼び出すことのできるあらゆるものを指す。(関数、関数ポインタ、関数オブジェクトなど。)
ラムダ式で生成された関数オブジェクトはクロージャーとして知られているが、この本では区別せずラムダ式と呼ぶ。

deprecate

基本的にC++は後方互換性を保っているが、C++11でdeprecateされたものもある
deprecateは今後の規格で取り除かれる可能性があることを示す
たとえばstd::auto_ptrはstd::unique_ptrの方が良いのでdeprecatedとなった

ctor: constructor(コンストラクタ)の略
dtor: destructor(デストラクタ)の略

【読書ノート】Effective Modern C++ - Item 1 [1 / 2]

2014年7月12日土曜日

Effective Modern C++ のドラフトバージョンが販売開始

Scott Meyers氏によるEffective Modern C++のドラフトバージョンが本日公開されました。
http://scottmeyers.blogspot.jp/2014/07/draft-version-of-effective-modern-c-now.html

pdf版がO'Reilly の Early Releaseにて$42.99で販売されています。
http://shop.oreilly.com/product/0636920033707.do
こちらはクレジットカードやPayPalで購入でき、購入後は正式版が公開された場合も無料で再ダウンロードすることが可能です。
私も早速購入しました。
Safari Books Onlineからも購入できるようです。

このEffective Modern C++より前から出ているEffective C++は、日本で翻訳本も販売されている有名なC++の参考書です。
より良いデザインで、問題を抑えて効率的に動くC++の書き方について、実際にC++コードを書く初心者向けに解説された良本でした。
しかしC++はC++11やC++14など最近の規格で新たな文法・機能が追加されており、だいぶ様変わりしてきています。
今回のEffective Modern C++ではそこをカバーしてくれると期待しています。

以下、Scott Meyers氏のブログより注意点。

  • フルーカラーであるのが特徴だが、最終版までに修正が入るので今回のバージョンの見た目が最終版のものとは思わないで欲しい
  • このバージョンは6週間前に書かれた状態のもので、既にレビュアーから全体の70%についてコメントを受けており、問題がある
  • 問題には、誤字や文法ミスといった細かいものもあれば、どう述べたら良いか慎重に考えるべき重要な技術的欠陥もある
  • 自分は普段できるだけベストな状態にしてから出版するようにしているので、今回のドラフトバージョンを公開するのは少々緊張している
  • 読者の方で、改善すべき点を見つけた方は、気軽にO'ReillyかSafari onlineのエラーレポートシステムかEメールで知らせて欲しい
  • 以上、抜粋です。

    O'Reillyの電子書籍はpdf形式なので、好きな端末で読めるのが嬉しい点です。早速読んでいこうと思います。

    追記: 読み始めました。
    【読書ノート】Effective Modern C++ - Introduction

    2014年3月2日日曜日

    SFINAEの書き方3種類

    SFINAE (Substitution Failure Is Not An Error)でSubstitutionを行う場所は関数のsignatureならどこでも使えるので、以下のように3種類(template引数、関数引数、戻り値)の書き方が可能です。

    #include <iostream>
    #include <type_traits>
    #include <string>
    
    // 書き方1 (template引数)
    template <typename T, class = typename std::enable_if<std::is_fundamental<T>::value>::type>
    void f(T t) {
        std::cout << "fundamental type : " << t << std::endl;
    }
    
    template <typename T, class = typename std::enable_if<!std::is_fundamental<T>::value>::type>
    void f(const T& t) {
        std::cout << "not fundamental type : " << t << std::endl;
    }
    
    // 書き方2 (関数引数)
    template <typename T>
    void g(T t, typename std::enable_if<std::is_fundamental<T>::value>::type* = 0) {
        std::cout << "fundamental type : " << t << std::endl;
    }
    
    template <typename T>
    void g(const T& t, typename std::enable_if<!std::is_fundamental<T>::value>::type* = 0) {
        std::cout << "not fundamental type : " << t << std::endl;
    }
    
    // 書き方3 (戻り値)
    template <typename T>
    typename std::enable_if<std::is_fundamental<T>::value, void>::type h(T t) {
        std::cout << "fundamental type : " << t << std::endl;
    }
    
    template <typename T>
    typename std::enable_if<!std::is_fundamental<T>::value, void>::type h(const T& t) {
        std::cout << "not fundamental type : " << t << std::endl;
    }
    
    int main() {
        int a = 0;
        std::string str = "string";
        f(a); // fundamental type : 0
        f(str); // not fundamental type : string
        g(a); // fundamental type : 0
        g(str); // not fundamental type : string
        h(a); // fundamental type : 0
        h(str); // not fundamental type : string
    }
    

    いずれも、std::enable_if::typeは存在、std::enable_if::typeは存在しない(falseの時に特殊化されたenable_if内でtypeが定義されていない)ことをSFINAEに利用しています。

    1番目のtemplate引数に与える方法では、無名のクラスにデフォルトtemplate引数を与えています。
    2番目の引数に与える方法では、enable_ifのtypeのポインター型にヌルポインターのデフォルト引数を与えています。
    3番目の戻り値を使う方法では、typename std::enable_if::typeがT型であることを利用しています。

    どの書き方もWebや書籍で見たことがあるので、どれを使用するかは好みでいいのだと思います。

    2014年3月1日土曜日

    SFINAEで可変個引数 (ellipsis) が使われる理由

    特定の型を持っているかをbool値で返す関数を作る時に使用するSFINAE (Substitution Failure Is Not An Error) では、以下のコードのように可変個引数 (可変長引数, ellipsis)が使われることがあります。

    // 下記サイトより引用
    // http://debugfailure.wordpress.com/2009/10/06/understanding-sfinae/
    template <typename T>
    class is_container
    {
        typedef char true_type;
        struct false_type{ true_type _[2]; };
        template <typename U>
        static true_type has_iterator_checker(typename U::iterator *);
        template <typename U>
        static false_type has_iterator_checker(...);
    public:
        enum { value = (sizeof(has_iterator_checker<T>(0)) ==
                        sizeof(true_type)) };
    };
    

    ellipsisは型の情報を失うのでC++では邪悪なものとして扱われていますが、上記のSFINAEでは可変個引数の関数は中身を実行しないので問題ありません。 とはいえなぜellipsisにする必要があるのでしょう。 それは、関数オーバーロードの優先順位が理由です。 例えば下のようにellipsisを使用せずに書いてみます。

    #include <iostream>
    #include <vector>
    
    template <typename T>
    void func(const T&, typename T::iterator* = 0) {
        std::cout << "container\n";
    }
    
    template <typename T>
    void func(const T&) {
        std::cout << "not container\n";
    }
    
    int main() {
        func(1);
        func(std::vector<int>());
    }
    

    この場合、func(1)は問題なく2番目のfuncが呼び出される一方、func(std::vector())の場合は両方とも関数のsignatureにマッチするので、どちらを呼び出せばいいのかわからず、"call of overloaded 'func(std::vector)' is ambiguous"というコンパイルエラーになってしまいます。

    そこで必要になるのがellipsisで、これについては言語仕様で他にオーバーロード解決できる関数が無い時に限り、ellipsisにマッチする、と定められています。

    N3337: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf
    13.3.2 Viable functionsに
    A candidate function having fewer than m parameters is viable only if it has an ellipsis in its parameter list (8.3.5). For the purposes of overload resolution, any argument for which there is no corresponding parameter is considered to “match the ellipsis” (13.3.3.1.3) .

    と書かれています。 そういうわけで、SFINAEではellipsisが使われているんですね。

    2014年1月5日日曜日

    C++で数値を2進数表記で出力する方法 (コンパイル時編)

    実行時に数値のビット列を出力する方法を前回紹介しました。
    http://code-mynote.blogspot.jp/2014/01/c2.html
    ここでは、コンパイル時にビット列の文字列定数を用意したい場合(他のメタ関数に渡したり、実行時の文字列生成コストが無視できない場合など)を考えます。
    自分の経験では今までそんな場合は無かったのですが、boost::mplの勉強を兼ねて下記のメタ関数を作ってみました。

    #include <boost/mpl/string.hpp>
    #include <boost/mpl/int.hpp>
    #include <boost/mpl/integral_c.hpp>
    #include <boost/mpl/bitwise.hpp>
    
    namespace mpl = boost::mpl;
    
    template <typename c>
    struct bin_string {
        typedef typename boost::mpl::push_back<
            typename bin_string<typename mpl::shift_right<c, mpl::int_<1> >::type>::type,
            boost::mpl::char_<'0' + (c::value & 1)>
            >::type type;
    };
    template <typename T>
    struct bin_string<mpl::integral_c<T, 0> > : boost::mpl::string<> {};
    
    template <long long unsigned N>
    struct bin_string_c : bin_string<mpl::integral_c<long long unsigned, N> > {};
    
    

    処理内容

    bin_stringがメタ関数の本体、bin_string_cが実行時に呼び出すメタ関数の転送役です。
    templateを使用しているので見た目が少々違いますが、やってることは前回実装したto_bin_strと同じです。
    数値を1つずつ右にシフトし、右端のビットを0か1の文字に変換することを数値が0になるまで(1のビットが無くなるまで)繰り返します。
    再帰で実装しているので左端のビットが出力文字列でも左端となり、最後に文字列を反転する必要がありません。

    使用例

    メタ関数の返す文字列はmpl::c_str<>に渡すことでコンパイル時にconst char[]文字列が生成されます。
    長いので、最初だけフルの名称で呼び出し、後はマクロ定義の呼び出しをしています。
    下段のformat_bin_stringは表示桁を揃えたい場合に使用するメタ関数です。(ソースは後述)

    #include <iostream>
    
    namespace mpl = boost::mpl;
    
    #define BINSTR(x) mpl::c_str<bin_string_c<x>::type >::value
    #define FBINSTR(x, n) mpl::c_str<format_bin_string<bin_string_c<x>::type, n>::type>::value
    
    int main() {
        const char x_c = 4;
        const unsigned y_c = 11;
        const int z_c = 103;
        const unsigned long long w_c = 4527856554780ULL;
        std::cout << mpl::c_str<bin_string_c<x_c>::type >::value << std::endl;
        std::cout << BINSTR(y_c) << std::endl;
        std::cout << BINSTR(z_c) << std::endl;
        std::cout << BINSTR(w_c) << std::endl;
    
        std::cout << mpl::c_str<format_bin_string<bin_string_c<x_c>::type, n>::type>::value << std::endl;
        std::cout << FBINSTR(y_c, n) << std::endl;
        std::cout << FBINSTR(z_c, n) << std::endl;
        std::cout << FBINSTR(w_c, n) << std::endl;
    }
    

    出力結果

    100
    1011
    1100111
    1000001111000111001010010000001001100011100
    00100
    01011
    00111
    11100
    

    メタ関数format_bin_stringのソース。

    
    template <typename Sequence1, typename Sequence2>
    struct concat :
        mpl::insert_range<Sequence1, typename mpl::end<Sequence1>::type, Sequence2> {};
    
    template <int N>
    struct zero_string :
        mpl::push_back<typename zero_string<N-1>::type, mpl::char_<'0'> > {};
    template <>
    struct zero_string<0> : mpl::string<> {};
    
    template <typename Sequence, int DigitNum, bool greater = (DigitNum >= mpl::size<Sequence>::value)>
    struct format_bin_string {};
    
    template <typename Sequence, int DigitNum>
    struct format_bin_string<Sequence, DigitNum, true> :
        concat<
            typename zero_string<DigitNum - mpl::size<Sequence>::value>::type,
            Sequence
        > {};
    
    template <typename Sequence, int DigitNum>
    struct format_bin_string<Sequence, DigitNum, false> :
        mpllib::tail_n_c<Sequence, DigitNum> {};
    

    mpllib::tail_n_cのソース。

    #include <boost/mpl/size.hpp>
    #include <boost/mpl/advance.hpp>
    #include <boost/mpl/erase.hpp>
    #include <boost/mpl/eval_if.hpp>
    #include <boost/mpl/int.hpp>
    #include <boost/mpl/less.hpp>
    #include <boost/mpl/minus.hpp>
    
    namespace mpllib {
    namespace mpl = boost::mpl;
    
    template <typename Sequence, typename N>
    struct erase_head_n {
        typedef typename mpl::begin<Sequence>::type begin_iter;
        typedef typename mpl::advance<begin_iter, N>::type cut_iter;
        typedef typename mpl::erase<Sequence, begin_iter, cut_iter>::type type;
    };
    
    template <typename Sequence, typename N>
    struct tail_n {
        typedef typename mpl::eval_if<
            mpl::less<mpl::size<Sequence>, N>,
            Sequence,
            erase_head_n<Sequence, mpl::minus<mpl::size<Sequence>, N> >
        >::type type;
    
    };
    
    template <typename Sequence, int N>
    struct tail_n_c {
        typedef typename tail_n<Sequence, mpl::int_<N> >::type type;
    };
    
    }; // end of namespace mpllib
    

    C++で数値を2進数表記で出力する方法(実行時編)

    ビットマスクやデータ構造としてビット列を使用する場合は数値を2進数表記で確認したいことがよくあります。 一番手っ取り早い方法はstd::bitsetでキャストする方法です。

    #include <iostream>
    #include <bitset>
    
    int main() {
        unsigned x = 11;
        std::cout << static_cast<std::bitset<8> >(x) << std::endl;
    }
    // 出力結果: 00001011
    

    文字列にしたい場合はstd::stringstreamに入れて、str()を呼びます。

    #include <iostream>
    #include <sstream>
    #include <bitset>
    
    int main() {
        unsigned x = 11;
        std::stringstream ss;
        ss << static_cast<std::bitset<8> >(x);
        std::string x_bin = ss.str();
        std::cout << x_bin << std::endl;
    }
    // 出力結果: 00001011
    

    桁数指定したくない場合

    桁数を自分で指定したくない、先頭のゼロが邪魔という場合は、以下の関数を用意します。

    #include <string>
    #include <algorithm>
    
    template <typename T>
    inline std::string to_bin_str(T n) {
        std::string str;
        while (n > 0) {
            str.push_back('0' + (n & 1));
            n >>= 1;
        }
        std::reverse(str.begin(), str.end());
        return str;
    }
    

    処理内容

    右端のビットから順に1か0かを調べます。
    n & 1とすることで右端のビットを0か1かで抽出し、'0'に加算して'0'か'1'に変換します。
    これをstringの末尾に追加していくのですが、このままだと文字列が逆順になってしまうので最後にstd::reverseで文字列を反転します。

    使用例

    #include <iostream>
    
    int main() {
        char x = 4;
        unsigned y = 11;
        int z = 103;
        unsigned long long w = 4527856554780ULL;
        std::cout << to_bin_str(x) << std::endl;
        std::cout << to_bin_str(y) << std::endl;
        std::cout << to_bin_str(z) << std::endl;
        std::cout << to_bin_str(w) << std::endl;
    }
    

    出力結果

    100
    1011
    1100111
    1000001111000111001010010000001001100011100
    

    実行時の値で桁を揃えたい場合

    std::bitsetによる出力では桁数指定にコンパイル時定数が必要でしたが、実行時の値で桁を揃えたい場合は以下の関数で対応できます。(桁が多い場合は先頭に0埋め、足りない場合は上位ビットを切り捨てます)

    #include <string>
    #include <algorithm>
    
    template <typename T>
    inline std::string to_bin_str(T n, std::size_t digit_num) {
        std::string str;
        while (n > 0) {
            str.push_back('0' + (n & 1));
            n >>= 1;
        }
        str.resize(digit_num, '0');
        std::reverse(str.begin(), str.end());
        return str;
    }
    

    使用例

    #include <iostream>
    
    int main() {
        char x = 4;
        unsigned y = 11;
        int z = 103;
        unsigned long long w = 4527856554780ULL;
        std::size_t n;
        std::cin >> n;
        std::cout << to_bin_str(x, n) << std::endl;
        std::cout << to_bin_str(y, n) << std::endl;
        std::cout << to_bin_str(z, n) << std::endl;
        std::cout << to_bin_str(w, n) << std::endl;
    }
    

    出力結果 (cinでは5を入力)

    00100
    01011
    00111
    11100
    

    コンパイル時に2進数表記文字列を生成する方法は次の記事へ。
    http://code-mynote.blogspot.jp/2014/01/c2_5.html

    2014年1月4日土曜日

    C++で64bit整数に対応したビットやビットマスク定数の生成

    C++でビットの定数を書く時に、いちいち4096とか、(1 << 12)のように書くのは面倒です。
    特に64bit整数の大きさのビットの場合、2305843009213693952と書かれても意味不明ですし、(1ULL << 62)も少々見づらいです。
    さらにビットマスクを書く場合、2305843009213693951や((1ULL << 62) - 1)などとても見づらくなります
    そこで今回、bit<N>::value、bitmask<N>::valueでアクセスできるビット定数を作ります。

    コンパイル時ビット

    まず、bit<N>::valueとして(1 << N)が取得できるメタ関数bitを用意します。
    桁数が大きくて64bit整数が必要になった時も型を自動選択できるように、uint_selectorというメタ関数も用意しました。

    #include <boost/mpl/if.hpp>
    namespace mpl = boost::mpl;
    
    const int BITS_PER_BYTE = 8;
    
    template <int DigitNum>
    struct uint_selector :
        mpl::if_c<
            (DigitNum > sizeof(unsigned) * BITS_PER_BYTE),
            unsigned long long,
            unsigned
        > {};
    
    template <int DigitNum, typename T = typename uint_selector<DigitNum>::type>
    struct bit {
        typedef T type;
        static const T value = (static_cast<T>(1) << (DigitNum - 1));
    };
    template <>
    struct bit<0> {
        enum {value = 0};
    };
    

    型は、明示的にも指定できるようにしてあります。

    実行サンプル

    typedef unsigned long long ull_t;
    
    int main() {
        const auto bit1 = bit<3>::value;
        const ull_t bit2 = bit<5, ull_t>::value;
        const auto bit3 = bit<64>::value;
        std::cout << bit1 << std::endl;
        std::cout << bin_string<bit1>::str() << std::endl;
        std::cout << bit2 << std::endl;
        std::cout << bin_string<bit2>::str() << std::endl;
        std::cout << bit3 << std::endl;
        std::cout << bin_string<bit3>::str() << std::endl;
    }
    

    なお、bin_string::str()は2進数表記のstd::stringを生成する自作の関数です。

    実行結果

    4
    100
    16
    10000
    9223372036854775808
    1000000000000000000000000000000000000000000000000000000000000000
    

    コンパイル時ビットマスク

    次にこれを利用し、bitmask<N>::valueとしてビットマスクを取得できるメタ関数bitmaskを用意します。

    template <int DigitNum, typename T = typename uint_selector<DigitNum>::type>
    struct bitmask {
        typedef T type;
        static const T value = bit<DigitNum + 1>::value - 1;
    };
    

    bitと同様に、型は明示的にも指定できるようにしてあります。

    実行サンプル

    typedef unsigned long long ull_t;
    
    int main() {
        const auto mask1 = bitmask<3>::value;
        const ull_t mask2 = bitmask<5, ull_t>::value;
        const auto mask3 = bitmask<64>::value;
        std::cout << mask1 << std::endl;
        std::cout << bin_string<mask1>::str() << std::endl;
        std::cout << mask2 << std::endl;
        std::cout << bin_string<mask2>::str() << std::endl;
        std::cout << mask3 << std::endl;
        std::cout << bin_string<mask3>::str() << std::endl;
    }
    

    実行結果

    7
    111
    31
    11111
    18446744073709551615
    1111111111111111111111111111111111111111111111111111111111111111
    

    いずれもコンパイル時に定数を生成するメタ関数なので、通常の定数と同じく実行時のコストはゼロで使用することができます。