2017-10-30

C++標準化委員会の文書: P0260R2-P0356R2

P0260R2: C++ Concurrent Queues

名前通り、競合なく複数のスレッドからアクセスできるconcurrent queueの提案。ヘッダーファイルは<conqueue>、キュー本体の名前はbuffer_queue。

P0275R2: A Proposal to add Classes and Functions Required for Dynamic Library Load

C++にshared library(WindowsではDLLと呼ばれている)機能を追加する提案。

shared library機能は広く使われているが、現在C++規格はshared libraryを直接サポートしていない。


// libearth.soという名前のshared libraryファイルを読み込む
shared_library lib("libearth.so") ;
// シグネチャがint(std::string)でシンボル名がquestionの関数へのポインターをlibearth.soから得る
auto ptr = lib.get_if<int (std::string)>("question") ;

int result = ptr("the answer to life the universe and everything"s) ;

[PDF] P0303R0: Extensions to C++ for Short Float Type

floatより小さいshort floatの提案。

short float x = 1.0sf ;

規格はshort floatのサイズとフォーマットを規定していないが、背景にはIEEE 754-2008のhalf precision floating pointであるbinary16を念頭に置いている。

[PDF] P0315R3: Lambdas in unevaluated context

lambda式を未評価式の中でも使えるように制限緩和する提案。ただし、外部リンケージを持つ関数のシグネチャーにlambda式が含まれないように注意深く制限はする。

[PDF] P0323R3: Utility class to represent expected object

expected<T,E>の提案。このクラステンプレートはT型の値を保持することが期待されているが、T型の値を保持できないときには、期待通りではないエラーの意味を示すためにE型の値を保持する。

ある型の値を用意できないときに、値を保持していないことを示すためには、optional<T>が使えるが、エラーの詳細な情報を伝えたいときには、エラー通知用の型の値を渡したい。そのために、ここで提案しているある型かエラー型の値を保持するexpectedが使える。

[PDF] P0327R3:Product types access

構造化束縛で分解できる型をすべて分解することができる機能の提案。ようするにジェネリックなtuple_size/tuple_element/getの提案だ。

コア言語でやる場合、size/element/getの機能を提供する演算子を提供することになるが、キーワードの追加が必要になる。そしてtraitsでカスタマイゼーションポイントを提供しているライブラリでは使えない。

ライブラリでやる場合、現状のコア言語仕様ではビットフィールドに対応できない。

これは必要だ。

[PDF] P0330R1:User-Defined Literals for size_t

std::size_tに対するユーザー定義リテラル。zu。

#include <cstddef>
using namespace std::support_literals ;

// std::size_t
auto x = 0zu ;

これはほしい。std::support_literalsではなく、すべての標準ライブラリのユーザー定義リテラルを使えるようになるstd::literalsでもよい。

P0332R1: P0332r1 : Relaxed Incomplete Multidimensional Array Type Declaration

mdspan(多次元配列スパン)のために不完全配列の宣言の文法の制限を緩和する提案。添字数を一切書かなくてよくなるので、int[][][]などと書けるようになる。

これにより、以下のようにmdspanが書けるようになる。

// 現在提案中のmdspanの宣言
// 3次元テンソル型
using tensor = std::mdspan<double,std::extents<std::dynamic_extent,std::dynamic_extent,std::dynamic_extent>> ;

// この提案が入れば書けるようになる上と同等の宣言
using tensor = std::mdspan<double[][][]> ;

楽になる。

しかし、連続したストレージを所有しない多次元配列に見せかけるラッパークラスとして、mdspanという名前は本当に通じるのだろうか。これはもともとarray_viewと呼ばれていたが、viewという用語は正しくないと物言いがついたために、spanになった。mdspanとは、Multi-Dimentional spanだ。果たして日本語でスパンといって伝わるのだろうか。

ただ、考えてみれば、ラッパーとかサンクといった用語も当初は日本人に馴染みのない用語だったはずで、イテレーターやデリゲートやクロージャーといった用語も最初は聞き慣れない用語だったはずだ。かつてはアドレスを番地と言ったものだ。

P0355R4: Extending <chrono> to Calendars and Time Zones

chronoをカレンダーとタイムゾーンに対応させる提案。これでようやくC++で日付処理が可能になる。

P0356R2: Simplified partial function application

std::bindにかわる新しいbind_frontの提案。この提案ではstd::bindのdeprecated化を提案している。

引数を5個とる関数fが以下のようにあるとして、

int f( int, int, int, int, int ) ;

これの第一引数だけ指定して残りを受け取る引数4個の関数を作り出す場合、std::bindでは以下のように書く。

bind( &f, 42, _1, _2, _3. _4 ) ;

わりとだるい。そもそも実引数の数を把握してそのとおりに書くのがとてもだるい。

lambda式ならどうか。

[]( auto && ... args ) { return f( 42, std::forward<decltype(args)>(args)... ) ; } ;

