2015-11-25

C++標準化委員会の文書のレビュー: P0080R0-P0089R0

P0080R0: Variant: Discriminated Union with Value Semantics

variantライブラリの提案。

P0081R0: A proposal to add sincos to the standard library

sinとcosを同時に計算してくれるsincosライブラリの提案。

ある値に対してsinとcosを同時に両方共計算したいことはよくある。同じ値を二度引数として別の関数に渡すのは間違いのもとである。また、x86などの一部のアーキテクチャには、sinとcosを同時に計算してくれる効率的な命令が用意されている。

GCCにあるsincosは、結果をポインターで受け取るが、ポインターが絡むとエイリアスなどの問題が出てくるので、提案されている文法は、単に値をpairで返す。

#include <utility>
namespace std {

pair<double, double> sincos(double x) noexcept;

// その他、floatなどのオーバーロード関数
}

std::tieを使えば、pairは意識せずにすむ。

#include <tuple>

void f( double x )
{
    double s, c ;

    std::tie( s, c ) = std::sincos( x ) ;
}

[PDF] P0082R0: For Loop Exit Strategies (Revision 1)

forループから、条件式がfalseになって抜けた時と、breakで抜けた時に実行されるブロック文を記述できるif for文の提案。

if for ( ... ; 条件 ; ... )
    {
        // for文のブロック文
    }
{
    // 条件がfalseになって抜けた時に実行される   
}
else
{
    // breakで抜けた時に実行される。
}

この論文筆者が主張するには、以下のようなコードより読みやすいらしい


for ( ; 条件 ; )
{
    // for文のブロック文
}

if ( !条件 )
{
    // 条件がfalseで抜けた
}
else
{
    // breakで抜けた
}

どうも必要性がわからない。そういう複雑なfor文は関数として切り分ける方が読みやすいし、lambda式のある現在、ライブラリベースの実装でも良い気がする。

[PDF] P0083R0: Splicing Maps and Sets (Revision 2)

std::listには、spliceという機能がある。これを使えば、あるlistコンテナーで管理されている要素を、所有権ごと別のコンテナーに移動することができる。


    std::list a { 1, 2, 3 } ;
    std::list b { 4, 5, 6 } ;

    a.splice( a.end() , b ) ;

    // a = { 1,2,3,4,5,6}
    // b = { }

たといint型のようにコピーやムーブの処理が軽くても、メモリの動的な確保と解放のコストは大きい。spliceは、所有権ごと要素を移すことで、不要なコストを削減できる。

しかしmapとsetには、このようなspliceがないし、簡単に提供できない。

そこで、連想コンテナーから要素の所有権を切り離せるextract、所有権が切り離された要素を管理するnode_ptr、node_ptrを受け取るinsertを追加する提案。

[PDF] P0084R0: Emplace Return Type

emplaceが構築したオブジェクトへのリファレンスを返すようにする提案。

emplaceは、コンテナーに挿入すべきオブジェクトを渡すのではなく、オブジェクト構築時の引数を渡す。しかし、構築後のオブジェクトを操作したい場合、コンテナーからそのオブジェクトをわざわざ引っ張り出してこないといけない。

struct X
{
    X( int, int, int ) ;

    void do_something() ;
} ;

int main()
{
    std::vector<X> v ;

    // 構築
    v.emplace_back( 1, 2, 3 ) ;

    // 構築後のオブジェクトを引っ張りだして操作
    v.back().do_something() ;
}

emplaceが構築後のオブジェクトへのリファレンスを戻り値として返すようにすれば、このコードは簡略化できる。

v.emplace_back( 1, 2, 3 ).do_something() ;

提案では、emplace_frontとemplace_backを変更するよう提案している。従来のfrontとbackは戻り値を返さないが、これはどうせオブジェクトを実引数で渡すので、コピー/ムーブされたオブジェクト

P0085R0: Oo... adding a coherent character sequence to begin octal-literals

新しい8進数リテラル0oを追加する提案。

0o11 ; // 10進数で9
0O11 ; // 9

既存のプレフィクス0から始まる8進数リテラルは、現代人には馴染みが薄く混乱のもとである。このリテラルは、まだコンピューターが原始的で、パーソナル・コンピューターといえば個人が8bit CPUや16bit CPUを使って自力で作っていた時代からの名残である。

この提案では、新しい8進数リテラルのプレフィクス0o(数字ゼロにアルファベットの大文字小文字のオー)を追加する。

新しい8進数リテラルの追加によって、8進数リテラルへの注目度を上げ、この新しい8進数リテラルを解説する時は、古い8進数リテラルへの注意も併記して問題認識を高め、いずれ既存の8進数リテラルをdeprecated扱いにして、除去することを目指している。

プレフィクス0oの8進数リテラルは、すでに、haskell, OCaml, Python 3.0, Ruby, Tcl Version 9でサポートされていて、ECMAScript 6もサポートする予定であるという。

[PDF] P0086R0: Variant design review

[PDF] P0087R0: Variant: a type-safe union without undefined behavior (v2)

[PDf] 0088R0: Variant: a type-safe union that is rarely invalid (v5)

ライブラリベースの型安全unionを実現するvariantライブラリの設計事情と、議論が別れている設計の差異について。

variantはinvalid/emptyな状態を許容すべきかどうか。その状態をクエリー可能にすべきかどうか。

純粋な数学的観点からinvalid/emptyなvariantは認めるべきではないという意見と、その場合、ユーザーが自前でダブルバッファリングをしなければならないので結局使いづらい。variantがダブルバッファリングを提供すべきだ。例外を投げないムーブのある型の場合、メタプログラミングでvariantのダブルバッファリングは除去できるし、将来的にはTransactional Memoryであらゆる型のダブルバッファリングが除去できるなどと豪語する壮大なお花畑意見まである。

[PDF] 0089R0: Quantifying Memory-Allocatiom Strategies

メモリ確保にローカルアロケーターを使うことによるパフォーマンス向上のベンチマークと考察

ローカルアロケーターを使い、個々のオブジェクトのデストラクターを呼ばずに、一括して解放することによる、パフォーマンスの向上の余地は十分にあると主張している。

ドワンゴ広告

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

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

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

2015-11-20

Mac OS Xで削除がとても難しいファイルを作成する方法

2013年の記事だが、Mac OS XとそのHFS+はこの上なくクソなので何があっても驚きはしないが、Mac OS Xで削除しにくいシンボリックリンクファイルを作成する方法があるらしい

OS X and the Unremovable File - galvanist

HFS+上で、削除しにくいシンボリックリンクを作成できる。

# be root, for example with: sudo -i
str1=$(python -c "print '1' * 255")
str2=$(python -c "print '2' * 255")
str3=$(python -c "print '3' * 255")
str4=$(python -c "print '4' * 253")
mkdir -p  $str1/$str2/$str3/$str4
ln -s ftw $str1/$str2/$str3/$str4/L

さて、このように作った以上、OS X v10.9では以下のコマンドでは削除できない。


# still as root...
unlink 1*/2*/3*/4*/L
unlink $str1/$str2/$str3/$str4/L
rm -rf 1*
rm -rf $str1
rm -rf $str1/$str2/$str3/$str4
rm -rf $str1/$str2/$str3/$str4/L
(cd $str1/$str2/$str3/$str4; unlink L)
(cd $str1/$str2/$str3/$str4; rm -rf L)

すべて、以下のようなエラーとなる。(読みやすさのために[...]で省略)

root# pwd
/private/tmp/111[ ... ]111/222[ ... ]222/333[ ... ]333/444[ ... ]444
root# ls
L
root# rm -f L
rm: L: No space left on device
root# df -H
Filesystem      Size   Used  Avail Capacity   iused     ifree %iused  Mounted on
/dev/disk1      250G   108G   142G    44%  26385563  34601956   43%   /
[...]

念の為、システムコールを直接呼び出すコードを書いてみる。/tmp/fixit.cにおいてあるとする。

#include <unistd.h>
#include <stdio.h>
#include <errno.h>

int main(int argc, char* argv[]) {
    printf("Unlink returned %i\n", unlink("L"));
    perror("Error was");
    return 0;
}

実行してみるが、

root# pwd
/private/tmp/111[ ... ]111/222[ ... ]222/333[ ... ]333/444[ ... ]444
root# gcc -o /tmp/fixit /tmp/fixit.c 
root# /tmp/fixit 
Unlink returned -1
Error was: No space left on device

ENOSPCだと。unlink(2) Mac OS X Developer Tools Manual Pageに返すとは書かれていないエラーだぞ。

状況は複雑だ。

  • 一般ユーザーが作ったのならば、一般ユーザーは、rm -rfで消すことができる
  • 一般ユーザーが作ったのならば、rootは消すことができない。おかしい
  • rootが作ったのならば、rootは消すことができない
  • rootが作ったのならば、通常通りの権限により、一般ユーザーは削除が行えない
  • rootが作ったものであっても、chmod -hとchown -hで権限を変えれば、一般ユーザーにも消せるはずである
  • rootが作ったものを、chmod -hとchown -hしようとすると、ENOSPCが返る。
  • rootが作ったものに対し、"mkdir -p some/containing/paths; mv 1111* some/containing/paths/"は動くが、その後、"rm -rf some"しても動かない。

Workaround

なぜか、パスはシンボリックリンクを作るには短いが、削除するには長すぎるようだ。パスを短縮すれば消せる。

root# pwd
/private/tmp/111[ ... ]111/222[ ... ]222/333[ ... ]333/444[ ... ]444
root# ls
L
root# mv /private/tmp/1* /private/tmp/one
root# pwd
/private/tmp/one/222[ ... ]222/333[ ... ]333/444[ ... ]444
root# rm L
root# ls
root# rm -rf /tmp/one
root# 

workaroundは十分なのか。

そういうわけで、一応workaroundはあるのだが、しかし疑問がある。

  • アンチウイルスソフトウェアはマルウェアがこのような方法で保存されていた場合、ちゃんと消せるよう作られているのか? アンチウイルスソフトウェアはすでにchflags(2)の問題に対処しなければならないが、こういうパスと複数のchflagsで構成されていた場合はどうか
  • マルウェアや悪意ある人物がディスクをこのようなもので埋め尽くした時に、rm -rfの代替品となるツールは存在するのか?

ちなみに、これは2013年の記事だが、Yosemiteまでは同じ問題があるそうだ。El Capitainだと、ファイルパスが長すぎて作成できないエラーが返るらしい。そもそも、Mac OS XのカーネルであるXNUのファイルパスの上限が2024文字なのに2025文字のファイルパスを作成できてしまうのがおかしいようだ。

C++標準化委員会の文書のレビュー : P0070R0-P0079R0

P0070R0: Coroutines: Return Before Await

現在のコルーチン提案では、関数がコルーチンかどうかを宣言から判定できない。そのような判定をさせるタグ機能は議論の結果却下された。しかし、awaitかyieldをみるまで関数がコルーチンかどうかわからないのは不便なので、コルーチン関数では、通常のreturnキーワードを使ったreturn文のかわりに、特別なco_returnキーワードを使ったreturn文の使用を強制させるようにしようという提案に対して、その必要はないとする論文。

理由は、MSVCやGCCは、すでに最適化のために、関数の全体を見るまで意味解析を行わないようになっているので、そんな技術的制約上の機能は必要ないとのこと。

P0071R0: Coroutines: Keyword alternatives

現在のコルーチンの提案では、awaitとyieldに当たるキーワードが未定である。理想世界においては、awaitとyieldというキーワードは機能をこれ以上なく明白に指し示したものである。しかし、現実世界においては、awaitやyieldのような既存のコードと衝突するようなキーワードを導入できない。

これに対して、いくつかの解決策が示されている。

ソフトキーワード:std名前空間に暗黙に宣言されている名前で、他の名前と衝突しない場合、キーワードとして認識される。

マジック関数: std名前空間内にある特別な関数で、コンパイラーが認識する。

問題は、文法が関数呼び出し式になってしまうので、すこし汚い。

await expr ;

が、

std::await( expr ) ;

になってしまう。

その他のキーワード案: exportキーワードが余っているではないか。yieldexpr/yield_exprはどうか。coyield/co_yieldはどうか。

論文では、awaitとyieldに変わる良いキーワードはないとした上で、ソフトキーワードかマジック関数のどちらかを採用すべきだとしている。

[PDF] P0072: Light-Weight Execution Agents

スレッドよりも制約の強い実行媒体(Execution Agent、スレッドプール、ファイバー、GPGPU、SIMDなど)を扱うために、実行媒体とその保証について定義する提案。

[PDF] P0073R0: On unifying the coroutines and resumable functions proposals

コルーチンとレジューム可能関数を統合したことについて

P0074R0: Making std::owner_less more flexible

std::onwer_lessという関数オブジェクトがある。これは、2つのshared_ptrとweak_ptrが、同じオブジェクトを所有しているかを判定してくれる機能を提供している。しかし、このインターフェースが悪いため、とても単純な例でしか動かない。

  shared_ptr<int> sp1;
  shared_ptr<void> sp2;
  shared_ptr<long> sp3;
  weak_ptr<int> wp1;

  owner_less<shared_ptr<int>> cmp;
  cmp(sp1, sp2);   // error, doesn't compile
  cmp(sp1, wp1);
  cmp(sp1, sp3);   // error, doesn't compile
  cmp(wp1, sp1);
  cmp(wp1, wp1);   // error, doesn't compile

最初の例は、owner_less<shared_ptr<void>>を使うことで解決できるが、単純にそれだけを使ってもコンパイルが通らない。なぜならば、sp1はshared_ptr<void>にもweak_ptr<void>にも変換可能だからだ。そのため、明示的に変換してやらなければならない。

owner_less<shared_ptr<void>> cmpv;
cmpv(shared_ptr<void>(sp1), sp2);

これは冗長でわかりにくいだけではなく、一時オブジェクトを作り出し、参照カウンターを増減させる。

shared_ptr::owner_beforeとweak_ptr::owner_beforeはどちらもshared_ptr<A>とweak_ptr<B>といった異なる型同士での比較を許しているのに、owner_lessは無用の制限をかけている。

owner_lessを改良して、上のコードが全てコンパイルが通るようにする提案。

[PDF] P0075R0: Template Library for Index-Based Loops

インデックスベースループの並列版をライブラリとしてParallelism TSに付け加える提案。

void ( int * p1, int * p2, std::size_t size )
{
    std::for_loop( std::seq, 0, size,
        [&]( auto i )
        {
            p1[i] = p2[i] ;
        }
    ) ;
}

このコードは以下のように書くのと同等だ。

void ( int * p1, int * p2, std::size_t size )
{
    for ( std::size_t i = 0 ; i != size ; ++i )
    {
        p1[i] = p2[i] ;
    }
    
}

他のParalellism TSのアルゴリズムのように、std::secをstd::parに変更すると、並列実行版にある。

for_loop_stridedは、インクリメントの刻み幅を設定できる。

for_loop_strided( std::par, 0, 100, 2, []( auto i ) { } ) ;

iは0, 2, 4, 6とインクリメントされていく。

提案はfor_loopとともに使えるreductionとinductionをサポートしている。

reductionはOpenMPの文法を参考に、純粋にライブラリベースで使えるように設計されている。

float dot_saxpy(int n, float a, float x[], float y[]) {
    float s = 0;
    for_loop( par, 0, n,
        reduction(s,0.0f,std::plus<float>()),
        [&](int i, float& s_) {
            y[i] += a*x[i];
            s_ += y[i]*y[i];
        });
    return s;
}

