本の虫

著者:江添亮
ブログ: http://cpplover.blogspot.jp/
メール: boostcpp@gmail.com
Twitter: https://twitter.com/EzoeRyou
GitHub: https://github.com/EzoeRyou

アマゾンの江添のほしい物リストを著者に送るとブログ記事のネタになる

筆者にブログのネタになる品物を直接送りたい場合、住所をメールで質問してください。

C++標準化委員会の文書: P0260R0-P0269R0

P0260R0: C++ Concurrent Queues

concurrent queueライブラリの提案。

使い方は、普通にpushやpopがあり、空きがなかったり空だったりするqueueに待ちなしで操作を試みるtry_pushやtry_popがあり、ブロックせずに操作を試みるnonblocking_pushとnonblocking_popがある。

P0261R0: C++ Distributed Counters

プログラムでは、様々な情報をカウンターで扱う。マルチスレッド環境では、このカウンターはatomic操作しなければならない。問題は、リードの頻度が低く、ライトの頻度が高いカウンターでは、atomic操作を用いるのはコストが高くつく。

このライブラリは、頻度の低いリードのコスト増と引き換えに、頻度の高いライトのコストが低いカウンターを提供する。

使い方としては、基本的にはgeneral_counterを使う。

general_counter<int> counter ;

void f()
{
    // インクリメントとデクリメント
    ++counter ;
    --counter ;

    // +=, -=
    counter += 10 ;
    counter -= 10 ;

    int result = counter.load() ;
}

カウンターへのライトは、戻り値がvoidで、カウンターの結果は返さない。カウンターの値を読むためには、メンバー関数loadを読む必要がある。

カウンターへの操作が集中する場所では、general_counterの代わりに、counter_brokerを使うことができる。counter_brokerはgeneral_counterへの参照を保持し、カウンターへのライトを一旦受け取り、counter_brokerに反映する。


general_counter<int> counter ;
thread_local counter_broker<int> broker( counter ) ;

void high_contention_thread()
{
    while ( true )
    {
        ++broker ;
    }
}

void f()
{
    int value = counter.load() ;
}

うまくライトの集中する部分を切り分けてcounter_brokerを効果的に使わなければパフォーマンスが出ない設計のようだ。

P0262R0: A Class for Status and Optional Value

P0260で提案されているConcurrent queueでは、状態と値を返す。値は返せることもあるし、返せないこともある。現在の設計は、値を返すか例外を投げるメンバー関数と、状態を返し、値はリファレンスで受け取った引数に書き込むメンバー関数とがある。

concurrent queueでは、値を返せないというのは通常起こりうることなので、例外のようなエラー処理を使うのは好ましくない。また、リファレンスで受け取ったオブジェクトに書き込むというのは、値はデフォルト構築可能であることを要求する。

理想的なインターフェースは、状態と値を同時に返すものだ。値は存在しないこともあるので、optionalが使える。しかし、状態と値のtupleを使うのは面倒だ。

すでに書いたように、concurrent queueでは、値を返せないというのは通常起こりうることなので、例外のようなエラー処理を使うのは好ましくない。したがって、状態と値を同時に返す、status_value< Status, Value >というライブラリを新たに提案する。

この提案は筋が悪い。素直にtupleを使ったほうがいい。tupleの使い勝手が悪いのならば、tupleを改良すべきだ。

P0263R1: Core "tentatively ready" Issues

コア言語で既知の問題で解決案が規格入りする準備のできたもの。

P0264R0: auto operator= considered dangerous

auto operator =有害論。

=defaultで定義される特別なメンバー関数でもautoによる戻り値の型推定をさせる作法を定着させようと言う動きに対し、auto operator =は有害であるとする文書。

autoはリファレンスにならない。

struct A
{
    // 戻り値の型はA
    auto operator = ( A & ) { /*...*/ return *this ; }
} ;

クラスAの代入演算子は、、lvalueリファレンスではなく、コピーを返す。

autoの代わりに、auto &やdecltype(auto)が使える。しかし、それでも問題は残る。