割と難しい。このようにC++14のlambda式を使いこなし、かつPerfect Forwardingも完璧にスラスラと書けるようになる頃には、京都で9年ぐらいニートした挙句にC++の参考書を出版するようになっているか、岡山で陶芸でもしている。

bind_frontは先頭の引数だけを指定することに特化したライブラリだ。以下のように書ける。

bind_front( &f, 42 ) ;

まあ、わかりやすいとは思うが、そう都合よく先頭だけ引数を指定したい場合があるだろうか。

ドワンゴ広告

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

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

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

2017-10-27

C++標準化委員会の文書: P0009R4-P0237R9

2017年10月分の文書が公開された。この8ヶ月ほど、C++17参考書の執筆に注力していて文書のレビューを怠っていたが、さっくりと解説して、次はC++入門書の執筆に移りたい。

P0009R4: P0009r4 : Polymorphic Multidimensional Array Reference

改定に改定を重ねてわけのわからないことになっている連続したストレージに対して多次元配列風のアクセスを提供するラッパーライブラリ。もともとarray_viewだったが、今ではmdspanという一見してわけのわからない名前になっている。これは、viewという用語が適切ではないという違憲によって変更された。

もともとレイアウト指定はなかったのだが、レイアウト指定は絶対に必要だという違憲があって追加された。C/C++風の末尾の次元がメモリ上で連続しているレイアウトと、FORTAN風の先頭の次元がメモリ上で連続しているレイアウト、そして、subspanで一部を切り出したときにメモリが連続していないことを示すレイアウトがある。

割と一見してコードの意味がわからなくなりそうな設計をしている。

P0037R4: Fixed-Point Real Numbers

markdownで書かれている。ほー、いいじゃないか。こういうのでいいんだよ。こういうので。焦るんじゃない。俺はPDFで読みたくないだけなんだ。

固定少数点数ライブラリfixed_point< Rep, Exponent>の提案。

fixed_point< uin32_t, -12 > a(1), b(2) ;

// 0.5
auto c = a / b ;

fixed_point<Rep, Exponent>の指数はpow(2, Exponent)となる。最小値はnumeric_limits<Rep>::min() * pow(2, Exponent)、最大値はnumeric_limits<Rep>::max() * pow(2, Exponent)

面白いことに、クラステンプレートのコンストラクターからの実引数推定と実引数推定ガイドを使った、初期化子の整数値からのExponentの推定機能がある。

auto a = fixed_point(0ul);
static_assert(is_same_v<decltype(a), fixed_point<unsigned long, 0>>);

Exponentは初期化子の整数値を表現できる最大の値が選ばれる。

また面白いことに、演算結果でRepやExponentが変わる。operator *ではRepが変わらないが、multiply/divideではRepも変わる。


// 安全に演算結果を表現できる型に変わる。
auto f = fixed_point<uint8_t, -4>{15.9375};
auto p = multiply(f, f);
// p === fixed_point<uint16_t, -8>{254.00390625}

// operator *だとRepが変わらない。
auto f = fixed_point<unsigned, -28>{15.9375};
auto p = f * f;
// p === fixed_point<unsigned, -56>{0}

// Repも変える
auto f = fixed_point<unsigned, -28>{15.9375};
auto p = multiply(f, f);
// p === fixed_point<uint64_t, -56>{254.00390625}

これは除算の場合も同じだ。


fixed_point< uint32_t, -16> a(1), b(2) ;
auto c = a / b ;
// c == fixed_point< uint32_t, 0>(0)

auto d = divide( a, b ) ;
// d == fixed_point<uint64_t, -32>(0.5) ;

Exponentの変化は、乗算の場合それぞれのExponentの加算、除算の場合は減算になる。

わかりやすいようなわかりにくいような。最悪の場合を想定しすぎなような。

exponentが小さくてもすむ型であれば最初から小さくしとけというのはわかるが、あまりにも目まぐるしくExponentが変わりすぎる。文書は組み込み型のDrop-in Replacementを意図して設計されているというが、以下の関数テンプレートに渡してエラーになるようなライブラリが本当にdrop-in replacementたりえるだろうか。

// 値を半分にして返す関数
template < typename T >
T half( T x )
{
    return x / T(2) ;
}

このコードは、どんなfixed_pointを渡しても、小数部が切り捨てられる。

P0096R5: Feature-testing recommendations for C++

機能テストマクロがC++17に対応。

P0124R4: Linux-Kernel Memory Model

Linuxカーネルのメモリーモデルの解説。

[PDF] P0196R4: Generic none() factories for Nullable types

ポインター、optionalなどnullableな型に使えるジェネリックなnone()の提案。

[PDF] P0201R2:A polymorphic value-type for C++

polymorphic_value<T>の提案。コピー時にT型を基本クラスとするpolymorphic typeをディープコピーしてくれるクラス。ディープコピーをするためにT型は何か特別なclone関数を用意する必要はない。


struct Base { int x ; } ;
struct Derived : Base { int y ; } ;

polymorphic_value p1( new Derived ) ;

// 値がディープコピーされる
polymorphic_value p2 = p1 ;
// Derivedのデストラクターが呼ばれる