reductionは、reduction( var, identity, op )のように使う。それぞれ、reductionの結果をうけとるlvalue, reduction操作のためのidentity value, reduction操作となる。

reductionの個別のsのコピーはfor_loopの関数オブジェクトに追加の引数として与えられる。for_loopはvariadic templatesを利用していて、reductionやinductionを受け取れるようになっている。最後の実引数が関数オブジェクトとなる。

ループのイテレーションごとに異なる可能性のあるsのコピーが渡され、それに対してreduction操作、最後にすべてのsのコピーがsに対してreduction操作が行われる。結果として、以下のコードを実行したのと同じ結果(ただしシーケンスとreductionの順序の制約がゆるい)が得られる。

float serial_dot_saxpy (int n, float a, float x[], float y[]) {
    float s = 0;
    for( int i=0; i<n; ++i ) {
        y[i] += a*x[i];
        s += y[i]*y[i];
    }
    return s;
}

簡便化のために、identitiy valueがreduction操作に合わせてT()やT(1)など無難なものになった上に、reduction操作も固定されている、reduction_plusなどが用意されている。上記の例は、reduction_plus( s ) と書くこともできる。

inductionもreductionと同じように指定する。

float* zipper(int n, float* x, float *y, float *z) {
    for_loop( par, 0, n,
        induction(x),
        induction(y),
        induction(z,2),
        [&](int i, float* x_, float* y_, float* z_) {
            *z_++ = *x_++;
            *z_++ = *y_++;
        });
    return z;
}

この分野の知識が乏しいのでinductionの意味がよくわからない。以下のコードとシリアル実行という点を除いては同じ意味になるようだが、単にlambda式でキャプチャーしてはダメなのだろうか。

float* zipper(int n, float* x, float *y, float *z) {
    for( int i=0; i<n; ++i ) {
        *z++ = *x++;
        *z++ = *y++;
    }
    return z;
}

[PDF] P0076R0: Vector and Wavefront Policies

Parallelism TS(STLのアルゴリズムに並列実行版のオーバーロードを追加する規格)に、新しい実行ポリシーとしてvecとunseqを追加する提案。

unseqはseqより実行時特性の制約がゆるいが、実行は単一のOSスレッド上に限定される。vecはシーケンスの制約がunseqより強い。vecはSIMDなどのベクトル演算を想定している。

P0077R0: is_callable, the missing INVOKE related trait

指定した型が指定した実引数型で呼び出して指定した戻り値の型を返すかどうかを調べられる、is_callable traitsの提案。

template <class, class R = void> struct is_callable; // not defined

    template <class Fn, class... ArgTypes, class R>
      struct is_callable<Fn(ArgTypes...), R>;

    template <class, class R = void> struct is_nothrow_callable; // not defined
    template <class Fn, class... ArgTypes, class R>
      struct is_nothrow_callable<Fn(ArgTypes...), R>;

以下のように使う。

void f( int, double ) ;

constexpr bool b = std::is_callable< f ( int, double ), void >::value ; // true

is_callable< Fn( args ... ), R >は、INVOKE( declval<Fn>(), declval<args>()..., R )が合法かどうかを返す。

上記の条件に加えて、無例外保証も調べるis_nothrow_callableもある、

[PDF] P0078R0: The [[pure]] attribute

[[pure]]属性を追加する提案。

[[pure]]属性は関数に指定することができる。指定された関数はpureとなる。pure関数には様々な制約がある。

pure関数は、与えられた入力に対して出力を返す。同じ入力が与えられた場合、必ず同じ出力を返す。副作用を発生させてはならない。戻り値の型がvoidであってはならない(純粋に数学的な関数は常に戻り値を返す)

現在の文面案は以下の通り。

関数fがpureである場合、数学的な関数を実現している。すなわち、(a) 同じ値の実引数に対して、fは常に同じ答えを返す。(b) fはクライアントコードと実引数リストと戻り値のみを使ってやり取りする。(c) 常に呼び出し元に戻る。(d) fはその実行外において観測可能な副作用を発生させない。関数gの本体の中の文Sがpureである場合、Sが実行されたならば、pure gの挙動から外れた挙動を示してはならない。pure関数、あるいは文の反対は、impureである。すべての関数と文は、pureであると指定されない限り、impureとなる。