struct A { decltype(auto) operator = ( A && a ) { reteurn swap(a) ; } // 100行のコード decltype(auto) swap( A && a ) { /*...*/ return *this ; } } ;

このクラスでは、もしswapの戻り値の型を変更すると、その他の多くの特別なメンバーの戻り値の型まで影響を受けてしまう。保守しにくいコードになる。

文書の上げる最後の点の問題点はよくわからない。swapのような他のメンバーからも使われている基本的なメンバーの変更をしたら、結果がクラス全体に及ぶのは当然だと思うのだが。

[PDF] P0265R0: SG5 is NOT proposing Transactional Memory for C++17

SG5はトランザクショナルメモリーをC++17に提案しない決定をした。時期尚早である。

現行のトランザクショナルメモリーはあまりにも柔軟すぎて、現実のアーキテクチャがサポートするネイティブのトランザクショナルメモリーに落とし込めず、ジャイアントロックによる実装になるだろうから、さもありなん。

[PDF] P0266R0: Removing Restrictions on requires-Expressions

コンセプトのrequiresをbool値を期待する文脈ならどこにでも書けるように制限を緩和する提案。

筆者は現状のコンセプトは入るべきではないと考えている。

[PDF] P0267R1: A Proposal to Add 2D Graphics Rendering and Display to C++,

2Dグラフィックライブラリの提案。

205ページもある、とても長大なドキュメントだ。

内容は普通のグラフィックライブラリだ。

[PDF] P0268R0: up-to expression

シマンテックによるup-to expressionの提案。以下のような式が、

[a..b)

half-openレンジ、a, a+1, a+2, ... , b-2, b-1を生成する。

以下のように使う。

// {0,1,2,3,4}
std::vector<int> v = [0..5) ;
// "01234"
for ( int i : [0..5) )
    std::cout << i ;

up-to式は、慣習的な数学記法に従った文法を持ち、レンジコンセプトを満たす型のオブジェクトを返す。これは遅延評価されるため、メモリ使用量はmake_pair(a, b)程度であり、サイズb-aの配列ではない。up-to式はレンジオブジェクトを期待するところで使うことができる。

有名なLinuxディストリビューションのC++で書かれている11,423件のパッケージのソースコードをパースしてトークンをカウントしたACTCD16によれば、識別子iは20,224,291回使われている。識別子iは最も多く使われている識別子である。平均して、一つのソースファイルあたり8個の識別子iが使われている。

よく書かれるコード例としては、以下のようなものだ。

for ( int i = 0 ; i < 10 ; ++i ) { /*...*/ }

searchcode.comで検索したところ、for文の90%はこのような形をとっている。

このようなfor文のうち、半分はrange-based forに置き換えることができる。残りの半分はup-to式に置き換えることができる。

for ( int i : [0..10) ) { /* ... */ }

up-to式はイテレーターやポインターに使うこともできる。

for ( auto iter : [ v.begin()..v.end() ) )
{
    std::cout << *iter ;
}

up-to式はレンジとしてcomposableである。


using namespace ranges::view;
for (int i : [0..5) | reverse)
    print(i); // outputs 4 3 2 1 0
for (int i : [0..10) | stride(3))
    print(i); // outputs 0 3 6 9
for (int i : [0..10) | stride(3) | reverse)
    print(i); // outputs 9 6 3 0

up-to式は式である。その結果は変数に格納できる。

void f()
{
    auto one_to_five = [0..5) ;

    for ( int i : one_to_five ) ;

    auto f = []( auto x ) { return x ; } ;
    auto a = f( one_to_five ) ;
}

文書では、将来的には[)だけではなく、(), [], (]にも対応できるとしている。

趣旨はわかるのだが、ライブラリでいいのではないのかという気もする。

そして、この提案はコンセプトと、コンセプトによるレンジライブラリが存在することを前提にしているので、使えるとしてもまだまだ先の話だ。

[PDF] P0269R0: Allocator-aware regular expressions

これもシマンテックの提案。std::regexをアロケーターに対応させる提案。

設計上の都合、今更他のライブラリのようにテンプレート実引数でアロケーターを取るわけには行かないので、uses_allocatorを使ってアロケーターを指定する。

シマンテックでは、regexを共有メモリ上に構築するためにregexのあロケーター対応が必須だという。

ドワンゴ広告

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0