アロケーターの指定がないのはなぜだろう。

P0202R2: A Proposal to Add Constexpr Modifiers to Functions in <algorithm> and <utility> Headers

algorithmとutilityをconstexprにする提案。

前の提案はcstring(主にmemmove/memset/memcmp)もconstexprにする提案だったが、cstringに手を入れるのは見送られた。かわりに、compiler intrinsicsなどの方法で実現する方向に調整された。

さて、既存のC++標準ライブラリの実装を見てみると、libstdc++はcompiler intrinsicsを使っているのでこのままconstexprに対応可能で、libc++はcstringを使っているので書き換えが必要だ。また、libc++はgotoを使っているのでこの点でも書き換えが必要となる。

stable_partition, inplace_merge, stable_sortは、メモリ確保、placement newによる構築、unique_ptrの使用があるので、constexprからは除外する。

shuffleとsampleはuniform_int_distributionを使うため、constexprからは除外する。

[PDF] P0214R6: Data-Parallel Vector Types & Operations

SIMDプログラミングのためのベクトル型の提案。なんだかすごく使いづらそうな設計だ。

[PDF] P0233R6: Hazard Pointers: Safe Reclamation for Optimistic Concurrency

ハザードポインターライブラリの提案。ハザードポインターはIBMのMaged M. Michaelによって考案され、IBMのMaged M. Michaelによって特許申請された。最終的に特許申請は取り下げられた。ところでこの提案文書の執筆者はIBMのMaged M. Michaelである。この提案が特許汚染されていないかどうか注意深い検証が必要だ。

ISO国際標準規格には特許汚染された技術を持ち込むべきではない。ましてや特許汚染の前科がある人間によって提案された機能は眉に唾をつけてみるべきだ。

[PDF] P0237R9: Wording for fundamental bit manipulation utilities

Nビットの符号なし整数型をN個のbit要素が入っているコンテナーだとみなし、ビットに対するイテレーターを提供するイテレーターアダプターライブラリ、bit_iteratorの提案。

ドワンゴ広告

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

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

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

2017-10-23

インデックス付きrange-based forに必要なのはネストされた構造化束縛ではなくてif constexprだった

親愛なるC++読者諸君、私がC++17を流暢に使いこなす江添亮である。前回、ネストされた構造化束縛がほしいと書いた。

本の虫: インデックス付きRange-based for文を実装したらネストされた構造化束縛が欲しくなった

このブログを公開してすぐ、同僚からwith_index側でpairを開けばいいのではないかと言われた。たしかにそのとおりだ。早速実装した。その結果、以下のコードが通るようになった。


int main()
{
    std::map<int,int> m = { {1,1}, {2,2},{3,3} } ;

    for ( auto[ i, key, mapped ] : with_index(m) )
    {
        std::cout << i << key << mapped ;
    }
}

その実装の骨子は以下の通り。


template < typename T >
struct is_pair
    : std::false_type { } ;

template < typename T1, typename T2 >
struct is_pair< std::pair<T1, T2> >
    : std::true_type { } ;

template < typename T >
constexpr bool is_pair_v = is_pair<T>::value ;

template < typename Iterator  >
class with_index_iterator
    : public Iterator
{

public :
    auto operator *() const noexcept
    {   // ここが重要
        if constexpr ( is_pair_v< typename std::iterator_traits<Iterator>::value_type > )
        {
            auto & pair = *static_cast<Iterator const &>(*this) ;
            return std::make_tuple( i, pair.first, pair.second ) ;
        }
        else {
            return std::make_pair( i, *static_cast<Iterator const &>(*this) ) ;
        }
    }
} ;

重要なのはif constexprだ。constexpr ifによって、わざわざクラステンプレートを書かずともコンパイル時条件分岐ができるようになった。これでメタプログラミングがとても書きやすくなった。

なお、この実装では以下のようなコードは通らない。