[ 例:関数fは、以下の場合、pureではない。

  • 実引数が値で渡されていない
  • グローバルメモリへの読み書きアクセス
  • 戻り値の型がvoid
  • impure関数を呼び出す
  • 呼び出し元や他のスレッドがfによる変更を感知できること、たとえば、

    1. 関数にローカルではない変数やデータ構造に依存するか書き変える
    2. 動的にメモリーを確保する
    3. 例外をcatchしないで外に投げる

pure関数には様々な利点がある。pure関数を使った式は除去したりmemoizationにより簡易化できる。同期やスレッドセーフ、コード順序の変更も大胆に行えるので、最適化の余地が広がる。pure関数はテストしやすい。

pure関数には問題もある。副作用を発生させることができないので、I/Oや動的メモリ確保ができないし、impure関数を呼び出すこともできない。コンパイラーはpure指定された関数はpureであるという前提のもとコード生成を行うので、pure関数に副作用を発生させるバグがある場合、不可思議な挙動を示し、特定が困難になるかもしれない。

論文は既存のプログラミング言語におけるpure関数の実装例も紹介しているので興味のある人は読むと良い。

P0079R0: Extension methods in C++

統一感数記法に変わる軽量な拡張メソッドの提案。

統一感数記法は、メンバー関数呼び出しとフリー関数呼び出しの文法を統一し、どちらで呼び出しても良いようにするものであった。

struct A { } ;
void foo( A & ) ;

struct B { void foo() ; }

int main()
{
    A a ;
    a.foo() ; // OK

    B b ;
    foo(b) ; // OK 
} 

目的としては、ジェネリックなコードで、型によってメンバー関数とフリー関数によって呼び出す文法を変えなければならないのは面倒なので、どちらで呼び出してもコンパイラーが勝手に変換してくれるようにしようというものだ。

しかし、この機能はあまりにも大きすぎる変更なので、既存のコードに問題を引き起こす恐れがある。そこで、この提案では、もう少し軽量な、明示的なopt-inが必要となる機能、拡張メソッドを提案している。

拡張メソッドは、フリー関数で、第一引数として明示的なthisポインターを取る。仮引数名は必ずthisとする。

struct A { } ;

// 拡張メソッド
void foo( A * this, int ) ;

int main()
{
    A a ;
    a.foo( 0 ) ; // OK
}

オーバーロード解決では、メンバー関数と拡張メソッドでは、メンバー関数のほうが優先される。


struct A
{
    void f() ;
} ;

void f( A * this ) ;

int main()
{
    A a ;
    a.f() ; // メンバー関数
}

拡張メソッドは、アクセス指定においては特別扱いされない。

struct A
{
private :
    int x ;
} ;

void f( A * this )
{
    this->x ; // エラー
} 

コンセプトと組み合わせることで、メンバー関数を持っている場合のみ呼び出す拡張メソッドが書ける。

template < typename T >
concept bool has_foo()
{
    return requires( T & t )
    {
        { t.foo() } -> void ;
    } ;
}

void foo( has_foo * this )
{
    this->foo() ;
}

C#にある拡張メソッドを参考にしているらしい。

なんだか素朴な案だ。

ドワンゴ広告

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

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

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

2015-11-18

Minecraftクライアントからdockerの管理ができるminecraftサーバー: Dockercraft

docker/dockercraft

公式の未改変Minecraftクライアントから接続してDockerの管理ができるMinecraft互換サーバー、Dockercraftが公開されている。

Dockercraft

Dockercraftの実行方法

1. Minecraftのインストール: Minecraft

Minecraftクライアントは改変していないので、公式リリースをそのまま使える。

2. Dockercraftイメージをpullするかビルドする。

docker pull dockercraft

もしくは、

git clone [email protected]:docker/dockercraft.git
docker build -t dockercraft dockercraft

3. Dockercraftコンテナーを実行する

docker run -t -i -d -p 25565:25565 \
-v /var/run/docker.sock:/var/run/docker.sock \
--name dockercraft \
dockercraft

Docker remote APIを使うには/var/run/docker.sockをコンテナー内でマウントする必要がある。

Minecraftサーバーのデフォルトのポートは25565だ。他のポートがいいのであれば、"-p <port>:25565"

4. Minecraft > Multiplayer > Add Serverを開く

サーバーアドレスはDockerホストのIP。デフォルトのものを使っているのであればポート番号を指定する必要はない。

Docker Machineを使っているのであれば、"docker-machine ip <machine_name>"

5. サーバーにjoinする。

少なくともひとつのコンテナーが世界に見えるはずだ。それはDockercraftサーバーをホストしているものである。

レバーとボタンを操作することで、コンテナーをスタート、ストップ、リムーブできる。Minecraftのチャットウインドウから一部のDockerコマンドがサポートされている。チャットウインドウはTキー(デフォルト)か/キーで表示される。

実装

Mnecraftクライアント自体は改変していない。操作は全てサーバーサイドで行われている。

Minecraftサーバーには、Cuberite.orgを用いた。C++で書かれた独自のMinecraft互換ゲームサーバーだ。GitHubレポジトリ

サーバーはLUAで書かれたスクリプトをプラグインとして受け付ける。そこでDocker用のものを書いた。(world/Plugins/Docker)

残念ながら、このプラグインと通信するいいAPIがない。ただしwebadminがあり、プラグインは"webtabs"に応答できる。

Plugin:AddWebTab("Docker",HandleRequest_Docker)

つまり、プラグインは、http://127.0.0.1:8080/webadmin/Docker/Dockerに送られたPOSTリクエストを見ることができる。

Goproxy

Docker remote APIのイベントはGoで書かれた小さなデーモン(go/src/goproxy)によってLUAプラグインに送られる。

func MCServerRequest(data url.Values, client *http.Client) {
    req, _ := http.NewRequest("POST", "http://127.0.0.1:8080/webadmin/Docker/Docker", strings.NewReader(data.Encode()))
    req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
    req.SetBasicAuth("admin", "admin")
    client.Do(req)
}

デーモンにリクエストを送るため、LUAプラグインからgoproxyバイナリを匹数付きで実行することもできる。

function PlayerJoined(Player)
    -- refresh containers
    r = os.execute("goproxy containers")
end

貢献

Dockercraftをハックしたいならば、Docker's contributions guidelinesを参照。

これで、仕事中にMinecraftで遊んでいても、「Dockerの管理をビジュアライズしてくれる便利なツールなんです」という言い訳がたつ。

「なるほど、しかしなぜ大規模なクラフトをする必要があるんだね?」
「Docker操作を自動化するために必要な論理回路を組んでいます」

2015-11-16

C++標準化委員会の文書のレビュー: P0060R0-P0069R0

P0060R0: Function Object-Based Overloading of Operator Dot

operator .をオーバーロード可能にする提案。

この提案は、リファレンスを返す提案とは異なり、コンパイラーが関数オブジェクトを生成する提案となっている。

operator .をオーバーロードしてるユーザー定義型がある。

struct X
{
    // なにもしない
    template < typename T >
    void operator .( T && ) { }
}

そのような型の非staticデータメンバーの名前検索(たとえば.some_name)をして、名前が見つからない場合、コンパイラーは以下のような型を生成し、そのオブジェクトをoperator .のオーバーロード関数の実引数に渡す

struct synthesized_function_type {
    template<class T>
    auto operator()(T&& t) -> decltype(t.some_name) noexcept(t.some_name) {
        return t.some_name;
    }
};

その結果、以下のように書ける。

X x ;
x.hoge ; // OK
x.hage ; // OK

メンバー関数の場合は、以下のような型が生成される。

struct synthesized_function_type {
    template<class T, class... Args>
    auto operator()(T&& t, Args&&... args) -> decltype(t.some_name(std::forward<Args>(args)...)) noexcept(t.some_name(std::forward<Args>(args)...)) {
        return t.some_name(std::forward<Args>(args)...);
    }
};

これを使って、例えばスマートリファレンスを作成できる。

template < typename T >
class Ref
{
    T t ;
public :
    template < typename F >
    auto operator . ( F && f )
    {
        return f( t ) ;
    }

    template < typename F, typename ... Args >
    auto operator . ( F f, Args && ... args )
    {
        return f( std::forward<Args>( args ) ... ) ;
    }
} ;

struct Point
{
    int x, y ;
    void func( int x ) { }
} ;

Ref<X> r ;

r.x ;
r.y ;
r.func( 0 ) ;

P0061R0: Feature-testing preprocessor predicates for C++17

SG-6が推奨している機能テストマクロに含まれている、__has_includeと__has_cpp_attributeを正式にC++17に提案している。

__has_includeは指定したヘッダーファイルがあるかどうかを調べることができる。__has_cpp_attributeはattributeが存在するかどうか調べることができる。

#ifdef __has_include(<optional>)
#   include <optional>
#else
#   include "my_optional"
#endif 

P0062R0: When should compilers optimize atomics?

atomicではすべての最適化が無効になると考えている者もいるが、atomicは最適化できる。たとえば、load( x, memory_order_x ) + load( x, memory_order_x )は、2 * load( x, memory_order_x )に変換することができる。しかし、非volatileなatomic loadが使われてはいないからといって、消してしまうのは異論が多い。

この論文では、c++std-parallelで議論された、異論のある最適化についてまとめている。

// 途中経過表示用の変数
atomic<int> progress(0);

f() {
    for (i = 0; i < 1000000; ++i) {
        // 処理
        ++progress;
    }
}

このようなコードがあるとする。atomic変数progressは処理の進行具合を記録していて、他のスレッドはこの変数を参照してユーザーに処理の進行具合をプログレスバーで表示している。

過剰に最適化するコンパイラーは、以下のようにコードを変換するかもしれない。

atomic<int> progress(0);
f() {
    int my_progress = 0; // レジスタ割り当てされた非atomic変数
    for (i = 0; i < 1000000; ++i) {
        // ...
        ++my_progress;
    }
    progress += my_progress;
}

コードがこのように変換されると、他のスレッドがprogressを参照した時に、0か999999しか見えない。

他には以下のような例がある。

x.store(1); // アトミック操作
while (...) { … } // ループ

コンパイラーは以下のように変換するかもしれない。

while (...) { … }
x.store(1);

これも規格上問題ないが、ループを始める前にアトミック操作を行って他のスレッドに通知したいと考えるプログラマーの意図とは異なるだろう。

Peter Dimovによれば、過剰な最適化は好ましいという。例えば、shared_ptrのカウンターの増減を除去できる。

Paul McKennryによれば、上記のプログレスバー問題を解決するのによく使われる手法は、アトミック変数をvolatile修飾することだという。しかし、これでは問題のない好ましい最適化まで抑制されてしまう。コンパイラーがそこまで過剰に最適化を行わない場合はパフォーマンスの損失につながる。

論文筆者のHans Boehmの考えでは、コンパイラーはアトミック変数を過剰に最適化するべきではない。属性などで過剰に最適化させる方法を提供すべき。としている。

過剰な最適化は、shared_ptrの例のようにとてもよいこともあれば、プログレスバーの例のようにとても悪いこともある。コンパイラーにとって、良い悪いは判断できない。判断できないのであれば、どちらがわにも倒さずにコードをそのままにしておくべきだ。

とはいえ、過剰な最適化の利点はあるので、そのための方法を提供すべきだ。

論文は最後に、規格の文面に追記する注釈案を載せている。

P0063R0: C++17 should refer to C11 instead of C99

C++規格が参照するC規格をC99からC11に引き上げる提案。

単に参照先を買えるだけではない変更も考察されている。シグナルハンドラー内ではthread_local変数は使えない。新たに追加されたCヘッダーに対してはC++規格からは参照しない。Atomicsについては、_Atomic(T)がatomic<T>になっていると嬉しい。C言語に新しく追加された有益な機能のC++版がないなど。

[PDF] P0065R0: Movable initializer lists, rev. 2

initalizer_list<T>のbegin/endの戻り値の型は、const T *である。そのため要素をムーブできない。この提案は、非constなT *を返すbegin/endを持ったown_initializer_list<T>を提案している。

初期化リストから推定される型Tが、非トリビアルなムーブコンストラクターを持っている場合は、型はown_initializer_list<T>になる。そうでない場合は、initializer_list<T>になる

// std::initializer_list<int>
auto x = { 1, 2, 3 } ; 
// std::own_initializer_list<std::string>
auto y = { std::string("hello") } ;

[PDf] P0066R0: Accessors and views with lifetime extension

一時オブジェクトをリファレンスに束縛すると、一時オブジェクトの寿命がリファレンスの存在期間中延長されるが、現行の規程は問題が多い。

std::string const & f( std::string const & ref ) { return ref ; }

int main()
{
    // 寿命が延長されない。
    auto const & ref = f( std::string("hello") ) ;
}

initializer_listだけはこのような一時オブジェクトの寿命延長をコンパイラーマジックによって行っている。

一時オブジェクトの寿命延長を関数やクラスのサブオブジェクトを経由してもユーザーが行えるようにする寿命修飾子を提案しているのだが、どう考えても非現実的な机上の空論のクソ機能にしか思えない。ABIに変更をもたらすのもクソだ。真面目に読んで損をした。

P0067R0: Elementary string conversions

整数と文字列、浮動小数点数と文字列間の相互変換のみを行うライブラリ、to_string, from_stringの提案。

JSONやXMLなどのテキストベースのフォーマット利用が拡大している現代では、数値と文字列間の高速な変換が必要である。変換はinternationalizationを行う必要はない。

そのためには、以下の要件を満たす必要がある。

  • フォーマット文字列の実行時パースを行わない
  • インターフェース上動的メモリ確保をしない(大抵のインターフェースは必要とする)
  • localeを考慮しない
  • 関数ポインターを経由した関節アクセスを行わない
  • バッファーオーバーランを防ぐ
  • 文字列をパースするとき、エラーと有効な数値は区別できる
  • 文字列をパースするとき、空白文字などが静かに無視されたりしない

既存の標準ライブラリで提供されている方法はすべて、localeに依存している。

また、変換処理は、教科書通りの実装では速くない。高速な実装方法が多数ある。

この提案では、極めて原始的なインターフェースを採用している。char *型のイテレーターベースのインターフェースだ。数値を文字列に変換するにはto_string、文字列を数値に変換するにはfrom_stringを使う

char * to_string(char * begin, char * end, T value, その他の引数) ;

T型の数値valueを文字列に変換したものが、[begin, end)に書き込まれる。戻り値は書き込んだ最後の要素の一つ次のポインターになる。文字列を書き込むスペースが足りない場合、endが返される。スペースが足りない場合に書き込まれる内容は未規定である。

整数型は以下のような関数になる。

char * to_string(char * begin, char * end, T value, int base = 10);

baseの範囲は2から36までで、10から36までの数値はaからzで表現される。valueが負数の場合、マイナス符号が先頭に書き込まれる。

浮動小数点数型は、変換する文字列のフォーマットごとに、以下のような2つの関数が提供されている

31415e-4のようなフォーマットの文字列に変換

char * to_string(char * begin, char * end, T value);

valueが負数の場合はマイナス符号が先頭に書き込まれる。文字列は10進数で、整数部が少なくともひと桁あり、オプショナルで小文字eに続いてexponentが記述される。表現は最小文字数かつ、from_stringによって元のvalueが正確に復元できるものとなる。valueが無限大の場合、infか-infとなる。NaNの場合、nanか-nanとなる。

3.1415のようなフォーマットの文字列に変換

char * to_string(char * begin, char * end, T value, int precision);

小数部の桁数はprecisionになる。

文字列を数値に変換するfrom_stringは以下の通り

const char * from_string(const char * begin, const char * end, T& value, std::error_code& ec, 残りの引数 );

戻り値はパターンにマッチしない最初の文字へのポインター。

整数型に変換する場合は以下の通り

const char * from_string(const char * begin, const char * end, T& value, std::error_code& ec, int base = 10);

浮動小数点数型に変換する場合は以下の通り

const char * from_string(const char * begin, const char * end, T& value, std::error_code& ec);

文字列に変換する際のinfやnanも正しく認識される。

[PDF] P0068: Proposal of [[unused]], [[nodiscard]] and [[fallthrough]] attributes

タイトル通り、[[unused]], [[nodiscard]], [[fallthrough]] attributeの提案。

[[unused]]

[[unused]]が指定されたエンティティは、何らかの理由で使われていないように見えるという意味を持つ。これによって、実装による未使用警告を抑制できる。

int x ; // 警告、xは未使用
[[unused]] int x ; // 警告が抑制される

[[nodiscard]]

[[nodiscard]]が指定された関数は、戻り値がdiscardされてはならないことを意味する。戻り値が捨てられた場合、警告が出る。

[[nodiscard]] bool do_something_that_may_fail() ;

int main()
{
    do_something_that_may_fail() ; // 警告、戻り値が捨てられてい

    // OK
    if ( do_something_that_may_fail() )
    {
    }
    else
    {
    }
}

[[nodiscard]]が型に指定された場合、その型を戻り値の型に使う関数が[[nodiscard]]指定される。


[[ nodiscard]] class error_code { ... } ;

// 関数fは[[nodiscard]]指定される
error_code f() ;

[[fallthrough]]

[[fallthrough]]は文のように記述される。これをfallthrough文という。

[[fallthrough]] ;

[[fallthrough]]はcaseラベルの直前に書く。これによって、複数のケースを突き抜けたコードに対しての警告を抑制できる。

switch( x )
{
case 0 :
    do_something() ;
    // 警告、fallthrough
case 1 ;
    do_something() ;
    [[fallthrough]]
    // 警告なし
case 2 ;
    do_something() ;

} 

いい提案だ。

[PDF] P0069R0: A C++ Compiler for Heterogeneous Computing

GPU用に現在提案されているGPUのサポートも視野に入れた機能をC++コンパイラーに実装した経験の報告書。

開発したのコンパイラー、HCC(Heterogeneous C++ Compiler)は、Clang/LLVMベースのコンパイラーだ。HCCはホストプロセッサー用のコード(x86)とアクセラレーター用のコード(AMD GPU/HSA)を同時に生成する。HCC自体は公開されていないようだ。

問題となるのは、コードのどこの部分をどちらのプロセッサー用のコードで生成するかだ。すべてのコードをすべてのプロセッサー向けにコード生成を行うと、コンパイル時間が増大する上に、プロセッサーごとの特性もあるので、逆に遅くなる。

C++提案では、特定の領域に対して機能を制限してコンパイラーにヒントを与える機能があるが、これはあまり役に立たなかったと報告している。

HCCがかわりに開発したのは、プログラマーがコンパイラーがコード中の並列部分を指示できる機能云々

なんだかOpenMPのようだ。

モダンなGPUはC++の機能のほとんどを実行できるようになった。たとえば、間接的な関数呼び出し、バイト単位のメモリアクセス、基本型、アライメントされていないメモリへのアクセス、スタック、gotoなどだ。

HCCはC++のほとんどの機能をアクセラレーター向けにコード生成できる。例外はtry/catchブロックなどだ。try/catchブロックの実装は技術的に可能であるが、現在のところ実装していない。論文著者は、アクセラレーター向けにC++のサブセットを定義する必要はないと報告している。

ドワンゴ広告

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

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

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

2015-11-09

C++標準化委員会の文書: P0050R0-P0059R0

[PDF] P0050R0: C++ generic match function

variantやoptionalなど、type erasureを使って複数の型を格納するsum typeは、それぞれが独自のvisitorインターフェースを提供している。例えば、std::experimantal::variantはvisitを、Boost.variantはapply_visitorを提供している。

ライブラリごとに異なる方法で要素にアクセスするのは汎用的ではない。そこで、この提案では、汎用的にtype erasureされた要素にアクセスできるmatch関数テンプレートを提案している。

matchは以下のように宣言されている。

template <class R, class F>
R match(ST const&, F&& );

戻り値の型Rがないバージョンもある。

matchはライブラリによるcustomization pointであり、ライブラリが対応させる。

たとえば、Boost.variantをmatchに対応させる場合は、以下のような実装になる。

namespace boost {
template <class R, class F, class ...Ts >
R match(variant<Ts...> const& v, F&& f)
{
    return apply_visitor(std::forward<F>(f), v); }
}

std::variantでvisitを使うには、以下のように書く。

std::experimantal::variant< int, double > a = 2 ;
std::experimental::visit( []( auto x ) { }, a ) ;

これをmatchで書き直すと、以下のようになる。

std::experimental::variant< int, double > a = 2 ;
std::experimental::match( a, []( auto x ) { } ) ;

variantのvisitがvisitorを先に書くのに対し、matchは後に書く。

また、P0051が提案するoverloadライブラリを使うと、以下のように書くこともできる。

std::experimental::match( a, overload( 
    []( int x ) { },
    []( double x ) { } ) ;

複数のsum typeに対するvisitorも書ける。

std::experimental::variant<int, X> a = 2;
std::experimental::variant<int> b = 2;
std::experimental::visit(overload(
    [](int i, int j )
    {
    },
    [](auto i, auto j )
    {
        assert(false);
    }
   ), a, b);

現在、戻り値の型の決定方法、customization pointとしてどの関数を洗濯するかmatchより既存のvisitの方がわかりやすいか、引数の順序などの議論があるらしい。

[PDF注意] P0051: C++ generic overload function

選択した関数オブジェクト群を保持する関数オブジェクトを返し、呼び出すとオーバーロード解決して最適関数を呼び出してくれるoverload関数テンプレートの提案。

auto o = overlord(
    []( int ) { },
    []( double ) { },
    []( std::string ) { } ) ;

o( 0 ) ; // int
o( 0.0 ) ; // double
o( "hello" ) ; // std::string

簡易的な実装は以下の通り。

template < typename ... Functors >
struct overloader ;

template < >
struct overloader < >
{
    void operator () () { }
} ;

template < typename Functor0, typename ... Functors >
struct overloader< Functor0, Functors ... >
    : Functor0, overloader< Functors ... >
{
    overloader( Functor0 && functor0, Functors && ... functors )
        : Functor0( std::forward<Functor0>(functor0) ),
          overloader< Functors ... >( std::forward<Functors>( functors ) ... )
    { }

    using Functor0::operator () ;
    using overloader< Functors ... >::operator () ;
} ;

template < typename ... Functors >
overloader < Functors ... >
overload( Functors && ... functors )
{
    return overloader< Functors ... > ( std::forward<Functors>(functors) ... ) ;
}

渡されたすべての関数オブジェクトから派生し、すべての関数オブジェクトのoperator ()をusing宣言で最も派生したクラスのスコープに持ち込むことにより、オーバーロード解決を実現している。

この実装では、関数オブジェクトが関数ポインターやメンバー関数ポインターの場合は動かないが、ラッパーを挟めばオーバーヘッドなしに対応できる。提案では関数ポインターやメンバー関数ポインターもサポートするようになっている。

興味本位でざっと実装したら以下のようになった。

// std::invoke
  template<typename Functor, typename... Args>
  typename std::enable_if<
    std::is_member_pointer<typename std::decay<Functor>::type>::value,
    typename std::result_of<Functor&&(Args&&...)>::type
  >::type invoke(Functor&& f, Args&&... args)
  { 
    return std::mem_fn(f)(std::forward<Args>(args)...); 
  }
   
  template<typename Functor, typename... Args>
  typename std::enable_if<
    !std::is_member_pointer<typename std::decay<Functor>::type>::value,
    typename std::result_of<Functor&&(Args&&...)>::type
  >::type invoke(Functor&& f, Args&&... args)
  { 
    return std::forward<Functor>(f)(std::forward<Args>(args)...); 
  }

  template<typename Return, typename Functor, typename... Args>
  Return invoke(Functor&& f, Args&&... args)
  {
    return invoke(std::forward<Functor>(f), std::forward<Args>(args)...);
  }

// simple callable wrapper
template < typename Functor, typename ... Args >
struct sfun_impl
{
    Functor f ;

    sfun_impl( Functor f ) : f( f ) { }

    auto operator() ( Args && ... args )
    {
        return invoke( f, std::forward<Args>(args)... ) ;
    }
} ;

// function pointer
template < typename R, typename ... Args >
sfun_impl< R (*)( Args ... ), Args ... >
sfun( R (*p)( Args ... ) )
{
    return sfun_impl< R (*)( Args ... ), Args ... >( p ) ;
}

// member function pointer with class pointer
template < typename R, typename C, typename ... Args >
sfun_impl< R (C::*)( C *, Args ...), C *, Args ... >
sfun( R (C::* p)( C *, Args ...) )
{
    return sfun_impl< R (C::*)( C *, Args... ), C *, Args ... >( p ) ;
}

// member function pointer with class reference
template < typename R, typename C, typename ... Args >
sfun_impl< R (C::*)( C &&, Args ...), C &&, Args ... >
sfun( R (C::* p)( C &&, Args ...) )
{
    return sfun_impl< R (C::*)( C &&, Args... ), C &&, Args ... >( p ) ;
}

// just to pass class type T
// lazy conditional can be used instead.
template < typename T >
void sfun( T && ) ;


template < typename T >
using sfun_if =
    typename std::conditional<
        std::is_function< typename std::remove_pointer< typename std::remove_reference<T>::type >::type >::value ||
        std::is_member_function_pointer<T>::value || 
        std::is_member_object_pointer<T>::value, 
            
        decltype(sfun( std::declval<T>() ) ), T >::type ;

// primary template
template < typename ... Functors >
struct overloader ;

// terminator
template < >
struct overloader < >
{
    void operator () () { }
} ;

template < typename Functor0, typename ... Functors >
struct overloader< Functor0, Functors ... > : sfun_if<Functor0>, overloader<Functors...>
{
    overloader( Functor0 && func0, Functors && ... funcs )
        : sfun_if<Functor0>( std::forward<Functor0>(func0) ),
          overloader<Functors ...>( std::forward<Functors>(funcs) ... )
    { }

    using sfun_if<Functor0>::operator () ;
    using overloader<Functors...>::operator () ; 
} ;



template < typename ... Functors >
overloader< Functors ... >
overload( Functors && ... functors )
{
    return overloader< Functors ... >( std::forward<Functors>(functors) ... ) ;
} 

また、提案では、登録順で呼び出し可能な最初の関数を呼び出す、first_overloadも提案している。


auto fo = first_overload (
    []( void * ) { },
    []( double ) { },
    []( int ) { } ) ;

fo( 123 ) ; // double

これは、おそらくtupleのようなものに格納した上で、is_callable(実装方法はN4446を参照)のようなメタ関数を適用するのだろう。

便利なライブラリだ。

[PDF] P0052R0: Generic Scope Guard and RAII Wrapper for the Standard Library

汎用RAIIラッパーとライブラリ版finallyの提案。

ヘッダーファイル<scope>をincludeすることで使える。

scope_exit

scope_exitは、ライブラリ版のfinallyだ。ブロックスコープを抜ける際に指定した関数オブジェクトを実行してくれる。

void f()
{
    scope_exit s( [](){/* 処理 */} ) ;

    // デストラクターが自動的に処理を実行してくれる
}

自動ストレージ上に確保する習慣をつけさせるため、make_scope_exit関数も用意されている。

unique_resource

汎用RAIIラッパー


void f()
{
    auto fp = make_unique_resource( fopen( ... ),
        []( FILE * fp )
        {
            if ( fp != NULl )
                fclose( fp ) ;
        } ) ;

        fread( ..., fp.get() ) ;

    // デストラクターがデリーターを実行してくれる
}

はやくほしい。

P0053R0: C++ Synchronized Buffered Ostream

同期バッファつきostream、osyncstreamの提案。

ストリーム出力は競合を起こさないと規定されているが、結果については規定されていない。そこで、出力をバッファして、途中に他の出力を挟まずに一括して出力できる機能の提案。


{
    std::osyncstream bout( std::cout ) ;
    bout << "hello," << "world" << std::endl ;
    // boutのデストラクターはバッファを一括して出力
}

P0054R0: Coroutines: Reports from the field

現在提案中のコルーチン、レジューム可能関数を実装しているコンパイラーがでてから丸一年になる。実際の使用経験から得られた知見をもとに、設計を見直す提案。様々な変更が提案されている。

P0055R0: On Interactions Between Coroutines and Networking Library

ネットワークライブラリの非同期処理は、futureによる継続ベースの処理と、コールバック関数による処理ができるが、コルーチンを使うとオーバーヘッドがさらに削減できるのでコルーチンに対応させる提案。

また、直接関係ないが、コルーチンは[[nodiscard]]の恩恵を受けるということが書かれていて面白い。await f()と書くべきレジューム可能関数fを、f()のみで使った場合はコンパイルエラーにできる。

P0056R0: Soft Keywords

ソフトなキーワードを導入する提案。

C++標準化員会の会議では、よく、「いいヤツは全部使われてるから、変なヤツを使わなきゃならん」という発言が行われる。ヤツというのは、キーワードのことだ。

最近の提案に含まれているキーワードを、既存のコードベースと比較するだけでも、await, concept, requires, synchronized, module, inspect, whenは、すでに変数名やフィールド名や引数名や名前空間名として使われている。

そこでこの提案では、ソフトキーワードを提案している。ソフトキーワードは、std名前空間に暗黙に定義されるエンティティである。

void yeild(int) ;

auto f()
{
    yeild( 1 ) ; // ::yeild
    std::yeild( 2 ) ; // キーワード
}

名前の衝突がない場合、using宣言を使うことでスコープ解決演算子を省略できる。

auto f()
{
    using std::yeild ;
    yeild( 3 ) ;
}

しかし、using宣言を書くのも面倒だ。提案では、ソフトキーワードに対する名前検索が何も見つけられなかった時に、ソフトキーワードだと認識するようにする提案もしている。

auto f() { yeild( 1 ) ; // std::yeild } void yeild( int ) ; auto g() { yeild( 2 ) ; // ::yeild }

ただし、この変更を受け入れた場合でも、テンプレートの場合は、依存名の解決のために、std::が必要だ。


template < typename T >
auto f( T t )
{
    yeild( 0 ) ; // 非依存名、std::yeild

    std::yeild( t ) ; // 依存名
}

typenameやtemplateと同じで、Two-Phase lookupについてはすでに知られているので、テンプレートコードを書くプログラマーならば対処できる些細な問題だろう。

ただし、using namespace stdを使った既存のコードが壊れる可能性がある。これを防ぐには、using directiveはソフトキーワードには働かない例外条項を付け加えればよいという提案もしている。

確かに便利だが、ものすごいツギハギ感がある。

[PDF] P0057R0: Wording for Coroutines (Revision 3)

コルーチンの文面案。

まだ、awaitとyeildのキーワードが固まっていない。

久しぶりに文面案を見ると、だいぶ変わっている。

まず、キーワードとして、await-keywordとyeild-keywordがある。このキーワードはプレイスホルダーで、将来的には何らかのキーワードが割り当てられる。

await式は、オペランドの式の実行が完了するまで、コルーチンの評価を停止させる。

auto read_from_disk()
{
    await read_gigabytes_of_file() ;
}

yeildキーワードは、await式を適用した後に、コルーチンのpromiseオブジェクトpに対して、p.yeild_value(yeilded value)を呼び出す。つまりシンタックスシュガーだ。yeild式は、generatorを作るのに使える。

auto gen( )
{
    for ( int i = 0 ; i < 10 ; ++i )
        yeild i ;
}

しかし、戻り値の型がよくわからない。await式のみが使われているとき、await式とyeild式が両方使われているとき、yeild式のみが使われているときで、型が変わる。どうも、その型はコルーチンpromiseを実装した型で、どうもユーザーが実装しなければならないようだ。

[PDF] P0058R0: An Interface for Abstracting Execution

様々な実行媒体を隠匿して扱えるライブラリの提案。

実行媒体には、スレッドプールを始めとして、ファイバーやGPUスレッドやSIMDなど様々なものがあるが、この提案では、それらの多種多様な実行媒体を効率的に隠匿できる設計にしていると主張している。

[PDF] P0059R0: Add rings to the Standard Library

リングアダプターの提案。既存のコンテナーをリングと呼ばれる固定サイズのキューとして扱えるコンテナアダプター。

既存のキューアダプターは、dequeやlistの上に構築されていて、動的にサイズを増加させる。これはメモリを断片化させる問題がある。リングアダプターは、arrayやvectorといった連続したストレージを使うコンテナーのアダプターで、動的にサイズを増やすことがない。

すでにBoostにはcircular_bufferがあるが、これはあまりにもでかすぎる。双方向である上にランダムアクセスイテレーターまで提供している。

インターフェースは既存のqueueを模倣している。ただし、pushとemplaceに加えて、try_pushとtry_emplaceが追加されている。これはリングが満杯の時に呼び出すと失敗する。

arrayがデフォルトのコンテナーになっているfixed_ringと、vectorがデフォルトのコンテナーになっているdynamic_ringがある。dynamic_ringという名前だが、サイズを実行時の初期化時に指定できるだけで、サイズがあとから伸長されることはない。後からサイズを増やしたければ、すでにあるqueueにvectorを指定して使えばいいだけだ。

ドワンゴ広告

明日はFallout 4が解禁されるために有給を取る。

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

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

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

2015-11-05

GoogleのエンジニアがAmazonで片っ端からType-C USBケーブルをレビューしている

Amazon.com: Profile for Benson Leung

GoogleのChromebook PixelのエンジニアであるBenson Leungが、アマゾンで売られているType-C対応を謳っているUSBケーブルとアダプターを片っ端からレビューしている。

なぜそんなことをしているのか。Googleの製品であるChromebook PixelはUSB Type-Cによる充電ができるが、巷に出回っている自称USB Type-C対応の製品の多くが、USB規格に違反していたり、十分な性能がない欠陥品だったりするからだ。

そもそも、USB Type-C規格は、3A, 5V, 15Wの電力供給ができる。3Aの電力供給は、途中の経路がすべてUSB Type-C対応の製品である場合に限る。例えば、途中にUSB 2.0などのレガシーケーブルを挟む場合は、USB Type-C規格準拠のケーブルは、それを検知し、3Aが供給できない状態にあることを通知するようになっている。

問題は、世の中の自称USB Type-C対応のUSBケーブルとアダプターには、Type-C規格で定められた方法で3A対応を謳いながら、実際には3Aもの電流を流せない粗悪な製品が多い。

そのような粗悪なUSBケーブルを使って充電を行うと、充電ができないか、充電はできるがUSBケーブル、ハブ、USBホストが損傷する危険がある。

以下はそのような粗悪な製品のレビューの一例である。

Amazon.com: Benson Leung's review of For the Christmas Gift !Monba Type c to US...

私はBenson、GoogleでChromebook PixelとPixel C開発部に所属している。このケーブルを購入して、様々なUSB micro-b typeのケーブルを使ってChromebook Pixelで検証してみたが、このアダプターはChromebook Pixelを充電できない。

詳しく調べてみると、このケーブルはUSB Type-C規格バージョン1.1に正しく準拠していない。規格はusb.orgのdevelopers/usbtypecにある。

このアダプターがChromebook Pixel 2015を充電dけいないのは、アダプターがC-Cラインを浮かせているからだ。規格ではRpがvbusをpullupしてケーブルがレガシーアダプターかケーブルであることを通知するよう規定している。

"USB Type-C Specification Release 1.1.pdf"という名前のドキュメントのセクション4.11と以下の注釈を参照されたし。

1. USB Type-CからUSB 3.1 Standard-Aケーブル、USB Type-CからUSB 2.0 Standard-Aケーブル、USB Type-CからUSB 2.0 Micro-Bアダプター、USBホストに接続されているUSB Type-C captiveケーブルに接続されているUSB Type-Cプラグは、56 kΩ ± 5% を使うことによって、V BUSとGNDにおけるIR低下に対応すること。

この製品はUSB Type-CをUSB 2.0 Micro-Bに接続するものであるから、製造者は56kΩの抵抗をpullupとして使わなければならない。このアダプターは使っていない。

Pixelと互換性のあるUSB Type-C規格に準拠したUSB Type-Cアダプターを探しているならば、このアダプターを使ってはならない。

ようやくまともな製品を発見した時のレビューは、記述に義憤が感じられる。

Amazon.com: Benson Leung's review of FRiEQ Hi-speed USB 3.1 Type C Cable - 3.3f...

これは私がアマゾンで発見できた最初のUSB規格準拠のSuperSpeedケーブルだ。

Amazon.com: Benson Leung's review of Type C, iOrange-E™ 6.6 Ft (2M) Braid...

このiOrange-E USB Type-AからType-Cへのケーブルは、サードパーティ(GoogleやAppleから提供されているものではないという意味)によって製造されたUSB製品における私の期待を上回るものである。

コネクターの品質はとても良い...略

技術面としては、このケーブルはUSB Type-C規格バージョン1.1に準拠している。これは正しくvbusに56kΩ pullupによって、正しくレガシーUSBケーブルのType-Cであると通知している。充電用としては、このケーブルはBC 1.2充電速度に対応している。最大で2.4A 5Vだ。

アマゾンで買える他のケーブルは、3A充電を対応していると主張しているが、USB Type-C規格に準拠しておらず、3A充電に対応しているかどうかを識別する抵抗を使わず、3Aに対応しないType-Aコネクターがもう一方に接続されているかどうかを確かめるすべがない。3A充電対応を謳うA-C変換ケーブルはすべて、レガシーUSBハブ、PC,充電器を破損する恐れがある。このiOrange-Eのケーブルは正しく規格に従っているため、その恐れはない。

さて、まとめると、これはいいケーブルだ。ようやくType-C規格に準拠したサードパーティケーブルが見つかった!

調査には以下の機器を使っているらしい。

USB-PD Sniffer - The Chromium Projects

2015-11-03

Linus Torvals、クソコードにブチギレ

Linux-Kernel Archive: Re: [GIT] Networking

Linus TorvalsがGCCの独自拡張を使った整数演算のオーバーフロー検知コードがあまりにクソすぎるためにブチギレしている。

On Wed, Oct 28, 2015 at 3:32 PM, David Miller <davem@xxxxxxxxxxxxx> wrote:

リリースサイクルのこの後半に入れるのはちょっと怖いと思われるかもしれないが、小規模なドライバーの修正をあちこちに施しただけだよ。

マジかよテメーら、こりゃクソだ。

コンフリクトはGCCの新しいクソヘッダーファイルのせいなんだが、俺がブチギレてるのはそこじゃなくてこいつがクソなせいだ。

net/ipv6/ip6_output.cの以前のコードはこれだ。

mtu -= hlen + sizeof(struct frag_hdr);

そして、こいつが新しい、「改良された」コードだ。最近流行りのコンパイラーマジックな組み込み機能を使っていて、その機能がない場合に備えて泥臭いラッパー関数を使ってる。

if (overflow_usub(mtu, hlen + sizeof(struct frag_hdr), &mtu) ||
mtu <= 7)
goto fail_toobig;

この上記のコードに対して、

(a) 読みやすい

(b) 効率的(しかもコンパイラーマジックまでサポートしてやがるぜ)

(c) とても安全

なんて考える奴は無能だしこの場にいる資格すらねぇ。

上記のコードはクソだ。しかもクソコードを生成しやがる。見た目がクソでしかも不必要なクソだ。

このコードは、簡単に、たった一つの、わかりやすい条件分岐だけで書けるし、しかもコンパイラーはもっとマシなコードを吐き出す上に、コードは見た目にマシでわかりやすい。なんでこうしないんだ。

if (mtu < hlen + sizeof(struct frag_hdr) + 8)
goto fail_toobig;
mtu -= hlen + sizeof(struct frag_hdr);

これは同じ行数だし、何をするか誰も知らんキチガイみたいなヘルパー関数使う必要がないし、実際に何をするか明白だ。

こっちの明白版の方が読むのも簡単だし理解するのも簡単だ。反論はあるか?

いやマジで、なんで2つも条件分岐を使うマヌケなコードと、ピカピカの真新しいコンパイラーサポートを必要とする非標準の関数を使って、まともに見えないコードでクソコードを吐かせる必要があるのか、誰か教えてくれよ。

他では使う必要がないピッカピカのこの関数は、単にコンパイラーがシコってるだけだろ。

ああ、それから、"hlen + xyz"という式がオーバーフローしたら、やっぱりオーバーフローは起こるだろ。だが、"overflow_usub()"コードにも同じ問題がある。というわけでその心配をしているのならば、そもそも最初から間違ってるってこった。

この手の完全にアホくさいクソが存在する理由がわからん。

いや、この完全にキチガイな上にrc7というこの時期にコンフリクトを起こすようなものを俺がpullするわけがねーし、読めないほどクソになる理由すらないんだが、どういうこった?

このコードは、新しい"overflow_usub()"コードを使いたいために設計されたかのように思える。関数を使う言い訳としてな。

頭イカれてる言い訳にすらならんぞ。

申し訳ございませんが、こちらと致しましてもこのようなよろしくないコードのために好ましくないインターフェースを追加するわけにはまいりません。

まあ、これがネットワークレイヤーの仲にいたならば、俺も気が付かなかっただろうよ。だが俺が気づいたからには、こいつは絶対pullしねぇ。いいかお前らみんなに知っていてもらいたいんだが、こういうコードは絶対受け付ける気がない。こういうコードが最近流行りのオーバーフロー検出を使っているから「安全」だとか「セキュア」だとか考える奴は、場違いにも程があるし、冗談にしても笑えねぇ。

このクソが成し遂げた唯一のことは、まともな人間が読めないコードを作り出しただけだ。

取り除け。俺はこんなクソをもう二度と見たくねぇ。

Linus。

2015-11-01

MicrosoftがPDBフォーマットの構造体定義のソースコードを公開

Microsoft/microsoft-pdb

MicrosoftがPDBファイルを扱うのに使っているMSVCのソースコードの一部をMITライセンスで公開している。ソースコード、cvinfo.hの中身の大半は、構造体定義のようだ。

このレポジトリは、MicrosoftにおけるPDB(プログラムデータベース) シンボルファイルフォーマットの情報を載せている。

PDBフォーマットはこれまで公式のドキュメント化されておらず、他のコンパイラーやツールセット(例えばClang/LLVM)がWindows上で動作したり、Visual Studioデバッガーとのやり取りを困難にしていた。MicrosoftはオープンソースコンパイラーがWindowsプラットフォームで動作するのを支援する。

このレポジトリの中身の大半は、VC++コンパイラーツールセットにおける実際のソースコードである。ソースコードは究極のドキュメントである(^_^;)。役に立てば幸いだ。もし、他の情報が必要であれば、Issuesを投げて知らせてもらいたい。

シンボルファイルフォーマットが今まで公式に非公開だったことに驚いている。

2015-10-29

C++標準化委員会の文書のレビュー: P0040R0-P0048R0

P0040R0: Extending memory management tools

イテレーターを取り、イテレーターの範囲の未初期化のストレージに対して、構築や破棄をする関数destroy, uninitialized_move, uninitialized_move_n, uninitialized_value_construct, uninitialized_default_constructの追加。

たとえば、destroyは以下のように実装できる。

template < typename ForwardIterator >
void destroy( ForwardIterator begin, ForwardIterator end )
{
    using type = iterator_traits<ForwardIterator>::value_type ;
    while ( begin != end )
    {
        begin->~type() ;
        ++begin ;
    }
}

P0041R0: Unstable remove algorithms

unstable_removeアルゴリズムの提案。

従来のeraseは、消した要素の穴を、後続の要素をシフトさせることによって埋める必要がある。これは、要素の順序を変えない安定した動作が要求されているためである。

std::vector<int> v = { 1, 2, 3, 4, 5, 6, 7, 8, 9 } ;

// v = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }
std::remove( begin(v), end(v), 2 ) ;
// v = { 1, 3, 4, 5, 6, 7, 8, 9, ? }   

要素を一つづつコピー/ムーブして穴を埋める動作は、要素の具体的な型にもよるが、コストがかかる。

ここで、もし要素の順序を変える不安定な動作が許されるのであれば、末尾の要素で埋めるなどして、コピー/ムーブの回数を減らせる。

// v = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }
std::unstable_remove( begin(v), end(v), 2 ) ;
// v = { 1, 9, 3, 4, 5, 6, 7, 8, ? }   

そこで、unstable_removeの提案となる。他にも、unstable_remove_if, unstable_remove_copy, unstable_remove_copy_ifも追加される。

提案では拡張案として、メンバー間数版であるeraseにもunstable_eraseを加える案が考察されている。

P0042R0: std::recover: undoing type erasure

function, any, variant, optionalなど、C++にはtype erasureを使ったライブラリが多い。問題は、ライブラリごとに元の型を得る方法がバラバラで、全く統一されていないということだ。たとえば、functionはtargetメンバー関数を使うし、anyはany_castというフリー関数を使う。variantはstd::getをオーバーロードしていて、std::optionalは、型が入っていないとbad_optional_access例外を投げる。

今後、type erasureを使うライブラリが増えるたびに、bad_library_name_accessなる例外クラスが増えるのでは、ますます標準ライブラリを肥大化させるだけだ。元の型を得るという同じような処理は、共通の汎用的な方法で扱えるべきだ。

そのために、この提案では、ErasureClassコンセプトを定義し、type erasureを行うクラスが実装すべきメンバーの要件を定義している。

そして、ErasureClassコンセプトを使って元の型を取り出すフリー関数recoverが追加される。

void f() { }

int main()
{
    function< void () > a = &f ;
    std::experimantal::any b = &f ;

    auto c = a->target() ;
    auto d = any_cast< void (*)() >( b ) ;
}

このようにバラバラの方法で取り出していたのが、

auto c = std::recover<void (*)()>(a) ; 
auto d = std::recover<void (*)()>(b) ; 

このように統一化された方法で取り出すことができるようになる。型チェックに失敗すると、bad_recovery_access例外が投げられる。

[PDF注意] P0043R0: Function wrappers with allocators and noexcept

functionをアロケーターに対応させるにあたって、type erasureを使ってアロケーターを格納する方法を採用した。これは様々な問題を生む。たとえばnoexceptにできなくなる。

そこで、functionの挙動変更と、ひとつのアロケーターオブジェクトを要素であるfunctionに渡すfunction_containerを提案している。

文章がやたらと不親切で読みにくい上に、提案している機能が不必要に複雑で、一般人が使いこなせるとは思えない。

[PDF注意] P0044R0: unwinding_state: safe exception relativity

uncaught_exceptionsをdeprecatedさせて、新たにunwinding_stateを導入する提案。

uncaught_exceptionは、ハンドルされていない例外が存在するかどうかをbool値で返す。つまり、例外がthrowされたことによるstack unwindingが発生しているかどうかを調べることができる。これは、デストラクターの挙動を変えるのに使える。

struct X
{
    ~X()
    {
        if ( std::uncaght_exception() )
        {
            // 通常の破棄
        }
        {
            // do_somethingが例外を投げたので
            // stack unwinding中
        }
    }
} ;

int main()
{
    try {
        X x ;
        do_something() ;
    } catch ( ... ) { }
}

問題は、stack unwindingの最中にも、例外スコープは作成されるということだ。

struct Y
{

    ~Y()
    {
        if ( std::uncaught_exceptions() )
        {
            try {
                X x ;
                do_recovery_thing() ;
            } catch( ... ) { }
        }
    }
}

この場合は、通常のtryスコープを抜けたxの破棄であっても、xのデストラクターはstack unwinding中であると認識してしまう。

これを防ぐために、uncaught_exceptionsが追加された。これは、現在ハンドルされていない例外の数をint値で返す。

問題は、未ハンドルの例外の数を返すということは、現在の未ハンドルの例外数をどこかに保存しておいて、それを比較しなければならないということだ。

struct X
{
private :
    static int e ; // 別のところで書き込まれる
public :
    X() : e( std::uncaught_exceptions() ) { }

    ~X()
    {
        if ( std::uncaught_exceptions() == e )
        {
            // 通常の破棄
        }
        else
        {
            // stack unwinding中
        }
    }

} ;

uncaught_exceptionsを正しく使うには、このように例外が発生する前に例外数を記録しておいて、例外が発生しているかもしれない箇所で、前回の例外数と異なるかどうかを比較しなければならないのだ。

そもそも、例外数を返すというのは、Itanimu ABIに例外数を記録するためのグローバル変数があるからだ。他の実装も考えられる。例えば、ARM EHABIは、現在未ハンドルの例外をリンクリストで管理することを規定している。

現在の例外数を取得することに需要はない。需要は、現在stack unwinding中かどうかを判定することだ。そのため、例外ハンドルの状況が変わったかどうかだけを調べる方法があればよい。

そこで、unwindling_stateクラスが提案されている。このクラスは、非staticメンバーのガードオブジェクトとして使うことができる。

struct X
{
private :
    std::unwinding_state s ;
public :

    ~X()
    {
        if ( 1s )
        {
            // 通常の破棄
        }
        else
        {
            // stack unwinding中
        }
    }

} ;

unwinding_stateのコンストラクターは、現在のstack unwindingの状態を記録する。operator boolは、現在のunwindingがオブジェクト構築時に保存したunwindingと同一かどうかをboolで返す。

operator boolはexplicitとなっているので、lvalueとして使わなければならない。以下のような誤った記述はできない。

if ( unwinding_state() )

コピーやムーブは普通にできる。

[PDF注意] P0045R0: Overloaded and qualified std::function

オーバーロード可能なfunctionの提案。既存のstd::functionにVariadic Templatesで複数のシグネチャを指定可能にするというもの。

以下のようなコードが書けるようになる。

int main()
{
    std::function< void ( int ) , void ( std::string ) > f ;

    f = []( int ) { } ;
    f = []( std::string ) { } ;

    f = []( auto ) { } ;

    f( 0 ) ;
    f( std::string("hello") ) ;
}

これを実現するために、target型ごとにグローバルなtupleを作ってディスパッチさせる、いわゆるvtableを実装している。正直ついていけない。

しかし思うのだが、コア言語にあるvtableが非効率的なので独自vtableを自作しましたというのはなんだか本末転倒ではないか。もちろん、通常の関数ポインターまで含めたtype erasureを、本来その目的に設計されていなかった組み込みvtableで実装するので、そういう非効率さが発生するのだが。

P0046R0: Change is_transparent to metafunction

連想コンテナーでHeterogeneous lookupを実現するために、比較関数にis_transparentというネストされた型があるかどうかをヒントにしてきたが、これは使いにくいので、permits_heterogenous_lookupというtraitsを追加する提案。

正直すごく使いづらい。

P0047R0: Transactional Memory (TM) Meeting Minutes 2015/06/01-2015/09/21

トランザクショナルメモリー会議の議事録

P0048R0: Games Dev/Low Latency/Financial Trading/Banking Meeting Minutes 2015/08/12-2015/09/23

ゲーム開発、低レイテンシー、アルゴリズム取引、銀行に関する会議の議事録。

ドワンゴ広告

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

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

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

Visual StudioにClangフロントエンドがやってくる

22日に書いたのだがあげ忘れていた。

Microsoft promises Clang for Windows in November Visual C++ update • The Register

先月のCppConでMicrosoftが発表した内容によれば、Visual StudioにClangがやってくるそうだ。

Microsoftは独自のプロプライエタリなC++コンパイラーを開発しているが、将来、Visual StudioでClangが扱えるようになるそうだ。

現在、AndroidやiOS開発用には、Visual StudioでもGCCやClangが使えるが、Windows用にもClangが提供されるらしい。

MSのC++コンパイラーには、フロントエンド部分(パースして内部表現に変換)とバックエンド部分(内部表現からコード生成)があるが、Clangが使われるのはこの内のフロントエンド部分になる。ClangでLLVM IRを生成して、そこからMSのプロプライエタリなバックエンドが期待する内部表現フォーマットに変換して、コード生成は自前のプロプライエタリなコンパイラーで行うそうだ。

MS独自のプロプライエタリなTwo-phase lookupすらまともにできない規格違反のクソC++コンパイラーの開発も続行されるらしい。

ドワンゴ広告

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

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

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

ある程度自由なラップトップ、Novena

Novena: A Laptop With No Secrets - IEEE Spectrum

ある程度自由なラップトップを作ったそうだ。

ムーアの法則が破れてきた今となっては、小規模なハードウェア制作もやりやすくなっているとのこと。というのも、このプロジェクトがスタートしてから数年たっているが、当初選んだ40nmプロセスは、いまだにコストとパフォーマンスを天秤にかけてちょうどいい釣り合いがとれている。また、中国の製造業者も、少数製造にも興味を示しているとのこと。

できるだけファームウェアブロブの少ないパーツを選定したとのこと。CPUにはFreescaleのARMプロセッサー、i.MX6 SoCを選んだ。ファームウェアはROMに内蔵されているが、IntelのCPUと違い、アップデートできないし、かつ暗号化もされていないとのこと。

Freescaleの提供するパッチをLinuxのコード規約にあわせるなどして、Linuxカーネルの上流に採用させたとのこと。

内蔵GPUはプロプライエタリなバイナリブロブのドライバーを必要とする。このドライバーを使うと、Linuxカーネルのバージョンが固定されてしまう上に、ドライバー自体もクソで、浮動小数点演算をソフトウェアで行っているとのことだ。

そのため、内臓GPUは使わず、ソフトウェアですませる。このため、WMにはXfce4を使い、ディストロはDebianを使っているとのことだ。

気に入らないのは、FPGAが載っていることだ。FPGAのツールは不自由であるし、詳細はわからないが、もしそのような不自由なハードウェアがメモリにDMAアクセスできるようになっているのは危険だ。

どうもいまひとつ惜しいコンピューターだ。

2015-10-26

C++標準化委員会の文書のレビュー: P0030R0-P0039R0

[PDF] P0030R0: Proposal to Introduce a 3-Argument Overload to std::hypot

C++11で追加されたstd::hypotに3引数版のオーバーロードを追加する提案。

std::hypot( x, y )は、\(\sqrt{ x^2 + y^2}\)を、計算過程でオーバーフロー、アンダーフローが発生しない方法で計算する関数だ。

hypotの応用方法として、二次元空間における2点間の距離の計算、\(\sqrt{(x_1 - x_2)^2 + (y_1 - y_2)^2}\) に使える。

struct point
{ double x, y ; } ;

double dist( point a, point b )
{
    return std::hypot( a.x - b.x, a.y - b.y ) ;
}

しかし、多くの分野では、三次元空間における2点間の距離を計算、、\(\sqrt{(x_1 - x_2)^2 + (y_1 - y_2)^2 (z_q - z_2)^2}\)したいことがよくある。hypotは2引数なので、現状では以下のように使わなければならない

struct point
{ double x, y, z ; } ;

double dist( point a, point b )
{
    return std::hypot( a.x - b.x, std::hypot( a.y - b.y, a.z - b.z ) ) ;
}

hypotに3引数版のオーバーロードを追加すると、以下のように書ける。


std::hypot( a.x - b.x, a.y - b.y, a.z - b.z ) ;

論文では、Variadic templatesを利用した任意個の引数を取る汎用的な関数の提案も考えたが、そのような関数の利用例が明らかではないとして、今回の提案では3引数版のみにとどめたという。

P0031R0: A Proposal to Add Constexpr Modifiers to reverse_iterator, move_iterator, array and Range Access

array, reverse_iterator, move_iteratorをconstexprに対応させる提案。

P0032R0: Homogeneous interface for variant, any and optional

現在提案されているvariant, any, optionalには、機能は同じだがメンバー名や引数などが異なるメンバーがあるので、統一を図る。

P0033R0: Re-enabling shared_from_this

現行のenable_shared_from_thisの文面では、挙動が明確に規定されていないコードが存在してしまう。

enable_shared_from_thisは、shared_ptrで管理する型の基本クラスとすることで、shared_from_thisというメンバー関数を追加できる。このメンバー関数を呼び出すと、そのオブジェクトを所有している既存のshared_ptrが返される。以下のように使える。

// CRTPとして使う
struct X : public std::enable_shared_from_this<X>
{ } ;

int main()
{
    auto sp1 = std::make_shared<X>() ;

    // sp1と所有権を共有するshared_ptrが得られる
    auto sp2 = sp1->shared_from_this() ; 
}

ところが、現行の文面では、以下のようなコードの挙動が未定義になってしまう。

int main()
{
  struct X : public enable_shared_from_this<X> { };
  auto xraw = new X;
  shared_ptr<X> xp1(xraw);  // #1
  { // 破棄時にdeleteしないので安全
    shared_ptr<X> xp2(xraw, [](void*) { });  // #2
  }
  xraw->shared_from_this();  // #3
}

shared_from_thisの前提条件として、オブジェクトを所有するshared_ptrのオブジェクトが存在しなければならない。このコードでは、存在している。問題は、複数のshared_ptrが独立して存在しているということだ。ただし、後から構築される#2のオブジェクトのデリーターは何もしないので、このコードは二重にdeleteするという問題はない。すると、このコードは合法なのだろうか。

しかし、合法だとして、この場合はどちらのshared_ptrと所有権を共有するオブジェクトが返るのだろうか。#1だろうか、#2だろうか。

#1が返る場合、#2のshaared_ptrの構築時には、xraw->_weak_thisがアップデートされないことになる。#2が返る場合、アップデートされる。

問題は、shared_from_thisのpostconditionに照らし合わせると、どちらの挙動もweak_ptrがらみのpostconditionを満たせないので、規格上挙動が規定されていない。

さて、既存の実装であるDinkumware, GNU, LLVMの実装は、いずれも#2が_weak_thisをアップデートする挙動になっている。これは設計上意図的なものではない。

一方、Boostの実装では、ユーザーの意見を取り入れた結果、#2は_weak_thisをアップデートしない挙動に意図的にしている。つまり、一番最初に作られたshared_ptrと所有権を共有するshared_ptrを返す。

現在、既存の実装の挙動に依存したコードはなく、Boostの挙動の方が実際の需要があるため、Boostの挙動にあわせる変更を提案している。

また、この提案では、weak_ptrを得るweak_from_thisの追加も提案されている。

P0034R0 – Civil Time

軽量な日付ライブラリの提案。

Boost. Date Timeはあまりにも巨大すぎるし、作者が標準ライブラリに追加することに興味を示していないので、軽量な日付ライブラリを提案している。

このライブラリは、グレゴリオ暦以前の日付はtime_tのオーバーフロー、time_t以上の精度でうるう秒を扱うことは考慮しない。タイムゾーンが使える。

P0035R0: Dynamic memory allocation for over-aligned data

待望のオーバーアライメントしてくれる確保関数の提案。

C++の現行規格では、確保関数はオーバーアライメントする必要はない。例えば、floatのアライメントが4バイトで、SIMD命令の都合上、floatの配列を16バイトアライメントしたいとする。以下のようなコードを書いても、16バイトアライメントされる保証はない。

class alignas(16) float4 {
 float f[4];
};
float4 *p = new float4[1000];

C++の規格上は4バイトアライメントされれば十分なのだ。オーバーアライメントは規格上必須ではない。

シグネチャは以下のようになる。

namespace std {
    enum class align_val_t: size_t;
}
void* operator new(std::size_t size, std::align_val_t alignment);
void* operator new(std::size_t size, std::align_val_t alignment,
   const std::nothrow_t&) noexcept;
void operator delete(void* ptr, std::align_val_t alignment) noexcept;
void operator delete(void* ptr, std::align_val_t alignment,
   const std::nothrow_t&) noexcept;
void* operator new[](std::size_t size, std::align_val_t alignment);
void* operator new[](std::size_t size, std::align_val_t alignment,
   const std::nothrow_t&) noexcept;
void operator delete[](void* ptr, std::align_val_t alignment) noexcept;
void operator delete[](void* ptr, std::align_val_t alignment,
   const std::nothrow_t&) noexcept;

P0036R0: Unary Folds and Empty Parameter Packs (Revision 1)

folding expressionに空のパラメーターパックを指定した時のフォールバックを、operator &&, operator ||, operator , だけに留める提案。

デフォルトで0が変えるとまずい場合がある。

P0037R0: Fixed_Point_Library_Proposal

固定少数点ライブラリの提案。

template < class ReprType, int Exponent >
class fixed_point ;

ReprTypeは内部でつかう整数型で、Exponentは、ビットを指定された数だけシフトする。fixed_pointの精度は、pow(2, Exponent)であり、最小値と最大値は、pow(2, Exponent)にstd::numeric_limites<ReprType>::min()/max()を掛けた値になる。

Exponentを指定するのは面倒だ。固定少数は、基数ビット数と少数ビット数を記述することで指定できる。プログラマーの多くはこの記述方法を好む。

そこで、fixed_pointのエイリアスが用意される。

template <unsigned IntegerDigits, unsigned FractionalDigits = 0, bool IsSigned = true>
    using make_fixed ;

template <unsigned IntegerDigits, unsigned FractionalDigits = 0>
    using make_ufixed ;

例えば、8bitの符号なしの固定少数で、基数と少数のビット数にそれぞれ4ビット割り当てたい場合は、

make_ufixed<4, 4> value{ 15.9375 } ;

32bit、符号あり、基数が2ビット、少数が29ビットにしたい場合は、

make_fixed<2, 29> value { 3.141592653 } ;

固定少数と組み込みの整数型は相互に明示的に変換できる。

丸め誤差は発生する。

make_ufixed<4, 4>(.006) == make_ufixed<4, 4>(0)

このコードは丸められた結果trueとなり得る。

整数に普通に使える演算子は固定少数にもオーバーロードされていて普通に使える。シフト演算子と比較演算子以外では、二項演算子は固定少数同士や、固定少数と他の演算型を組み合わせてオペランドに取れる。

組み合わせが同じ固定少数型ではない場合、簡単なプロモーション風のルールによって、戻り値の型が決定される。

  1. どちらの実引数も固定少数の場合、大きなサイズの型が選ばれる。どちらか片方が符号付きならば、符号付きになる
  2. どちらかの実引数が浮動小数点数型の場合、結果の型は入力と同じかそれ以上大きい、最小の浮動小数点数型になる
  3. どちらかの実引数が整数型の場合、結果は他方の固定少数型になる

make_ufixed<5, 3>{8} + make_ufixed<4, 4>{3} == make_ufixed<5, 3>{11};  
make_ufixed<5, 3>{8} + 3 == make_ufixed<5, 3>{11};  
make_ufixed<5, 3>{8} + float{3} == float{11};  

提案では、このプロモーションルールの理由を、挙動を簡単に推測できるためとパフォーマンス上の理由から、各ルールについて以下のように説明している。

  1. 固定少数のみが使われていた場合に、最小の計算のみが行われることを保証する。
  2. 型を混ぜた演算のプロモーションルールをまねた。固定少数の範囲から外れるようなexponentを持つ値も作り出せるようにし、浮動小数点数から整数へのコストのかかる変換を避ける
  3. 入力として与えられた固定少数型を維持する。処理に重要なのは意図的に固定少数型である

シフト演算子は右辺に整数型を取り、オーバーフロー、アンダーフローしない新しい型を返す。

比較演算子は、プロモーションルールにしたがって入力を共通の型に変換した上で、比較して、結果をtrueかfalseで返す。

符号付きと符号なしの固定少数のオーバーフローは、未定義の挙動となる。符号なしの整数型のオーバーフローの挙動が規定されている基本型とは異なる。

// オーバーフローの例
make_fixed<4, 3>(15) + make_fixed<4, 3>(1)

アンダーフローが発生して精度が失われることは許容されている。


make_fixed<4, 3>(15) + make_fixed<4, 3>(1) ;

この結果は7になるが、これは許容されている。

ただし、すべてのビットがアンダーフローによって失われた場合、その値は「フラッシュされた」状態になり、未定義の挙動となる。

固定少数の精度を上下させるpromote/demote関数がある。これは、基数部と小数部のビット数をそれぞれ、2倍、1/2倍する。

// make_fixed< 4, 4 >
promote( make_fixed< 2, 2 >(1) ) ;
// make_fixed< 2, 2 >
promote( make_fixed< 4, 4>(1) ) ;

オーバーフローを防ぐために精度を上げつつ計算する関数が用意されている。

単項演算子としては、trunc_reciprocal, trunc_square, trunc_sqrt, promote_reciprocal, promote_square

二項演算子としては、trunc_add, trunc_subtract, trunc_multiply, trunc_divide, trunc_shift_left, trunc_shift_right, promote_add, promote_sub, promote_multiply, promote_divide

trunc_は、入力よりも大きくはない結果を返すが、exponent部分をオーバーフローを避けるために変更される。

promote_は、オーバーフローとアンダーフローが起きないほど十分に大きな型を結果として返す

_multiplyと_squareは、64bit型には提供される保証がない。

_multiplyと_squareは、入力が最小負数である場合、未定義の挙動となる。

_squareは符号なし型を返す

_divideと_reciprocalは、ゼロ除算チェックは行わない

trunc_shift_は、最初の入力と同じ型を返す。

関数はまだ追加される余地がある。

P0038R0: Flat Containers

Boost.ContainerにあるFlat Associative Container(flat_map, flat_set, flat_multimap, flat_multiset)の追加。

フラット連想コンテナーとは、従来の連想コンテナーであるmapやsetのように、キーと対応する値をもっていて、キーによって値を高速に検索できる特性を持つ。従来のノードベースの連想コンテナーと違い、連続したストレージ上に要素が確保される。

実装方法は2つある。ひとつは要素を常にキーでソート済みにしておくこと。もうひとつはヒープ構造を用いること。Boostの実装はソートを使っている。ヒープを使った実装はキャッシュのローカル性を保ちやすいので、理論上優れた実装であるとされ、会議では関心が高かったが、実装経験が少ないため、さらなる検証が必要であるとしている。

P0039R0: Extending raw_storage_iterator

raw_storage_iteratorを改良する提案。

raw_storage_iteratorにムーブ代入演算子を追加し、ムーブに対応させる。

raw_storage_iteratorを作るのは面倒なので、factory関数を追加する。

template<class T>
auto make_storage_iterator( T&& iterator)
{
 return raw_storage_iterator<std::remove_reference<T>::type, decltype(*iterator)>( std::forward<T>(iterator));
}

raw_storage_iteratorの目的は、主にplacement newで使うためだが、placement newの文法はraw_storage_iteratorをサポートしていない。これを直接サポートする。

template<class T, class U>
void* operator new(size_t s, raw_storage_iterator<T,U> it) noexcept
{
 return ::operator new(s, it.base() );
}

template<class T, class U>
void operator delete ( void* m, raw_storage_iterator<T,U> it) noexcept
{
 return ::operator delete(m, it.base() );
}

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。来月は有給を取りまくる必要のある事情がある。

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

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

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

2015-10-20

C++標準化委員会の文書のレビュー: P0021R0-P0029R0

P0021R0: Working Draft, C++ Extensions for Ranges

Range TSのドラフト。コンセプトを使ったRangeライブラリの提案。

イテレーターのペアを扱うよりも、Rangeという単位で簡単に扱うことができる。

ユーザーにわかりやすい恩恵としては、algorithmにイテレーターのペアではなくコンテナーを渡せるようになる。単に使う際にはコンセプトを意識する必要はない。

P0022R0: Proxy Iterators for the Ranges Extensions

vector<bool>のようなプロクシーイテレーターをRangeに対応させるためにRange提案を修正する提案。既存のiter_swapに加えて、iter_moveを追加して、プロクシーイテレーターのためのカスタマイゼーションポイントとする。

コンセプトにマップがあればもっと楽になるのに。

P0024R0: The Parallelism TS Should be Standardized

既存のalgorithmを並列実行させ、また並列計算のためにnumericライブラリを追加するParallelism TSを、標準規格に取り入れるべきだという提案。

Parallelism TSの実装経験は豊富であり、C++17に加えるに足る。

Parallelism TSは、たとえば、以下のようにvector<int>の各要素に処理を加えるコードがある。

void process_data( std::vector<int> const & data )
{
    std::for_each( std::begin(data), std::end(data),
        []( auto e ) { DoSomething(e) ; } 
    ) ;
}

このコードは、以下のように変更を加えるだけで、並列実行させることができる。


std::for_each( std::par, std::begin(data), std::end(data), ...

このように、既存のalgorithmに並列実行版のオーバーロードを加える。また、並列計算用のnumericが用意されている。

すでに、Microsoft, HPX, Codeplay, HSA, Thibaut Lutz, NVIDIAが独立してParallelism TSを公に実装している。

P0025R0: clamp: An algorithm to 'clamp' a value between a pair of boundary values (Draft) -

標準ライブラリにclamp( value, min_value, max_value )を追加する提案。valueがmin_valueより小さいならmin_valueを、max_valueより大きいならmax_valueを、そうでなければvalueを返す。

比較関数をとるオーバーロード、clamp( value, min, max, comp)も追加される。

P0026R0: multi-range-based for loops

Range-based forの拡張提案。

range-based forはコンテナーの各要素に対する処理を簡単に書くことができる。

std::vector<int> v ;

for ( auto && elem : v )
{
    DoSomething( elem ) ;
}

しかし、現実では、複数のコンテナーのそれぞれの各要素を先頭から同時にアクセスしたいことがよくある。そういう場合、古典的なforループを使わざるを得ない。

std::vector<int> v1, v2 ;

auto iter1 = v1.begin() ;
auto iter1_end = v1.end() ;
auto iter2 = v2.begin() ;
auto iter2_end = v2.end() ;

for( ; iter1 != iter1_end && iter2 != iter2_end ;
    ++iter1, ++iter2 )
{
    DoSomething( *iter1, *iter2 ) ;
}

そこで、range-based forを拡張して、セミコロンで区切ることで、複数のRangeを受け取れるようにする。

// P0026提案

for ( auto && e1 : v1 ; auto && e2 : v2 )
{
    DoSomething( e1, e2 ) ; 
}

レンジの要素の数が同じではない場合、要素数が少ないレンジの終端に達した時点でループが打ち切られる。

P0027R0: Named Types

名前付きの型を作り出す機能の提案。

typedef名は、型に別名をつけることができる。

typedef double Force ; typedef double Mass ; typedef double Accel ; Force calc_force( Mass m, Accel a ) { return m * a ; }

問題は、typedef名はあくまで別名であって、新しい型ではない。そのため、double型を渡すことができる。

void f( Object o )
{
    Mass m = o.get_mass() ;
    Accel a = o.get_accel ;

    // コンパイルエラーにならない
    Force f = calc_force( m, m ) ;
}

これを解決して、論理的に間違っている場合にコンパイルエラーを出すためには、新しい型を作り出す必要がある。

newtype double Force ;
newtype double Mass ;
newtype double Accel ;

提案では、何らかの新しいキーワード(仮にnewtype)を使って新しい型名を宣言する文法を提案している。まだ具体的な文面案がないので、どのようになるかは詳しく書かれていない。

少なくとも、暗黙の型変換は阻害されて欲しいし、テンプレート実引数で異なる特殊化をされてほしい。

提案は先行事例としてAdaを挙げている。また、Adaにはある値の範囲を定める機能: newtype Age is int(0...130)のような案も漏らしている。

P0028R0: Using non-standard attributes

attributeは、アノテーションを記述するのに便利なので、すでによく使われている。たとえば、| Reengineering and Enabling Performance and poweR of Applicationsでは、attributeを使って、GPUやDSPに処理をオフロードするアノテーションを記述できる。

問題は、inとかoutとかの短くてわかりやすい単語を使いたいが、他の実装依存のattributeとかぶるとまずいため、attribute名前空間を用いている。これは記述を煩雑にする。

void f() {
    [[ rpr::kernel, rpr::target(cpu,gpu)]]
    do_task();
}

そこで、同一のattribute内であるattribute名前空間内のattributeトークンをグローバルスコープに持ち込むための機能を提案している。

void f() {
    [[using(rpr), kernel, target(cpu,gpu)]]
    do_task();
}

P0029R0: A Unified Proposal for Composable Hashing

unorderedコンテナーのキーは、ハッシュ値を計算できる必要がある。基本型と標準ライブラリの型はstd::hashでハッシュ値が計算できる。しかし、ユーザーがハッシュ計算可能な型を組み合わせたクラスを作った場合、標準ライブラリはそのような型にハッシュ値を計算させる簡単な方法を提供していない。ユーザーが独自の方法でハッシュ値を計算しなければならない。

ハッシュ値の計算は素人には難しい。そこで、既存のハッシュ計算可能な型のオブジェクトを任意個ぶち込めばハッシュを計算してくれるライブラリhash_valueとhash_combineを提案している。

struct Foo { int i ; std::string str ; bool b ; friend std::hash_code hash_value( std::hash_code h, const Foo & foo ) ; } ; std::hash_code hash_value( std::hash_code h, const Foo & foo ) { return std::hash_combine( std::move(h), foo.i, foo.str, foo.b ) ; }

このようにすれば、Foo型はstd::hashでハッシュ計算ができるようになり、unorderdコンテナーのキーとして使うことができるようになる。

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

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

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

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

2015-10-13

ゲーム用のPCを買ってゲームを始めた

東京に引越してからというもの、まともなゲームをしていなかった。ゲームをするにはゲーム用の高スペックなPCが必要だが、引越してから色々とやることがあったので、つい他のことを優先してしまった。

しかし、ゲームは重要である。幸い、引越しをして落ち着いた空間を手に入れたのと、Fallout 4が発売されるので、この機会に高スペックなPCを購入した。

以下が筆者のSteamのプロファイルだ。友人申請は気軽にしてよい。

Steam Community :: ezoe

とりあえず遊びたかったゲームを少しづつ消化しているが、本命はFallout 4だ。

C++標準化委員会の文書 2015-09 pre-Kona: P0011R0-P0020R0

P0011R0: Additions to Filesystem supporting Relative Paths

filesystemライブラリに相対パスを作り出す関数の追加。

以下のように使える。

using namespace std::experimental::filesystem ;
path Path  = "/a/d";
path Start = "/a/b/c";

auto RelPath = relative( Path, Start );

assert( RelPath == path("../../d") );
assert( Path == normalize( Start/RelPath ) );

P0012R0: Make exception-specifications be part of the type system, version 4

例外指定を型システムに含める提案。

現在、例外指定は型システムに含まれていない。例外指定された関数へのポインターについて僅かに言及があるだけである。これにより、違法であるべきコードが合法になってしまったり、合法になって欲しいコードが違法になってしまったりする。解決には、例外指定を型システムに含めるしかない。

P0013R0: Logical Operator Type Traits

任意個のtraitsを受け取って論理値を返すand_, or_, not_の追加。それぞれ以下のように宣言される。

template < typename ... B >
struct and_ ;
template < typename ... B >
struct or_ ;
template < typename ... B >
struct not_ ;

以下のように使う。

template < typename T >
void f()
{
    constepxr bool b = and_< std::is_copy_constructible<T>, std::is_copy_assignable<T> >::value ;
}

また、これの変数テンプレート版である、and_v, or_v, not_vも提供される。

[PDF注意] P0014R0: Proposal to add the multiline option to std::regex for its ECMAScript engine

std::regexにregex_constants::multilineを追加する提案。^と$とが、文字列全体の先頭と末尾ではなく、各行の先頭と末尾にマッチするようになる。

// マッチ数をカウントする
unsigned int count_match( std::string const & text, std::regex const & re ) 
{
    std::sregex_iterator iter( text.cbegin(), text.cend(), re ) ;
    std::sregex_iterator end ;

    unsigned int count = 0 ;
    for ( ; iter != end ; ++iter )
    { ++count ; }

    return count ;
} 

int main()
{
    std::regex const re1( R"(^[^]*?$)", std::regex_constants::ECMAScript ) ;

    std::string text = R"(this is
a 
test.)" ;

    std::cout << count_match( text, re1 ) ; // 1


    // multilineオプション
    std::regex const re2( R"(^[^]*?$)", std::regex_constants::ECMAScript | std::regex_constants::multiline ) ;

    count_match( text, re2 ) ; // 3
    
}

P0015R0: A specialization-friendly std::common_type

std::common_typeの実装を変えて、ユーザーが特殊化するときにCV修飾子はリファレンス修飾子の違いによって何種類も明示的特殊化を書かずにすむようにする提案。

P0017R0: Extension to aggregate initialization

アグリゲート初期化を拡張して、基本クラスを持つ型でも初期化できるようにする提案。

C++11のアグリゲート初期化は、基本クラスがある場合には使えない。

struct unique_id { std::uint32_t id ; } ;
struct user : unique_id
{
    std::string name ;
} ;

// エラー
user Adams{ "Douglas Adams"} ;

そのため、基本クラスがある場合には、面倒なコンストラクターを書かなければならない。

user( std::uint32_t id, std::string const & name )
    : unique_id{id}, name(name)
{ }

このようなボイラープレートコードは書きたくない。そこで、アグリゲート初期化を拡張して、基本クラスもメンバーのように初期化できるようにする。

user Adams { {42}, "Douglas Adams" } ;

複数の基本クラスがある場合は、記述された順番に初期化する。

struct unique_id { std::uint32_t id ; } ;
struct Name { std::string name ; } ;

struct user : unique_id, Name { } ;

user u{ {0}, {"null"} } ;

まあ、便利そうだ。

P0018r00 : Lambda Capture of *this by Value

lambda式で*thisを値でキャプチャできるようにする提案。

struct X
{
    int data ;

    std::function<int ()> f()
    {
        return [*] { return data ; }
    }
} ;

int main()
{
    std::function< int () > f ;

    {
        X x ;
        f = x.f() ;
    }// xは破棄された

    f() ; // OK、*thisはコピーキャプチャされている
}

キャプチャーデフォルト*は=とほぼ同じだが、thisポインターではなく、*thisを値でコピーキャプチャーする。

*thisだけをコピーキャプチャする、*thisもシンプルキャプチャーとして書ける。

P0019r00 : Atomic View

非アトミック型のオブジェクトに対してアトミック操作をするviewライブラリの提案。

atomic_viewは、非アトミック型の単一のオブジェクトに対してアトミック操作を提供する。

int x = 0 ;

i = 42 ;

{// iへのこれ以前の操作は全て行われる
    std::atomic_view<int> ai(i) ;
    // atomic<int>風の操作ができる
    // aiを介して操作する限りアトミックになる
}// ai破棄、aiへの操作がiに反映される

実装方法として最も汎用的なものは、mutexのような排他的ロックを使うことだ。もっと効率的な方法としては、まずアトミック型にコピーして、アトミック型のオブジェクトを経由して操作を行い、viewオブジェクトが破棄されるときに元のオブジェクトに結果をコピーするというものだ。

atomic_array_viewは、非アトミック型の配列に対するアトミック操作を効率的に行う意図で用意されている。例えば、排他的ロック実装の場合、ロックを要素ごとではなく配列全体に対して単独で用意できる。

想定用途は巨大な配列に対する高パフォーマンス演算だ。以下のような用途を想定している。

  1. 巨大な非アトミック型の配列を確保する
  2. 並列の競合しない初期化を非アトミック操作で高速に行う
  3. atomic_array_viewを構築する
  4. 並列の競合する書き込みをview経由で行う
  5. viewを破棄
  6. 並列の競合しない読み込みを非アトミック操作で高速に行う

atomic_global_viewは、グローバルにアクセス可能なアトミック型のオブジェクトをアトミックに操作するためのviewだ。インターフェースはatomic_viewと同じ。意図としては、アトミック型へのコピーを行わず、元となる非アトミック型を直接操作するものらしい。

P0020r00 : Floating Point Atomic View

非アトミックな浮動小数点数型に対するアトミック操作を提供するatomic_viewに、整数型と同等のメンバーを追加する提案。

add/sub/fetch_add/fetch_sub/operator +=/operator -=が追加される。

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

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

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

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

Boost.勉強会 #18 大阪で発表してきた

Boost.勉強会 #18 大阪 - Boost.勉強会 大阪 | Doorkeeper

Boost.勉強会 #18 大阪で発表してきた。発表資料はGitHubに上げている。

EzoeRyou/boost-benkyo-18

発表内容は、2週間前に公開されたpre-Kona mailingの提案から興味深いものを抜粋している。

Boost勉強会が大阪であるので、ドワンゴに金を出してもらって出張という名目で発表してきた。

なんでも午前11時から始まるそうだ。当日に東京から新幹線で大阪まで向かうので、7時頃には家を出なければならない。眠いのを我慢しながら家を出て、新幹線に乗り、大阪に向かった。

そして、大阪駅で30分ほど迷った。大阪梅田のあたりはますます複雑なプロシージャルダンジョン生成アルゴリズムを導入したと見える。

11時をやや遅れながら会場に到着すると、何と誰もいない。何でも、発表者が少ないので13時からに変更になったという。

さて、発表開始だ。

まず最初は、区間演算ライブラリを開発したという発表だった。区間演算ライブラリは多数あるし、Boostにもあるが、皆Partial Orderingでの比較しかサポートしていない。Weak orderingとTotal orderingもサポートしたライブラリを作ったという。しかし利用者がいないという。

発表者に投げられた厳しい意見としては、そもそも何に応用することを目的としたライブラリなのか。既存のライブラリがPrtial orderingしかサポートしていないのには相応の理由があるはずで、用途を考えずに単に純粋に計算だけするライブラリがあっても使われるはずがないとのことであった。

次にでちまるさんの兄のタグ付き共用体の発表のはずであったが、当日の朝にMicrosoftの不自由なPower Pointを購入してスライド資料を作り始めたので、まだ出来上がっていないとのことであった。怠惰はプログラマーの美徳とはいいながら、スライド資料の遅延評価はお勧めできない。

代わりに私が発表した

その次にでちまるさんの兄が発表した

次の発表は、Boostに追加が予定されている新時代のメタプログラミングライブラリ、Boost.Hanaの紹介だった。型計算と値計算を相互に行き来できるライブラリだそうだ。

最後の発表は、Proxygenを使ったWebアプリケーションの発表。なんだかWebの歴史を一通り見ているような内容だった。ブラウザーが高速化のために複数のHTTPコネクションをサーバーにはっていた時代があったことを思い出した。

その後は近所の安い飲み屋で懇親会を行った。Boost勉強会の規模が年々縮小しているという話を聞いた。果たしていつまで続くだろうか。また、大阪は学生の発表が多いという話だが、それは事情が違っていて、職業プログラマーはみんな東京に行ってしまうので、学生しか関西に残っていないのだという話だった。

10時半ごろに確保していたホテルに向かったが、極めて常識はずれなことに旅行会社が喫煙可の部屋を手配したので、極めて煙草臭かった。翌日の始発で家に帰った。

ドワンゴ広告

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

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

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

2015-10-10

スマフォの近くで話をしたらFacebookに関連する広告がでたんだけどマイクで音声拾ってやがるのか?

[Discussion] Facebook listening to conversations through microphone? : jailbreak

Redditで、害虫に悩まされたので、スマフォの近くで大声で害虫を駆除する話をしていたら、何も害虫駆除の検索などはしていないのに、Facebookが害虫駆除の広告を出したという話が注目を集めている。

しかし、Facebookのアプリはマイクの利用権限を要求しない。iOSの脆弱性を利用して権限を回避しているのでもなければ、マイクは使えないはずだ。

Hacker Newsのコメントでは、「その地域のその時期は害虫被害が発生しやすいので広告を入れたのではないか」とか、「友達に害虫のことを話したから、友達が検索したのではないか。友達の興味のあることは、自分も興味があるだろうから、広告が表示されたのではないか」などと推測されている。

十分に発達した科学はなんとやら。

2015-10-07

C++標準化委員会の文書2015-09 pre-Konaのレビュー: P0001R0-P0009R0

今回から、文書番号の規則が変わっている。PxxxxRxというフォーマットになっている。Pxxxxが文書番号で、Rxがリビジョン番号だろう。たしかに、今までどの文章がどの文章の改訂版かわかりにくかったので、いい変更だ。

P0001R0: Remove Deprecated Use of the register Keyword

registerキーワードを廃止する提案。registerキーワードは2011年にdeprecatedになり、将来のために予約されることになった。C++17では廃止する。

廃止するにあたっての懸念は、Cとの互換性だ。例えば、関数のシグネチャがCとC++で変わってしまう。とはいえ、registerキーワードの使用はCでも廃れているし、もっと利用例があるrestrictキーワードでさえ、この15年の間、CとC++との互換性にそれほど大きな問題を引き起こしていないので、大丈夫とのこと。

register機能は廃止されるが、registerキーワードは、将来の利用のために予約される。一般的な英単語のキーワードは極めて貴重である。

registerを予約語から消してユーザーに識別子として解放する案は、会議により否決された。

キーワードといえば、asmも実質廃止しても良さそうな気がする。というのも、C++規格のasmの文法は極めて使いづらいので、現実のコンパイラーは__asmなどの独自のキーワードを使ってインラインアセンブリを提供している。

NP0002R0: Remove Deprecated operator++(bool)

boolに対するoperator ++を廃止する提案。以下のようなコードが違法になる。

bool b = true ;

++b ; // false
++b ; // true

boolに対するoperator ++は、そもそも正式な規格以前の機能で、C++98ですらdeprecated扱いになっている。もう20年以上の猶予期間を与えてやったのだから、いい加減にいいだろうということだ。

なお、後置演算子はまだ利用価値がある。というのも、現在の値を得つつ値を変更できるからだ。

void f( bool b )
{
    g( b++ ) ; // 値を渡しつつ変更
}

この代替機能として、ライブラリにstd::exchangeが導入された。

void f( bool b )
{
    g( std::exchange( b, !b ) ) ;
}

P0003R0: Removing Deprecated Exception Specifications from C++17

動的例外指定を廃止する提案。

まともに利用されなかったので、すでにC++11でdeprecated扱いされている。

throw()だけは残されるが、noexcept(true)と同じ意味になる。

P0005R0: Adopt 'not_fn' from Library Fundamentals 2 for C++17

Library Fundamentals TSにあるnot_fnを標準ライブラリに追加する提案。

auto f = []( bool b ){ return b ; } ;
auto nf = not_fn(f) ;

f( true ) ; // true
nf( true ) ; // false

not_fnは小さいし、not_fnが依存する他のライブラリもないので、not1, not2の代替機能として。

P0006R0: Adopt Type Traits Variable Templates from Library Fundamentals TS for C++17

Library Fundamentalsにある変数テンプレートを使ったtype traitsのラッパーを標準ライブラリに追加する。traits::valueのかわりに、traits_vで値が返。is_same<T, U>::valueと書くかわりに、is_same_v<T, U>と書ける。

変数テンプレートは以下のように使う。

template < typename T, typename U >
constexpr bool is_same_v = std::is_same< T, U>::value ;

P0007R0: Constant View: A proposal for a 'std::as_const' helper function template

constなlvalueリファレンスを返す関すテンプレート、as_constを追加する。

利用例は以下の通り。

struct X {  } ;

void f( X & ) ; 
void f( X const & ) ;

int main()
{
    X x ;

    // 非const版
    f( x ) ; 

    // const版
    f( std::as_const(x) ) ;
}

関数を呼ぶときに、意図的に非constなオブジェクトから、const版のオーバーロードを呼び出したいときに使える。

わざわざconst_cast< const T & >( object )と書くのはだるい。しかもobjectの型Tを明示的に書かなければならない。const_cast< std::add_const< decltype( object ) >::type & >( object )と書けば明示的に型を書く必要はないが、極めてダルい。

実装例

template <class T> add_const_t<T>& as_const(T& t) noexcept
{
    return t ;
}

P0008R0: C++ Executors

スレッド風の実行媒体であるexecutorの提案。asyncをかなり高機能にしたものとも言える。スレッドプールなexecutorなどが提供される。

N4414で提案されていたtype erasure executorは懸念事項があるため見送り。

executorがシャットダウンするタイミングについては、main関数の実行を抜けたら自動的にシャットダウン。明示的にシャットダウンさせる方法については、まだ検討中。その他様々な会議での議論内容が書かれている。

P0009r00 : Polymorphic Multidimensional Array View

連続したストレージを多次元配列に見せかけるラッパー、array_viewが提案されているが、この論文は、array_viewはゼロオーバーヘッドの原則を満たさないし、異なるメモリレイアウトに対するゼロオーバーヘッドabstractionもできない欠陥品だとしている。この論文は、array_viewに対抗したviewライブラリを提案している。extentやstrideをconstexprで提供することによりコンパイラーの最適化の余地を高め、様々なハードウェアが直接サポートするレイアウトに直接マッピングできるようにするなどの設計になっている。とてもシンプルなので、viewというシンプルな名前こそがふさわしいと主張してる。

array_viewでfloat型の三次元配列[5][5][5]を宣言するには以下のようになる。

std::size_t size = 5 * 5 * 5 ;
float buffer[ size ] ;
array_view< float, 3>( size, buffer ) ;

対して、viewでは以下のように宣言する。

std::size_t size = 5 * 5 * 5 ;
float buffer[ size ] ;
view< float[5][5][5]> a( buffer ) ;

要素数がわからない場合は、以下のように設定する。

int x = 5, y = 5, z = 5 ;
float buffer[ x * y * z ] ;
view< float[][][] > a( buffer, x, y, z ) ;

コンパイル時にサイズがわかる箇所だけ書ける。

view< float [][5][] > a( ... ) ;

このような記法を可能にするため、コア言語にも手を入れる。配列の宣言子で、任意の添字の省略を認める。Clangではコードを一行修正するだけで対応可能だそうだ。

すごく大胆な提案だ。使い勝手は確かによい。

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

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

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

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

C++標準化委員会の文書2015-09 pre-Konaのレビュー: N4545-N4552

ISO/IEC JTC1/SC22/WG21 - Papers 2015

またもやC++標準化委員会の文書集の時期になった。

N4545: PL22.16/WG21 draft agenda: 4-9 May 2015, Lenexa KS/US

kona会議の予定表のドラフト

[PDF注意] N4546: Agenda and Meeting Notice for WG21 Concepts Telecon

コンセプト電話会議の予定表と告知

N4547: Business Plan and Convener's Report

今後の予定。次のC++規格は2017年に発行する予定。

ネットワーキングTSは2015年か2016年までにPDTSドラフトを出して、2016年に発行予定。コンセプトTSはPDTSコメント解決済み、2015年に発行予定。配列拡張TSは作業中だが、破棄されるかも。Concurrency TSはPDTSを出して、2015年に発行予定。

N4558: WG21 2015-07-20 Telecon Minutes

2015年7月20日に行われた電話会議の議事録。

N4549: Programming Languages -- C++ Extensions for Concepts

コンセプトTSの文面だが、古い。R0121R0が最新だ。

N4550: Record of Response: National Body Comments on ISO/IEC PDTS 19217, Technical Specification: C++ Extensions for Concepts

コンセプトTSに対するNBコメントとその対応。日本からは特に誤りを発見できなかったので送っていない。

N4551: National Body Comments, ISO/IEC PDTS 19571, C++ Extensions for Concurrency

Concurrency TSに対するNBコメントの修正。日本からはeditorialな問題の修正が通った。

N4552: Agenda and Meeting Notice for WG21 Telecon Meeting

2015年10月9日に行われた電話会議の告知。

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

今週末はBoost勉強会大阪#18で発表する予定だ。

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

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

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

Sarah Sharp、Linuxカーネルコミュニティの暴力性に嫌気がさして貢献をやめる

Closing a door | The Geekess

ドアは閉められた

この記事は一年もの間、下書きフォルダーに入れてた。いまこそ投稿すべき時。炎上は怖かったので、この問題について触れるのはなるべく避けてきたのだけれど、部屋の中の象を指摘しないのはもやもやする。そういうわけで、公開する。

つまりこういうこと。私はもうLinuxカーネル開発者じゃなくなった。2014年の5月にUSB 3.0ホストコントローラードライバーのメンテナーの座を明け渡したし、2015年の1月には女性向けFOSS推進計画(FOSS Outreach Program for Women)のLinuxカーネル部門とかその他の役職も降りたし、2014年12月6日には、最後のLinuxカーネル開発のプレゼンをした。2015年8月のシアトルでのLinux Plumbers Conferenceへの協力は断った。Linux財団の技術顧問委員の任期もすぐに切れるし、次回に入るつもりはない。

私はできることなら、もう二度とLinuxカーネルのメーリングリストに対してパッチやバグ報告や提案を投げることはない。受信欄には最近のカーネルの不具合がたまっているが、無視する。今やっているユーザースペースのグラフィックの仕事上、カーネルの不具合のパッチを投げる必要があるかもしれないが、カーネルコミュニティのやかましいやり取りに悩まされることを考えると、まず丸一日は躊躇するだろう。

私はもうLinuxカーネルコミュニティの一員とはならない。

この決断は長いこと考えたあげくのことだ。今の立場から降りることは軽々しく行ったのではない。降りたことに対して、長い間罪悪感を感じていた。でも、技術的には尊敬されこそすれ、個人的に尊敬されないようなコミュニティに貢献することなどできるはずがないとようやくわかったのだ。新参にはパッチを送るよう推奨しておきながら、メンテナーはどんな暴言でも吐くことを許されるような人間と関わり合いたくはない。下ネタとホモフォビックジョーク飛ばして何のお咎めもない連中と共同作業などできるはずがない。行動を律する規則なく、また規則を守らせる権力の存在しないコミュニティのなかでは無力だ。

Linuxカーネルコミュニティの技術力は認めている。この世の中で一流のコード品質を維持するほどに成長したプロジェクトだ。文化や習慣の違うメンテナーが膨れ上がるなかで、技術力に注力するということは、Linuxカーネルメンテナーは作業をすすめるために、往々にして愚直で、礼儀知らずで、暴力的になる。トップのLinuxカーネル開発者達は、他人の行動を正すために、たいてい怒鳴りあう。

こういうコミュニケーション方法は私向きではない。技術的に愚直なのはいいとして、人間は尊重しなければならない。技術的にせよ、社会的にせよ、間違いを犯した時は、個人攻撃せずに正すコミュニティでなければならない。我々は皆人間であり、間違いを犯す。そして正す。誰かにいらだち、やりすぎ、謝り、一緒に解決策を考える。

Linuxカーネルコミュニティのコミュニケーション方法はもっと礼儀正しくあるべきだ。メンテナーが不満を抱えているときにコミュニケーションを行うときに、もっと健全な方法を取るべきだ。Linuxカーネルには挑発や愚直を避けるために、もっとメンテナーが必要だ。

残念ながら、Linuxカーネルコミュニティに私が望むような態度の変化は近い将来に起こりそうもない。多くの古参Linuxカーネル開発者は技術的に性格的にも暴力的であるために現在の地位にいる。たとえ直接対面すればいい人であったとしても、彼らは現在のLinuxカーネルのコミュニケーション方法に変化を望んでいない。

つまり、このLinuxカーネルコミュニティにおいては、他のLinuxカーネル開発者の感情的な需要(ストレスを他人に愚直で失礼で暴力的な方法でぶつけて発散させること)が、私の感情的な需要(人間として尊重されること、罵倒されないこと)より優先されるということだ。このコミュニティでは基本的人権の尊重より既存のメンテナーを優先する強力な政治力が働いている。

この記事はカーネル開発者に向けて書いているわけではない。誰か特定の人間のせいにしているのではない。この記事は、私がもうその一員とならないコミュニティに対する失望だ。コミュニティをより良くするために私が立ち上がるたびに賛同してくれた人たちに申し訳なく思う。というのも、私はLinuxカーネルコミュニティの変化を諦めたからだ。文化の変遷は遅く、難しい作業だ。私はもはや文化の変遷を先導する一員にはなれないということだ。

Linuxカーネルコミュニティがいずれ変わることを願っている。その変化の片鱗には私がいる。ドキュメント、チュートリアル、そして私が始めた女性向けカーネルインターンシップ制度は、私がいなくても成長していくだろう。いずれの日か、環境が良くなれば、私は戻るかもしれない。私にはまだ時間がある。私は待てる。その間、他に私の活躍できる、もっと友好的なオープンソースコミュニティがいくつもあるというものだ。

あるドアが閉まれば、また別のドアが開く。しかし、我々は往々にして閉まってしまったドアのことを残念がり、新たに開かれたドアには目もくれない。

Alecander Graham Bell

ところで、HackerNewsではUSBスタックの不具合を修正するためにカーネルメンテナーとやり取りしたが、そのメンテナーがあまりにもやる気がなかったため諦めた体験談が書き込まれている。サラ・シャープもUSBスタックのメンテナーであった。これを受けて、ひょっとしたらLinuxカーネルの特定の領域のメンテナーとその文化がクソなのではないかという推測がされている。特に、既存のコードの保守が主な作業になると、自然と担当者の考え方まで保守的になるのではないかなどと議論されている。

Sigh. Story time. A long time ago (2006,7,8?) before Sarah took over USB develo... | Hacker News

また、有名なカーネル開発者のMatthew Garrettが、この記事を受けて、LinuxカーネルにBSD風のsecurelevelインターフェースを加えたforkを発表している。

mjg59 | Going my own way

2015-10-03

自作のサポセン応答AIを作ったとされるニューヨーク市の職員は単にロボット声で応答していただけだった

以下のような報道記事が上がっている。

ニューヨーク市のヘルプデスク職員、自作のAIプログラムに仕事をさせて停職処分 - BusinessNewsline

日付は執筆時点で"Posted Yesterday, by Anthony Holt"と書かれている。つい昨日、2015年10月2日の最新のニュースだ。ガイジン風の名前も信憑性が高い。おそらくは翻訳記事に違いない。重要な内容は以下の通りである。

ニューヨーク市がヘルプデスクの電話対応の作業を自作のAIプログラムに代行させていたとして、このヘルプデスクの職員に対して停職20日間の処分を下していたことが判った。

この職員は、Ronald Dillonという人物で、彼は自分の声とそっくりの自動音声応答システムを自作してヘルプデスクにかかってくる様々な質問をそのAIシステムを使って答えさせていた。

しかし、対応に疑問を感じた人が通報を行うことで、AIプログラムを使って電話対応を行わせていたことが発覚し、今回の処分に至った。

彼が自作したプログラムは、電話を通じて完璧にヘルプデスクの対応を行うことができるというもので、普通に電話対応を聞いた範囲では、対応を行っているのが人間ではなくロボットプログラムであるということを認識することはできない程の高度なものとなる。

このことは既に、多くのマスコミで報じられる状況ともなっており、ニューヨーク市が仕事をさぼった職員を停職処分にしたという以前の問題として、彼の作ったAIプログラムの出来があまりにも高度すぎることが大きな関心を集めている。

一部報道によるとこのRonald Dillonという人物は数学科を卒業してMBAを取得した秀才で、ニューヨーク市では元々はシステム開発のプロジェクトマネージャーをしていたが、その後、システム部が外部委託となったことを受けて、ヘルプデスクのサポート係に異動になったとしている。

なるほど、実際スゴイニュースだ。ヤバイ級ハッカーをサポセンのような分不相応にスゴイシツレイな職にあてがったために起こった痛快なニュースのように読める。このハッカーのウカツな上司にはケジメが必要だろう。

記事にはRonald Dillonなる人物の実際の応答音声らしきものがある。さて聞いてみると、呼吸や間投詞が多く、ものすごく高度に洗練された合成マイコ音声を使っていることがわかる。さすがネオニューヨークのハッカーは格が違う。

いやそんなバカな。そんな自動応答システムは、仮に作れたとしても、一人の天才の手によって作れるわけがない。「すでに、多くのマスコミで報じられる状況」で、ニューヨークの出来事であるので、さぞかし英語圏の報道も多いであろうと検索してみたところ、以下のような記事が引っかかった。

City Worker Gets 20-Day Suspension for Using Robot Voice to Answer Phone - Civic Center - DNAinfo.com New York

日付は2014年10月31日、去年のニュースである。これによると、

ニューヨーク市の保健局の職員は、カスタマーサービスの電話にロボット声で対応したことにより、20日間の停職処分を受けた。処分を受けた職員のRonald Dillonは同僚と一般市民に対するコンピューター関係の問題を解決する職についているが、上司の注意にもかかわらず、電話の応答に、意図的にロボット風の声を出して応答したという。

保健局によれば、2013年の2月から4月にかけて、少なくとも5回はロボット声(記事中では、Siri風、droid模倣という表現も使われている)で応答したという。

調査に使われた録音では、Dillonは電話口で、「ゆっくりとした、抑揚のない、過剰に明瞭な発音」で話したという。

Dillonによれば、ロボットを真似たのではなく、単に上司に渡されたマニュアルをゆっくりと読み上げただけだという。

Dillonは速いブルックリンなまりで話すので、うまく聞き取れないことがあるために単語ごとに読み上げたのだという。

Dillonはまた、彼はあまり人から好かれない性格であるため、上司が見せしめのために処罰を加えたのだと主徴している。

取材に答えたDillonは、上司から自分の声の抑揚について注意されたので、なるべく聞き取りやすい声を出すように努力しただけだと答えている。

裁判では、Dillonは上司に反抗するために意図的にロボット声を出したと判断された。

Dillonは1976年から保健局で働いていて、これまでにこのような処分を受けたことはない。

Dillonはかつて保健局のプロジェクトマネージャー(技術的なものであるとは書かれていない)であったが、3年前に本人の意思に反してサポセン業務に配置転換させられたという。サポセン業務はDillonには経験がなく、十分にこなす能力もない。「パワハラである」と主張している。

さて、もうひとつの記事がある。

NYC Health Department worker answers phone in robot voice - NY Daily News

日付は2015年9月29日。去年30日の停職処分を受けたRonald Dillonは、またもやロボット声を模倣したかどで、20日の停職処分にあったと報じているニュースだ。

保健局に寄せられる苦情のほとんどはDillonのロボット声に対するものである

上司によれば、Dillonは自分の技能と教育が現在のサポセン業務には分不相応であるが、配置転換が認められないため、仕事をサボタージュしているのであるという。

Dillonはこれを否定したが、裁判では認められなかった。

自動応答AIを開発したなどという話は英語圏のニュースには一切見当たらない。

2015-10-01

C++11/14 コア言語が出版された

「C++11/14 コア言語」が出版された。

C++11/14 コア言語

内容は、すでに去年公開しているものと同じだ。

EzoeRyou/cpp-book

EzoeRyou/cpp14-appendix

この本が出版に至るまでに実に様々な出来事があったのだが、今となっては語っても仕方がないことだ。

ドワンゴ広告

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

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

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