int main()
{
    std::map<int, std::pair<int,int> > m = { {1,{1,1}}, {2,{2,2}},{3,{3,} } ;

    for ( auto[ i, key, m1, m2 ] : with_index(m) )
    { }
}

再帰的なテンプレートメタプログラミングをすることにより、何段階にネストされようとも開くことができるwith_indexは実装可能だ。その実装は読者への課題とする。

また、構造化束縛がpairの他にも対応しているtupleやtuple_sizeとtuple_elementとgetに対応した型のネストへの対応も、読者への課題とする。

int main()
{
    std::map<int, std::tuple<int,int> > m = { {1,{1,1}}, {2,{2,2}},{3,{3,} } ;

    for ( auto[ i, key, m1, m2 ] : with_index(m) )
    { }
}

問題は、構造化束縛が対応しているクラスを開くことができない。


struct user_defined_pair
{
    int x ;
    int y ;
} ;

std::map< int, user_defined_pair > m = { {1,{1,1}}, {2,{2,2}},{3,{3,} } ;

これはどうしようもない。とはいえ、実用上はこれでいいのではないか。

with_indexの完全な実装は以下の通り。

template < typename T >
struct is_pair
    : std::false_type { } ;

template < typename T1, typename T2 >
struct is_pair< std::pair<T1, T2> >
    : std::true_type { } ;

template < typename T >
constexpr bool is_pair_v = is_pair<T>::value ;



template < typename Iterator  >
class with_index_iterator
    : public Iterator
{
    std::size_t i = 0 ;

public :

    with_index_iterator( Iterator iter )
        : Iterator( iter )
    { }

    auto & operator ++()
    {
        ++i ;
        this->Iterator::operator ++() ;
        return *this ;
    }

    auto operator *() const noexcept
    {
        if constexpr ( is_pair_v< typename std::iterator_traits<Iterator>::value_type > )
        {
            auto & pair = *static_cast<Iterator const &>(*this) ;
            return std::make_tuple( i, pair.first, pair.second ) ;
        }
        else {
            return std::make_pair( i, *static_cast<Iterator const &>(*this) ) ;
        }
    }

} ;

template < typename Range >
class with_index
{
    Range & range ;

public :
    with_index( Range & range )
        : range(range)
    { }

    auto begin() const
    {
        return with_index_iterator{ std::begin(range) } ;
    }
    auto end() const
    {
        return with_index_iterator{ std::end(range) } ;
    }

} ;

ドワンゴ広告

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

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

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

インデックス付きRange-based for文を実装したらネストされた構造化束縛が欲しくなった

親愛なるC++14以前の古臭いC++を書く読者諸君。疑う余地なく、私がC++17を華麗に使いこなす江添亮である。

私のC++17参考書も余裕で書き終わり、後は出版を待つだけだ。私の中で、C++というのはもうすっかりC++17になってしまい、以前のC++のことは忘れてしまった。いまさらC++14のような使いづらい時代遅れのC++を書きたくはない。時代はいよいよC++17が当然になったと言えよう。

私のC++17参考書はGPLv3という自由なライセンスで提供される究極の電子書籍だ。これから紙書籍、プロフェッショナルに組版された電子書籍も出版される予定になっているので、読者は読まないという法はない。

EzoeRyou/cpp17book: C++17参考書

ところで、インデックス付きRange-based for文がほしいとさえずっていた。というのも、レンジの要素のイテレートを手で書きたくはないし、インデックスのインクリメントも手で書きたくないからだ。

template < typename Container >
void f( Container & c )
{
    // 要素のイテレートは手動
    // インデックスのインクリメントは手動
    for (   auto i = std::size_t(0),
            iter = std::begin(c), end = std::end(c) ;
            iter != end ; ++iter, ++i )
    {
        // iはインデックス
        // *iterは要素 
    }
}

std::for_eachやRange-based for文を使うことにより、要素のイテレートは手で書かずにすむ。しかし、インデックスのインクリメントは依然として書かなければならない。

template < typename Container >
void f( Container & c )
{
    {
        std::size_t i = 0 ;
        for ( auto element : c )
        {
            // iはインデックス
            // elementは要素
            ++i ; // 手動インクリメント
        }
    }

    std::for_each( std::begin(c), std::end(c),
        [i = std::size_t(0)](auto element) mutable
        {
            // iはインデックス
            // elementは要素
            ++i ; 手動インクリメント
        } ) ;
}

これではfor文より読みやすさ、間違いにくさの点で悪化しているではないか。

ところで、C++17では構造化束縛が追加された。


// クラステンプレートのコンストラクターの実引数からの推定も追加された
std::pair p(1,2) ;
auto [a,b] = p ;
// 以下と同じ
// auto a = p.first ;
// auto b = p.second ;

構造化束縛は、もちろんRange-based for文でも使える。


int main()
{
    std::map<int,int> m = {{1,1},{2,2},{3,3}} ;

    for ( auto [key, mapped] : m )
    {
        std::cout << key << mapped ;
    }
}

このコードは期待通りに動く。この程度のコードは、すでにC++17プログラマーである私にとってごく自然で当然のコードだが、未だに旧態依然のC++14以下にとらわれている読者には垂涎のコードだろう。

ということは、コンテナーをラップして、インデックスカウンターを持ち、operator ++でインデックスをインクリメント、operator *でstd::pair<std::size_t, reference>を返すようなラッパーを書けば、Range-based for文にインデックスが追加できるのではないか。

早速そのようなラッパー、with_indexを書いてみた。


int main()
{
    std::vector v = {1,2,3,4,5} ;

    for ( auto[ i, element ] : with_index(v) )
    {
        std::cout << i << element ;
    }
}

いける。これは使える。

しかし、問題がある。構造化束縛はネストできないので、std::mapに使うと以下のようになってしまう。

int main()
{
    std::map<int,int> m = { {1,1},{2,2},{3,3} } ;

    for ( auto[ i, pair ] : with_index(m) )
    {
        auto [key, mapped] = pair ;
        std::cout << i << key << mapped ;
    }
}

C++にもネストされた構造化束縛がほしい。

// C++ではない
int main()
{
    std::pair< int, std::pair<int,int> > p = { 1, {2,3} } ;

    // これがほしい
    auto [a, [b,c]] = p ;
}

ちなみに、with_indexの実装は以下の通り。

template < typename Iterator  >
class with_index_iterator
    : public Iterator
{
    std::size_t i = 0 ;

public :

    with_index_iterator( Iterator iter )
        : Iterator( iter )
    { }

    auto & operator ++()
    {
        ++i ;
        this->Iterator::operator ++() ;
        return *this ;
    }

    auto operator *() const noexcept
    {
        return std::pair<
            std::size_t,
            typename std::iterator_traits<Iterator>::reference >
        { i, *static_cast<Iterator const &>(*this) } ;
    }

} ;

template < typename Range >
class with_index
{
    Range & range ;

public :
    with_index( Range & range )
        : range(range)
    { }

    auto begin() const
    {
        return with_index_iterator{ std::begin(range) } ;
    }
    auto end() const
    {
        return with_index_iterator{ std::end(range) } ;
    }

} ;

ドワンゴ広告

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

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

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

2017-10-20

Ubuntu 17.10にアップグレードした

Ubuntu 17.10がリリースされた。

ArtfulAardvark/ReleaseNotes - Ubuntu Wiki

今回のリリースでは、デスクトップ環境がUnityからGnomeに変わった。同時に、X.orgではなくWaylandに変わっている。

このため、Ubuntuのデスクトップ環境の使用感がだいぶ変わっている。まず、GnomeはUnity風の操作感を再現するために、やや手が入っている。具体的にはSuper+数字でランチャーに登録されているソフトウエアのウインドウを選択できたりする。

残念なことに、Global MenuやHUDは再現されていなかった。

Waylandに移行したので、日本語入力環境が大変不便になった。fcitxはWaylandに対応していないので使えない。使えないが、そのことは警告されないので、fcitxを使っているUbuntu 17.04からアップグレードすると、日本語が入力できなくなる。iBusに切り替える必要がある。iBusはキーボードレイアウトとかな漢字変換機能を持つIMEをごちゃまぜにしているので本当に使いづらい。しかも、変換候補のウインドウが、カーソル位置ではなく画面左上に出る。極めてつらい。

私は事前に調べているからいいものの、これは何も知らない人がアップグレードしたら日本語入力ができずに詰みそうだ。

特に興味深いパッケージのバージョンという点では、Linuxカーネルが4.13に、GCCが7.2.0に、Clangが4.0.1.6に、Neovimが0.2.0になった。

GCCとClangのバージョンが上がったので、これでfilesystemなどの一部のライブラリを除いて、どちらもC++17をある程度実装したコンパイラーになった。

ただし、現在libc++にはxlocale.hをincludeするという不具合がある。xlocale.hは標準のヘッダーではなくglibcの内部ヘッダーであり、しかもそのコメントには、"This file is not standardized, don't rely on it, it can go away without warning"と書いてある。

sourceware.org Git - glibc.git/commit

libc++がなぜxlocale.hをincludeしているのか全くわからない。実装を読んだが、単にincludeしているだけだ。なので、とりあえず空のxlocale.hファイルを作り、そのファイルへのディレクトリーを-Iで追加して対処した。

また、メインでは使っていないDellのLatitude E7470でUbuntuがうまく動かない問題は、Ubuntu 17.10でも解決しなかった。まずアップグレードが途中で失敗する。

DellのLatitude E7470でUbuntuがうまく動かない問題は、未だに原因がわからない。以下のような問題がある。

Live環境のブートが遅い。ログを見ると、pwconvが/etc/shadow-のパーミッションを0600に変更しようとしてエラーを出し、パッケージリストを読み込むのに時間を要し、regenerating SSL certificatesでまた時間を要する。放置しても絶対に進まず、キーボードを適当にワシャワシャ叩くと進む。まるで/dev/randomへのエントロピーが十分にたまるまで待っているかのような挙動をする。

初回ログイン時に数回画面が暗転する。

突然フリーズする。マウスカーソルは動くがそれ以外の操作を何も受け付けない。ttyの変更すらできない。文字入力をしているときにフリーズしやすい気がする。

cat /dev/urandomが何故か目視できるほど遅い。urandomなのに遅い。

grub-updateが遅い。

apt upgradeしたときの"procescing triggers for man-db"が遅い。

なので実用にならないのだが、一応維持している。Ubuntuの欠陥なのかDell Latitudeの欠陥なのか、個体の欠陥なのかわからないが、私は個体の欠陥だと思っている。しかし、memtest86+は何の問題もなく何パスも通るので、メモリではなさそうだ。また、ストレージへの読み書きも遅いわけではない。単純なベンチマークやストレステストで問題が見つからないのでよくわからない。

2017-10-19

江添ボドゲ会 10月22日

10月22日に以下の通り、自宅でボドゲ会を行います。

江添ボドゲ会 10月22日 - connpass

2017-10-16

GitHubで他人のプルリクエストに対しコンフリクト解消や追加の修正を行いつつマージする方法

読者がGitHubで何かを公開しているとしよう。そのレポジトリに対して他人がプルリクエストを送ってきた。なかなか良さそうな変更だ。早速マージしたい。

しかし、残念なことにそのプルリクをそのままマージすることができない。なぜならば、

  • コンフリクトを起こしている
  • 追加の修正が必要だ

こういう場合、大規模なプロジェクトや、PR主が職場の同僚や開発仲間であった場合、PR主に修正を依頼するものだ。しかし、個人的な小規模なプロジェクトなのでPR主はPRを出したまま返事がない。

こういう場合に、PRをマージするにはどうすればいいのか。答えは簡単で、プルリクエストが裏でやっているgit操作を自分のローカルでやればいいのだ。

まず、該当のPRのGitHub上のページの、「プルリクエストをマージ」ボタンの横に、コマンドライン操作を表示(view command line instructions)というリンクがあるはずだ。これをクリックすると、プルリクエストに相当する作業を、ローカルのコマンドでやる方法が表示される。その指示に従うだけでよい。

ローカルのコマンドでやる方法は、以下のような意味になっている。

# ローカルで新しくブランチを作る
git checkout -b example master
# 新ブランチにPR先の変更を入れる
git pull git://example.com example

# 必要に応じてローカルでコンフリクト解消と編集をしてコミット

# masterブランチに切り替え
git checkout master
# masterブランチにマージ
git merge --no-ff example
# 結果をGitHubレポジトリにpush
git push origin master

マージ操作の後はexampleブランチはいらないのでgit branch -d exampleで消してよい。

これによって、PRをマージしたという歴史は正しくgitレポジトリに反映される。GitHubのプルリクエストの方は何もする必要はない。自動的にマージされた扱いになっている。

GitHubのWebページ上でプルリクエストをマージするボタンを押すというのは、このgit操作をGitHubがリモートレポジトリ上でやってくれているだけの話だ。

プルリク、プルリクエスト、PR, Pull Request、GitHubに特有の複数人のgit操作を楽にするための概念だ。PRは面倒で複雑なgit操作をユーザーから隠している。しかし、こういう問題が発生した時、裏で行われているgit操作を知らない場合、途方に暮れてしまう。その結果、masterにマージしてから追加の編集を行うとか、手作業で同様の編集をするなどといった日付zipと何ら変わらない運用が行われてしまうこともある。

この記事はできるだけ簡潔に解説する目的で書いた。git強者な読者であれば、gitには同じ操作をもっと強力にやる方法が用意されているなど、俺のお気に入りのgitフロントエンドや拡張ツールを使えばもっと強力な操作が行えるなどと、言いたいことは様々あるかもしれないが、これは初心者用の解説だ。

2017-10-13

C++17の参考書を書き上げた

C++17の新機能のほぼすべてを解説した参考書を書き上げた。

GitHub: EzoeRyou/cpp17book: textbook for C++17

GitHub Pages: C++の新機能

この参考書は、後ほどアスキードワンゴから出版される予定になっている。

内容としては2017年に制定された新しい標準規格、C++17に追加された新しいコア言語と標準ライブラリのほぼすべてを解説した内容になっている。

C++17には数学関数も追加されていて、これの執筆には読者から多大な貢献を得た。

C++17の新機能: 数学の特殊関数群

この本はGPLv3でライセンスされ、紙書籍での出版も、GPLv3で行うことを予定している。

さて、C++17はすでに規格制定され、C++20まではまだ少し時間がある。この暇に、今まで得たC++の知識を使って本を書くべきだと思ったので、次も本を書くことに注力したい。今考えているのはC++の入門書だ。

入門書という分野について、私にはあまり好ましい印象がない。世の中のいわゆる入門書をめくると、なにやらSI屋がよろこぶようなエクセル方眼紙エビデンスよろしくスクリーンショットをベタベタと貼り付けて、無駄に紙面を50ページも100ページも使い、結局何を説明しているかというと、Microsoftの不自由なVisual Studioのインストール方法だけを説明していたりする。完全に訳がわからない。私の書く入門書はそのような本にはしない。

そもそも、今C++の入門書を必要とする人間は、一体どういう環境にあるのだろう。

この2017年にC++入門者の筆頭に上がるのは競技プログラマーだ。彼らは大抵のオンラインジャッジシステムでサポートしていてパフォーマンスが出て必要なアルゴリズムを書き起こせる言語であるC++を使う。興味深いことに、彼らはC++にある素晴らしい機能の筆頭に、next_permutationという標準ライブラリの存在を挙げる。next_permutationを実務で使う状況というのはまれにしか起こらないと思うのだが、不思議なことだ。

既存の自由ソフトウェアに貢献したいが、そのソフトウェアがC++で書かれている場合、これはC++を学ぶ必要がある。そういう人もいるだろう。

仕事でC++を書く必要が出てきて学ぶという人もいるだろう。C++が使われる仕事として一番わかり易いのは、不自由なゲーム専用機などの組み込み機器だと思われる。

すでにC言語は知っているが、C++を学びたいという人。15年前ならばそういう人はいくらでもいたが、果たしてこの2017年にいるのだろうか。ただ、例えばLinuxカーネルの開発に参加したい場合、これはC言語を学ぶしかない。カーネル開発者は未だにC言語が必要なので学び、C++を知らないが学びたいという人は多いのかもしれない。

これを考えると、次に書くべきC++入門書は、プログラミング入門書である必要はない。対象読者はすでに別の言語でプログラミングの基礎にある程度触れていることを想定して書いてもよいはずだ。つまり、環境構築や、変数とは箱のようなものであるといった比喩表現はいらない。

ドワンゴ広告

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

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

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

2017-10-08

オーストラリア警察が世界最大の児童ポルノサイトを11ヶ月運営していたことが判明

VG exposed the largest child sexual abuse forum. It was run by the police.

ノルウェイのタブロイド紙のヴェルデンス・ガング(VG)は、Tor経由でアクセスできるいわゆるダークウェブの中で世界最大の児童ポルノサイトであるChilds Playは、オーストラリア警察によって運営されていたことをつきとめた。この顛末は倫理的にも技術的にも興味深い。

この児童ポルノサイトは、当時ダークウェブの児童ポルノサイトの中でも世界最大級の規模を持っていた。各国の警察は様々な捜査の上、このサイトを運営していた二人の逮捕に至った。そして、Webサイトは、各国警察相談の上、おとり捜査が合法な国、オーストラリア警察、アルゴスの手に委ねられた。アルゴスはWebサイトのホスティングをオーストラリアのレンタルホスティングサービス、Digital Pacificに移し、そこでWebサイトの運営を続けた。

児童ポルノサイトの運営者を逮捕した後、警察がWebサイトを運営し続けておとり捜査に使うというのはよくあることだ。VGのまとめによれば、すでに以下のWebサイトが警察によって運営されていたことが知られているという。

  • Elysium: ドイツ、BKAにより数日アクセスされた
  • Playpen: USA, FBIにより2週間運営
  • The Giftbox Exchange: EUにより約1ヶ月運営
  • The Love Zone: オーストラリア、アルゴスにより6ヶ月運営

今回、オーストラリアの警察組織アルゴスによって、当時世界最大だった児童ポルノサイトのChilds Playは11ヶ月運営された。

児童ポルノサイトをおとり捜査のために運営することは様々な倫理上の問題を引き起こすが、今回の例はさらに倫理上の疑問点がある。Childs Playの運営者は頻繁にフォーラムにあらわれていた他に、一ヶ月ごとに必ず新作の児童ポルノを提供していたのだった。運営者が逮捕されたことを利用者にさとられないよう、オーストラリア警察の捜査官は運営者の語彙を使い、書き言葉をまねてフォーラムへの書き込みを続けていたが、ついに次の新作児童ポルノを投稿しなければならない時期にきた。そこで、オーストラリア警察はすでに押収していたそのWebサイトにはない児童ポルノを投稿して運営を継続した。

これはおとり捜査が違法で、法律の記述は、単に潜入捜査員の身を守るために本来ならば犯罪になる行為(麻薬の購入や違法な武器の受け渡し、競馬競輪競艇オートレースのノミ屋の脚)をすることができると定めているもので、おとり捜査を許可したものではない日本に住む筆者の倫理観では理解ができない。

結局、一定の割合で児童ポルノを求める人間がいるのであれば、児童ポルノサイトを運営し続ければ犯罪を摘発をし続けることができるわけだ。犯罪の摘発はオーストラリア警察にとって評価対象だ。すると、児童ポルノは評価を生み出す装置となり、継続して運営するインセンティブが生じる。今回、11ヶ月の長きに渡り押収した児童ポルノサイトの運営を警察が続けたのは、このようなインセンティブのせいだろう。ましてや押収した児童ポルノサイトにはなかった児童ポルノをよそから持ってきてオーストラリア警察が公開しているというのは、もはやオーストラリア警察が犯罪を行っているのと同じではないか。オーストラリアの法ではおとり捜査が認められているのでオーストラリア警察の行為はオーストラリア国内では違法ではないということになるのだが。

さて、倫理面についてはこのぐらいにして、技術面に移る。

ノルウェイのタブロイド紙であるVGはChils Playがオーストラリア警察の手によって運営されていることを突き止めたのだが、これは至って合法的な方法で行われた。

オーストラリア警察アルゴスは、WebサイトのホスティングをオーストラリアのレンタルホスティングサービスのDigital Pacificに移した。しかし、WebサイトへのアクセスはTor経由でなければ行えないため、物理サーバーのIPアドレスはわからない。VGは当初、フォーラムの投稿から運営元を突き止めようとしたが、Webサイトの利用者の一部は簡単に特定できたものの、元運営者は注意深かく、運営を引き継いだオーストラリア警察も注意深かったので、身元はわからなかった。

そこでVGは、もっと古典的な方法で物理サーバーのIPアドレスを調べることにした。

Webサイトの実装のほとんどは注意深くTor経由で行われていたが、うっかり物理サーバーから直接アクセスしている箇所があったのだ。

Webサイトはユーザーがアカウントのプロフィール画像を設定できるようにしていたが、この設定では、URLを指定することができた。URLから画像をダウンロードするのはTorを経由せず、物理サーバーが直接行ってしまっていた。

これによってVGはDigital PacificのIPアドレスであることを突き止めたが、まだここで終わりではない。Digital Pacificがホストしているサーバーは単なるプロクシー、VPN、あるいはTor exit nodeかもしれないからだ。

そこでVGはDigital PacificのサーバーがVPNではなくWebサイトを直接ホストしているかどうかを判定するために、いわゆるサイドチャネル攻撃を行った。Digital Pacificのサーバーをレンタルし、プロファイル画像のURLをそのサーバーに向けるようにしたのだ。そして、サーバー間のラウンドトリップ時間を計測した。Digital Pacificが単なる中継サーバーならば、ラウンドトリップ時間は同一データセンター内よりはるかに長いはずだ。こうして同一データセンター内で処理が行われていると思しき短いラウンドトリップ時間を確認した。

そしてもう一つ、サーバー間のMTUとパケット分割を計測することで、同一のローカルエリア・ネットワークに存在する可能性が高いことを確認した。

追記:Digital Pacificの該当のIPアドレスをレンタルしているのがオーストラリア警察であるとどうやって判明したかも、VGの元記事に書いてある。VGの記者はシドニーにあるDigital Pacificに直接取材をした。Digital Pacificの創業者であるAndrew KoloadinはVGの説明を聞き、誰にリースしているのかを調査したところ、オーストラリア警察に行き着いた。

「このようなものを我々のサーバーに保持することは契約に反する。警察は我々に事前に相談するべきであった。しかし、そうはしない理由はわかる。秘密捜査とやらだろう」 「貴社のサーバーに警察が性的犯罪物を置くことについてどう思われますか?」
「犯罪者は頭がよく、警察をかわすシステムの設置方法に精通していた。なので、警察も同じぐらい頭が良くなければならないし、実際そのようだ。しかし、警察が我々の及び知らないところでこういうことをやっているのは気に入らない。」

そして、VG記者は次の日にオーストラリア警察に取材を申し込み、二人の捜査官と喫茶店で話をすることになる。

この件は倫理と技術の両方の点で面白い事件だった。筆者はおとり捜査には反対の立場だ。理由は、児童ポルノサイトが存在すれば、そこにアクセスする人間が一定の割合で存在する。するとおとり捜査を続ければ続けるほど、検挙ができるということになる。検挙は成果でありインセンティブとなる。おとり捜査を続ければ続けるほど成果が出るのであれば、インセンティブに従って、おとり捜査は発覚するまで永久に続けるべきであるということになる。結果として今回のオーストラリア警察による児童ポルノサイトの運営は11ヶ月も続けられた。これではミイラ取りがミイラになったようなものだ。しかし、警察のおとり捜査によって児童ポルノサイトが存在しなければ、そのような犯罪は本来発生しなかったはずだ。

このようなインセンティブの問題のため、おとり捜査は違法となるべきだ。

2017-10-05

VS CodeがDOMによるターミナル実装のパフォーマンスを改善できなかったためCanvasに変更

Integrated Terminal Performance Improvements

Electronという史上まれに見るそびえ立つクソのようなGUIプラットフォーム上で実装されているVS Codeが、ターミナルの実装をDOMによるものからCanvasによるものに変更したそうだ。これは、DOMによる実装ではパフォーマンスの改善が十分にできなかったからだという。

DOMでターミナルを実装する際の問題ごととして、テキスト選択、テキストアライメント、GC、パフォーマンスを上げている。

テキスト選択:ターミナルのテキスト選択を実現するためにDOMのテキスト選択の挙動をだいぶ上書きしなければならない。

テキストアライメント:一部の文字はモノスペースになってくれず、workaroundとして一文字ごとに固定長のspanで包む必要があるが、これはパフォーマンス上よろしくない。

GC:DOMでターミナルを実装するためにメチャクチャなことをするのでGCによってパフォーマンスが劣化する。オブジェクトをプールすることでなるべくDOMを使いまわしGCを低減する泥臭いハックをしたが、限界がある

パフォーマンス:色々と頑張ったが、結局パフォーマンスは悪かった。

そして結局、要素を組み合わせてレイアウトを決定するという処理だけで16.6ms以上かかる場合もあり、60fpsを達成できないので、ターミナルをDOMで実装するのは不向きだという結論に達したそうだ。

フルHDのデスクトップ画面をキャプチャしてH.264などにエンコード、デコードすることをリアルタイムで行えるほどコンピューターが高速化したこの2017年にターミナルを60FPSで実装するのに苦労しているのはすさまじい。

やはりElectronは悪い文明 粉砕する!