2015-01-29

闘会議2015のアナログゲームエリアのボードゲームの紹介

きたる闘会議2015のアナログゲームエリアでは、様々なボードゲームが遊べる。すでに当日設置されるゲームは決まっているのだが、公式サイトはまだ更新されていない。

闘会議はスタッフの多くがドワンゴ社員である。ボドゲのインストができるスタッフが少なく、急遽社内のボドゲ好き社員に応援が要請されて、私もスタッフとして参加することになった。

何しろ、闘会議というのは今回が初めてなので、当日の雰囲気が全く予想できない。全くのボドゲ初心者が来るのか、はたまた、ガチ勢が来るのか。初心者にはボドゲに触れてもらい、上級者はそのまま楽しめるような雰囲気なればいいのだが。

ともかく、当日設置されているボドゲの一覧を紹介しようと思う

初級エリア

初級エリアのボードゲームは、どれも説明が簡単で、未経験者でもすぐに遊べるボードゲームが選ばれている。

  • おばけキャッチ

    参加人数は2-6人。短時間のアクションゲームである。カードに描かれた物を早いものがちで取るゲームだ。

  • ワードバスケット

    参加人数は2-8人。しりとりだ。はじめの文字だけではなく、終わりの文字も指定されている。筆者はこのゲームが非常に苦手だ。強い人はとてつもなく強いのだが。

  • どうぶつしょうぎ

    参加人数は2人。大胆に簡略化した将棋だ。

  • よんろのご

    参加人数は2人。大胆に簡略化した碁だ。

中級エリア

中級ボードゲームは、事前に少し長い説明が必要になってくるものもある。

  • ポーカー

    十分に有名なゲーム。ガチ勢に初心者が混じった場合、すぐに飛ぶだろう。

  • ラブレター

    手札を回していきながら相手の手札の中身を予想して勝負を仕掛けるゲーム。

  • ワンナイト人狼

    参加人数3人から7人。人狼系

  • 赤ずきんは眠らない

    参加人数4-6人。人狼系

  • ごきぶりポーカー

    参加人数2-6人。「これはゴキブリです」と言いながらハエやクモを渡すブラフゲーム。

  • エセ芸術家ニューヨークへ行く

    参加人数5-10人。ひとつのキャンバスにみんなで一筆づつ指定された物の絵を書いていく。ただし、参加者の中に一人だけ何の絵を描くか知らされていないエセ芸術家がいる。エセ芸術家をあてれば芸術家達の勝ち。ただし、何の絵を描いているかエセ芸術家にバレてしまうと芸術家達は負ける。

初級と中級のゲームは、未経験者でもできるし、時間もそれほどかからない。問題は、上級エリアだ。

上級エリア

上級ゲームは、説明にも時間がかかるし、プレイ時間も長い。1時間以上は確実にかかるゲームが多い。

  • カルカソンヌ

    参加人数2-5人。土地をつなげていくゲーム。ルールはそれほど複雑ではない。ガチ勢が来るかどうかはわからない。

  • カタンの開拓者たち

    参加人数4人。サイコロを振って資源を獲得して建築をし、10点先取するゲーム。ガチ勢の来る可能性がある。主に筆者がインスト要員として割り当てられている。

  • スコットランド・ヤード:東京

    参加人数2-8人。怪盗一人を刑事達が捕まえるゲーム。プレイ時間は長いが、ルールは簡単。ガチ勢もいない。

  • モノポリー

    参加人数2-8人。すごろくのように環状のマス目を回りながら不動産取引をして、他のプレイヤーを破産させて勝ち残るゲーム。ガチ勢の来る可能性がある。

  • 将棋

    参加人数2人。ご存知の将棋。指導できるほどの棋力を持つスタッフはいない。いい勝負になるほど実力が拮抗した参加者同士で対戦できるといいのだが、どうなることやら。

  • 麻雀

    参加人数4人。ご存知の麻雀。自動卓がないので手積み、東風戦。麻雀のルールが分からない全くの未経験者はお断わりいただく予定。

上級エリアのうち、カルカソンヌとスコットランドヤードは未経験者でも楽しめるゲームだ。ただし、プレイ時間が長めなので注意。

モノポリーは有名なゲームだ。ルールはそれほど複雑ではないが、ガチ勢がやってくる可能性もある恐ろしいゲームだ。

カタンはルールの説明にも時間がかかり、プレイ時間も長い。加えてガチ勢がやってくる可能性もある。

将棋と麻雀に関しては、どのくらいの実力を持った人間がやってくるのか予測できない。あらかじめプロを配置しているわけでもないので、完全に参加者が遊べる場所を提供するだけになる。

ドワンゴ広告

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

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

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

2015-01-28

ドワンゴのプログラミングコンテストをクリアできなかったお話

dwangoプログラミングコンテスト2016

ドワンゴが主催するプログラミングコンテストの予選が、24日に行われたそうだ。筆者はクリアできなかったが、簡単なものだけ解説する。本格的な解説が読みたい人は、わざわざこの記事を読まずとも、以下で解説されているようだ。

「dwangoプログラミングコンテスト」予選問題解説 // Speaker Deck

A: プレミアム会員 - dwangoプログラミングコンテスト | AtCoder

ニコニコ動画には、プレミアム会員という制度があります。このプレミアム会員制度には月額一定の額を支払うことで加入できます。

ニワンゴくんは、この n ヶ月間連続してプレミアム会員です。 また、x ヶ月前に月の一定支払い額が 525 円から 540 円に変わったことを知っています。 つまり、この n ヶ月のうち最近の x ヶ月間は月額 540 円支払っていて、それ以外の月では 525 円払っていたことが分かっています。

n と x が与えられるので、この n ヶ月間でニワンゴくんがプレミアム会員制度に支払った合計額を出力してください。

\(525(n-x) + 540x\)を計算すればよい。

#include <iostream>
 
int main()
{
    int n = 0 ;
    int x = 0 ;

    std::cin >> n >> x ;
 
    int payment = ( n - x ) * 525 + x * 540 ;
 
    std::cout << payment << '\n' ;
}

B: ニコニコ文字列 - dwangoプログラミングコンテスト | AtCoder

0 から 9 の数字から成る文字列 S が与えられます。

ある文字列 X について、X="25" または X="2525" または X="252525" …… というふうに "25" を何回か繰り返した文字列になっているとき、X はニコニコ文字列であるといいます。 たとえば "25" や "25252525" はニコニコ文字列ですが、"123" や "225" はニコニコ文字列ではありません。

あなたの仕事は、文字列 S について、ニコニコ文字列となるような連続した部分文字列の取り出し方が何通りあるかを答えることです。 文字列として同じであっても、取り出し位置が異なっていれば別々に数えます。

与えられた文字列から連続した"25"をすべて探し出し、25の数を数える。N個の"25"が連続した文字列の部分文字列の取り出し方は、1+2+3+...+N通りあるので、\(\frac{N(1 + N)}{2}\)で計算すればよい。

ただし、Sの長さは100000以下であるとのことなので、もし\(\frac{N(1 + N)}{2}\)の計算に32bit符号付き整数を使っていた場合、50000回連続した文字列が渡されると、50001 * 50000を計算しようとするとオーバーフローしてしまう。

連続した"25"文字列は、C++11で追加された<regex>ライブラリを使えばよい。

#include <iostream>
#include <string>
#include <regex>

unsigned long long int count( unsigned long long int n )
{
    return ( 1 + n ) * n / 2 ;
}

int main()
{
    std::string str ;
    std::cin >> str ;

    std::regex re(R"((?:25)+)") ;

    std::sregex_iterator iter( str.cbegin(), str.cend(), re ) ;
    std::sregex_iterator end ;

    unsigned long long int sum = 0 ;
    for ( ; iter != end ; ++iter )
    {
        auto n = iter->str().length() / 2 ;
        sum += count( n ) ;
    }

    std::cout << sum << '\n' ;
}

ただし、このコードはatcoderを通らない。理由は、atcoderのC++11のコンパイラーがGCC4.8.1という2年前の化石バージョンで、libstdc++がregex_iteratorをサポートしていないからだ。そのため、以下のようにregex_searchを複数回呼び出すというマヌケなコードを書かなければならない。

#include <iostream>
#include <string>
#include <regex>

unsigned long long int count( unsigned long long int n )
{
    return ( 1 + n ) * n / 2 ;
}

int main()
{
    std::string str ;
    std::cin >> str ;

    std::regex re(R"((?:25)+)") ;

    std::smatch m ;

    unsigned long long int sum = 0 ;

    auto iter = str.cbegin() ;
    auto end = str.cend() ;


    while ( iter != end )
    {
        std::regex_search( iter, end, m, re ) ;
        auto n = m.str().length() / 2 ;
        sum += count( n ) ;

        iter = m[0].second ;
    }

    std::cout << sum << '\n' ;
}

と思ったが、これも時間制限内に終わらない。Wandboxで試しても終わらないので、どうもlibstdc++ 4.8.1のregexライブラリには問題があるようだ。

[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ

2年前のC++コンパイラーなど使うべきではないという教訓が得られた。

C: ゲーマーじゃんけん - dwangoプログラミングコンテスト | AtCoder

期待値の計算方法がわからないのでモンテカルロ法で解こうとしたが、必要な精度が出ずに敗北した。小数点以下6桁は辛い。

なんでも、必要な精度を出すためには、1兆回は試行しなければならないそうだ。制限時間は2分。すると、一回の試行は0.12ナノ秒で終えなければならない。無理だ。

本の虫: 全プログラマーが知るべきレイテンシー数

最終的に以下のようなクソコードを書きちらしたが、もちろん必要な精度は出ない。ちなみに、atcoderはfutureを使うと実行時エラーになる。

#include <algorithm>
#include <iostream>
#include <string>
#include <random>
#include <future>
 
enum { Rock = 0, Paper = 1, Scissors = 2 } ;
 
template < typename Iter, typename RNG >
unsigned long long int play( Iter begin, Iter end, RNG rng )
{
    unsigned long long int i = 0 ;
 
for ( ; ; ++i )
{
    int n = std::distance( begin, end ) ;
 
 
    // 勝者決定判定
    if ( n == 1 )
    {
        break ;
    }
 
    // じゃんけん
    std::generate( begin, end, rng ) ;
 
 
    // 出された手が 1 種類の場合 
    int first_hand = *begin ;
    bool b = std::all_of( begin, end,
        [=](int x) { return x == first_hand ; } ) ;
 
    if ( b )
    {
        continue ;
    }
 
    // 出された手が 2 種類以上の場合
 
    // 手をカウント
    
    auto rock = std::count( begin, end, Rock ) ;
    auto paper = std::count( begin, end, Paper ) ;
    auto scissors = std::count( begin, end, Scissors ) ;
 
 
    // 出したプレイヤーの数が最も少ない手に注目する。 
 
    // そのような手が 3 種類あった場合
    if ( rock == paper && rock == scissors )
    {
        continue ;
    }
 
 
    auto hands = { rock, paper, scissors } ;
    auto minor = std::min( hands ) ;
 
    // 出ていない手がある場合
    if ( minor == 0 )
    {
 
        typename Iter::difference_type h[3] = { rock, paper, scissors } ;
        std::remove( h, h + 3, 0 ) ;
        auto m = std::min( h[0], h[1] ) ;
        end = begin + m ;
        
    }
    else {
        // すべての手が出た場合
        end = begin + minor ; 
    }
   
}
 
    return i ;
 
}
 
 
constexpr unsigned long long int trial = 10000 ;
 
 
unsigned long long int gamer_zyanken( int n )
{
    std::mt19937 engine ;
 
    std::uniform_int_distribution<> dist( 0, 2 ) ;
 
 
    std::vector<int> players( n ) ;
 
    unsigned long long int sum = 0 ;
 
    for ( int i = 0 ; i != trial ; ++i )
    {
        sum += play( players.begin(), players.end(), [&]{ return dist(engine) ; } ) ; 
    }
 
    return sum ;
}

 
int main()
{
    int n ;
    std::cin >> n ;
 
    auto f1 = std::async( std::launch::async, gamer_zyanken, n ) ;
    auto f2 = std::async( std::launch::async, gamer_zyanken, n ) ;
    auto f3 = std::async( std::launch::async, gamer_zyanken, n ) ;
    auto f4 = std::async( std::launch::async, gamer_zyanken, n ) ;
 
    unsigned long long int sum = f1.get() + f2.get() + f3.get() + f4.get() ;
 
    double result = double(sum) / double(4 * trial) ;
 
    std::cout << result << '\n' ;
}

ドワンゴ広告

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

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

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

2015-01-26

Convictedというボドゲが送られてきた

私宛に、Convictedというボードゲームが贈られてきた。調べたところ、Kickstarterで出資を受けたボードーゲームらしい。

The Convicted - expand your town and fight for survival by Mateusz Albricht — Kickstarter

設定としては、囚人が未開地の開拓を条件に王から恩赦を受けて、野蛮人や怪物などの襲撃から防衛しつつ、都市を発展させていくというものらしい。

具体的なルールは把握していないが、極めて重いゲームで、プレイには90分以上かかるらしい。

C++11の時間ライブラリ: chrono

<chrono>は、C++11で追加された時間ライブラリである。

単位時間を扱うためのduration、起点からの経過時間を扱うためのtime_point、現在の起点からの経過時間を取得するためのclockからなる。Cの標準ライブラリのtime_tとtime(), clock_gettime()を置き換えることが出来る。日付機能は含まれていない。

duration

時間について考える。一時間は60分である。1分は60秒である。1秒は1000ミリ秒である。

単位の異なる時間の値を相互に変換するのは、簡単な計算だ。

unsigned int min_to_sec( unsigned int min )
{
    return min * 60 ;
}

しかし、実引数minに渡される値の単位が分であることを保証する方法はない。間違えたとしても、コンパイルエラーにはならない。

chronoでは、時間単位を扱うライブラリ、durationを追加した。これは型安全に時間の計算をしてくれる。


#include 

int main()
{
    // 15分
    std::chrono::minutes min(15) ;

    // 分を秒に変換
    std::chrono::seconds sec = min ;

    // 900
    std::cout << sec.count() << std::endl ;

    // エラー、余りが発生する可能性があるため
    min = sec ;

    // OK
    min = std::chrono::duration_cast<std::chrono::minutes>( sec ) ;
}

durationテンプレートには、よく使う時間単位、hours, minutes, seconds, miliseconds, microseconds, nanosecondsというtypedef名があらかじめ宣言されている。

また、C++14からは、時間単位のtypedef名へのユーザー定義リテラルが定義されている。それぞれ、h, min, s, ms, us, nsとなっている。

auto hours = 3h ;
auto seconds = 100s ;

auto sum = 1h + 5min + 3s ;

durationクラスは、メンバー関数countにより、内部表現の値を得ることができる。単位はdurationテンプレートの特殊化のとおりだ。

int main()
{
    std::chrono::seconds s(10) ;

    s.count() ; // 10

    auto s2 = s + s ;

    s2.count() ; // 20

    std::chrono::hours h(1) ;
    h.count() ; // 1

    s = h ;

    s.count() ; // 3600
}

time_point

time_pointは、ある起点時間からの経過時間を表現するクラスだ。time_point同士を減算すると、その結果はdurationになる。time_pointを直接構築することはあまりない。time_pointは、clockから得ることができる。C標準ライブラリのtime_tに比べて、型安全になっている。

clock

clockは、現在のtime_pointを取得するクラスだ。staticメンバー関数のnowでtime_pointを取得できる。

int main()
{
    // 処理前の起点からの経過時間
    auto t1 = std::chrono::system_clock::now() ;

    // 処理
    std::this_thread::sleep_for( std::chrono::seconds(1) ) ;

    // 処理後の起点からの経過時間
    auto t2 = std::chrono::system_clock::now() ;

    // 処理の経過時間
    auto elapsed = t2 - t1 ;

    // 単位は未規定
    std::cout << elapsed.count() << std::endl ;
}

system_clockは、システム上のリアルタイムクロックを表現するclockである。

このクロックの使うdurationは未規定である。そのため、経過時間を実際の時間単位で知りたければ、duration_castが必要になる。

int main()
{
    // 処理前の起点からの経過時間
    auto t1 = std::chrono::system_clock::now() ;

    // 処理
    std::this_thread::sleep_for( std::chrono::seconds(1) ) ;

    // 処理後の起点からの経過時間
    auto t2 = std::chrono::system_clock::now() ;

    // 処理の経過時間をミリ秒で取得
    auto elapsed = std::chrono::duration_cast< std::chrono::milliseconds >(t2 - t1) ;

    // 単位はミリ秒
    std::cout << elapsed.count() << std::endl ;
}

C++規格は、起点時間がいつなのかを規定していない。経過時間はtime_pointのメンバー関数tiem_since_epochで取得できる。また、system_clockから得られるtiem_pointは、time_tに変換できる。

int main()
{
    // time_point
    auto t1 = std::chrono::system_clock::now() ;
    // 起点時間からの経過時間
    std::cout << t1.time_since_epoch().count() << '\n' ;

    // time_t
    auto t2 = std::chrono::system_clock::to_time_t( t1 ) ;
    std::cout << t2 << '\n' ;

    // tme_point
    auto t3 = std::chrono::system_clock::from_time_t( t2 ) ;

    std::cout << t3.time_since_epoch().count() << std::endl ;
}

system_clockのtime_pointとtime_tが、同じ時間単位の分解能を使っているとは限らない。

clockはsystem_clockだけではない。他にも、steady_clockがある。これは、実時間の経過によって、time_pointの経過時間が減らないことが保証されている。

int main()
{
    // time_point
    auto t1 = std::chrono::steady_clock::now() ;

    // この間にシステムの時刻が過去に変更されるかもしれない


    auto t2 = std::chrono::steady_clock::now() ;


    // trueであることが保証されている
    bool b = t2 >= t1 ;
}

その他のclockにも、constexpr staticデータメンバーのis_steadyの値によって、steady_clockと同じ保証があるかどうかを確かめることができる。

// true/false
bool b = std::chrono::system_clock::is_steady ;

C++規格はもうひとつ、high_resolution_clockを規定している。これは、時間の分解能が高いclockであると規定されている。

auto t1 = std::chrono::high_resolution_clock::now() ;
// 処理
auto t2 = std::chrono::high_resolution_clock::now() ;

// 処理のかかった時間
auto e = t2 - t1 ;

ドワンゴ広告

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

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

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

GCCのoverrideのバグらしきもの

ask.fmにこのような質問がきた。

C++では、overrideはキーワードではない。overrideは、特定の文脈でだけ文法で認識され、それ以外の場所では、単なる識別子となる。

そのため、以下のようなコードが書ける。

struct override { } ;

struct B
{
    virtual auto f() -> override ;
} ;

struct D : B
{
// GCC: error: two or more data types in declaration of 'type name'
    auto f() -> override override ;
} ;

しかしなぜかGCCは開発版の5.0ですら、コンパイルエラーとなる。

まさか未だにこのような面白い問題が残っているとは。

GCC Bugzillaを検索したところ、同じ不具合は報告されていないようなので、報告してみた。

Bug 64794 – GCC failed at virtual function with "override" trailing return type name, followed by override virt-specifier

Clangでは問題がない。

ドワンゴ広告

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

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

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

2015-01-25

人の顔が認識できない

どうも、私は人の顔を認識する能力が劣っているのではないかと、最近考えるようになった。

妖怪ハウスに何度か遊びに来ている客に、つい、「どちら様でしたっけ」と訪ねてしまうことがよくある。そのたびに、「もう何度も会いましたよ」とか、「何度も同じことを聞かれていますよ」などと言われるのだが、やはり思い出すことができない。

極めつけは、妖怪ハウスに住んでいた元住人ですら、顔を忘れていたことだ。なるほど、言われてみれば確かに、住んでいた当時はそのような顔をしていた気もする。しかし確証が持てない。名前は忘れていないし、その人物の言動も覚えている。話してみると、なるほど、確かにそのような声でそのような口調であったので、本人なのであろうが、やはり問題の顔面がそのようなものであったかどうか断定できない。

どうも私は、個人を識別するのに、服、髪型、体型などの情報を使っているのではないかと思われる。

思えば、同じような体型と髪型の人間が似たような服を着ていた場合、間違えてしまうことがよくあるのだ。

関係しているかどうかわからないが、私は人の顔面がイケメンや美人であるかどうかをうまく評価することができない。よく人が、「誰々はイケメンである、美人である」などと評しているのを聞いても、私にはよくわからないのだ。

これまでの経験から推定するに、世の中のイケメン、美人と他人が評価している人間に共通する項目というものは、頬骨が張り出るほど頬が痩せこけていて、髪が頬を隠すほど長いものを言うものだと思われる。しかし、これは思えば顔の外側の特徴だ。目鼻や口の特徴ではない。

はてさて、困ったことだが、どうしようもない。

2015-01-20

C++の正規表現ライブラリ: std::regex

いまさらながら、C++の正規表現ライブラリを調べている。

C++の正規表現ライブラリ、std::regexは、boost::regexを土台に設計されている。boost::regexの正規表現の文法は、perlなのに対し、std::regexは、ECMAScriptである。この理由は、しっかりと正規表現の文法が定義されていて、外部規格として参照できる品質のものが、perlには存在しないためだ。std::regexはposixと拡張posixとawkとgrepとegrepの正規表現にも対応している。

本記事では、ECMAScriptの正規表現を使う。また、参考のためのECMAScriptのコードも使う。

全体一致

文字列全体が正規表現に一致するかどうかを調べたいとする

var re = /1234/ ;
var text = "1234" ;

var result = re.test( text ) ;

これを、std::regexを使って書くと以下のようになる。

#include <regex>

int main()
{
    std::regex re("1234") ;
    std::string text("1234") ;

    bool result = std::regex_match( text, re ) ;
}

C++には、正規表現リテラルはないので、文字列リテラルで正規表現を記述する。

もうすこし正規表現らしいことをしてみよう。連続した数字に一致する正規表現を使ってみる。

var re = /\d+/ ;
var text = "1234" ;

var result = re.test( text ) ;

残念ながら、C++の文字列リテラルで/\d+/を書こうとすると、エスケープシーケンスのためにエラーになる。

std::regex r1 = "\d+" ; // エラー
std::regex r2 = "\\d+" ; // OK

"\\d+"と書けば動くのだが、これはとても書きづらい。C++11で追加された生文字列リテラルを使えば、自然に書けるようになる。

std::regex re = R"(\d+)" ;

外側の括弧は生文字列リテラルの文法なので紛らわしいが、バックスラッシュを二重に書くよりはマシだろう。

部分一致の検索

以下のようなECMAScriptのコードをC++で書きたいとする

var re = /\d+/ ;
var text = "There are 99 bottles." ;

var a = re.exec( text ) ;
var result = a[0] ; // "99"

C++では、以下のように書ける。

int main()
{
    std::regex re( R"(\d+)" ) ;
    std::string text="There are 99 bottles." ;

    std::smatch m ; // std::match_results<string::const_iterator>
    std::regex_search( text, m, re ) ;

    std::cout << m.str() << std::endl ;
}

部分一致を検索するには、regex_searchを使う。結果は、match_resultsで受け取ることができる。

部分一致をすべて検索。

以下は、部分一致をすべて検索するECMAScriptのコードである。

var re = /\d+/g ;
var text = "123 456 789" ;

// ["123", "456", "789"]
var result = text.match( re ) ;

C++では、イテレーターを使ってregex_searchを繰り返し呼び出すことで書くことができる。

std::vector< std::string >
search( std::string const & text, std::regex const & re )
{
    std::vector< std::string > result ;

    auto iter = text.cbegin() ;
    auto end = text.cend() ;

    std::smatch m ;

    while ( std::regex_search( iter, end, m, re ) )
    {
        result.push_back( m.str() ) ;
        iter = m[0].second ;
    }

    return result ;
}

int main()
{
    std::regex re( R"(\d+)" ) ;
    std::string text="123 456 789" ;

    auto result = search( text, re ) ;

    for ( auto & elem : result )
    {
        std::cout << elem << std::endl ;
    }
}

とはいえ、これは面倒だ。

C++には、このように部分一致を複数見つける用途に、regex_iteratorを用意している。これを使えば、以下のように書くことができる。

std::vector< std::string >
search( std::string const & text, std::regex const & re )
{
    std::vector< std::string > result ;

    std::sregex_iterator iter( text.cbegin(), text.cend(), re ) ;
    std::sregex_iterator end ;

    for ( ; iter != end ; ++iter )
    {
        result.push_back( iter->str() ) ;
    }

    return result ;
}

置換

以下のように、DogをCatに置き換えたいとする。

var re = /Dog/g ;
var text = "Dog is nice. Dog is cute. Dog is awesome." ; 

// "Cat is nice. Cat is cute. Cat is awesome."
var result = text.replace( re, "Cat" ) ;

C++では、regex_replaceを使って書くことができる。


int main()
{
    std::regex re( R"(Dog)" ) ;
    std::string text("Dog is nice. Dog is cute. Dog is awesome.") ;

    // "Cat is nice. Cat is cute. Cat is awesome."
    auto result = std::regex_replace( text, re, "Cat" ) ;
}

以下のように最初の一致だけを書き換えたい場合、

var re = /Dog/ ;
var text = "Dog is better than dog." ; 

// "Cat is better than dog."
var result = text.replace( re, "Cat" ) ;

以下のように書くことができる。

int main()
{
    std::regex re( R"(Dog)" ) ;
    std::string text="Dog is better than dog." ;

    // "Cat is better than dog."
    auto result = std::regex_replace( text, re, "Cat",
        std::regex_constants::format_first_only ) ;
}

置換はECMAScriptの文法と同じだ。

// ECMAScript
var text = "123 456"
var re = /(\d+) (\d+)/ ;

// "456 123"
var result = text.replace( re, "$2 $1" ) ;
// C++
std::string text("123 456") ;
std::regex re(R"((\d+) (\d+))") ;

// "456 123"
auto result = std::regex_replace( text, re, "$2 $1" ) ;

サブマッチ

正規表現は括弧でキャプチャをすることができる。

var re = /(\d+) (\d+) (\d+)/ ;
var text = "123 456 789" ;

// ["123 456 789", "123", "456", "789"]
var result = text.match( re ) ;

C++では、match_resultsから得られるsub_matchをたどることで、同等のことができる。


std::vector< std::string >
match( std::string const & text, std::regex const & re )
{
    std::vector< std::string > result ;

    std::smatch m ; // match_results

    std::regex_match( text, m, re ) ;

    for ( auto && elem : m )
    {// elemはsub_match
        result.push_back( elem.str() ) ;
    }

    return result ;
}

int main()
{
    std::regex re( R"((\d+) (\d+) (\d+))" ) ;
    std::string text="123 456 789" ;

    // {"123 456 789", "123", "456", "789"}
    auto result = match( text, re ) ;

    for ( auto && elem : result )
    {
        std::cout << elem << std::endl ;
    }
}

std::regexを一通り触ってみた感想としては、Boost.Regexのうち、規格として定義できる部分だけ定義したという感じだ。

std::regexと現行の主要なライブラリ(libstdc++とlibc++)の実装を調べると、ASCII文字か、あるいは単なるバイト列に対して使うことが出来る正規表現でしかなかった。charとwchar_tには対応している。ただし、ASCII文字以外には対応していない。UTF-8を使えばバイト列として一致させることはできた。つまり、

// 通常の文字列リテラルのエンコードはUTF-8の環境
std::regex re(R"(いろは にほへ)") ;
std::string text("いろは にほへ") ;

std::regex_match( text, re ) ;

これは動く。ただし、wchar_tは動かない。

std::wregex re(R"(いろは にほへ)") ;
std::wstring text(L"いろは にほへ") ;

// 動かない
std::regex_match( text, re ) ;

UTF-8は単なるバイト列だと考えるしかない。例えば以下は動かない。

// 通常の文字列リテラルのエンコードはUTF-8の環境
std::regex re(R"(.)") ;
std::string text("あ") ;

// 動かない
std::regex_match( text, re ) ;

char16_t、char32_tは、libstdc++とlibc++では、コンパイルすら通らない。

というわけで、現状の規格と実装状況から言えば、ASCII文字だけに限定していいのならば、std::regexとstd::wregexは使える。Unicodeが扱いたければ、規格外の正規表現ライブラリを使うべきだ。

ドワンゴ広告

そろそろC++のブログ記事を増やしていきたい。

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

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

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

2015-01-16

インドでC型肝炎の薬の特許が無効化された理由

How India's Patent Office Destroyed Gilead's Global Game Plan - Businessweek

インドの特許庁が、Gilead Sciences社のC型肝炎の薬、Sovaldiの特許を無効化した理由が取り上げられていて興味深い。

該当の特許の化合物は、既存の化合物と似通っているためと、発明者が既存の薬に比べて劇的な効果の違いを証明できなかったためだという。

2015-01-15

ガラケー時代の翻訳

When I was in Japan I did proof reading for a Japanese feature phone. A major Ja... | Hacker News

Hacker Newsのコメントが面白かった

日本にいた時に、日本のガラケーの翻訳検証をしたことがある。ある有名な日本のブランドだった。あれは実に喜劇であった。

英語を検証するオーストラリア人と、ドイツ人の男と、イタリア人の女と、フランス人の私がいた。検証前に行われていたこと:英語力の貧弱な人間(おそらくはソフトウェアエンジニア)による日本語から英語への翻訳があり、その不思議な英語を、文脈を一切与えられずに文字列だけを与えられた翻訳家が別の言語に翻訳していた。

現場では、我々に示されたものは文字列だけであった。そして、製造元からやってきた「超企業秘密」な未公開のデバイスにアクセスできる担当者が一人。

半分以上もの翻訳は、文脈の欠如により間違っていた。フランス語の翻訳家は、「ゴミの日」を、「クソな日」と訳していた。おそらく、とてつもなくついていない一日をカレンダーに書き込むのに使うと考えたのだろう。

「(あるものを)削除する」のような文章が頻繁に現れた。当然、我々には、「あるものって何だ? 男性名詞か女性名詞か中性名詞かどれだ?」という疑問が生じた。もちろん、それはできない相談であった。すでにコードを書き変えるには遅すぎるため、我々は、"%n item(s)"のような汚い手法を取る必要があった。

ところで、現場のオーストラリア人は人類への希望を失いつつあった。ある文章が、完全に間違っていたのだ。その文章は英語で何らの意味をも持たなかった。それを読んだ現場の人間は「なんじゃこりゃ?」というしかなかった。我々には英語の文字列を変えることは許されていなかった。それはすでに検証済みであったからだ。

さもありなん

2015-01-14

Linus Torvalds、HFS+に激怒

CVE-2014-9390 aka "Git on case-insensitive filesystems" I did not give the…

gitが影響を受けた、HFS+で、一部の文字を区別しなかったり無視したりする問題に対して、Linusが吠えている。

マジで、HFS+はたぶん最悪のファイルシステムだな。クソすぎるぜ。NTFSもutf8の正規化で似たような問題(/の非正規化された表現を使用)があったが、まあ、今は修正されたんだろうよ。OS Xの問題は根本的すぎる。

そりゃ、古いさ。そりゃ、データ保護がクソすぎるってのはあるさ。だが、そういうのは、単に「すげーファイルシステムじゃない」って問題だ。「自分のケツすら拭けないマヌケによって設計された信じがたいクソ」ってわけじゃない。

HFS+の恐ろしさは、すげーファイルシステムではない、ということではない。いいアイディアがあると信じている人間によって、今もなおクソなファイルシステムとして設計され続けているということだ。

大文字小文字の同一視は信じられないくらいクソなアイディアだ。Appleの連中は修正するべきだがそれをしない。逆に、クソなアイディアを更にくそったれにしてUnicodeに拡張していやがる。UTF-8ですらなく、たぶんUCS2だ。

NTFSも似たようなことをやらかしたが、AppleはHFS+でその上をいくクソをやらかしている。

レガシーモデル(「もっとまともな方法を知らなかった」)における大文字小文字の同一視は、まだいい訳もたつ。だがな、Unicodeの同一性比較がファイルシステムにおいてもいいアイディアだと考えた奴はこの業界から干されるべきだろ。離乳食でもやって部屋の隅っこで邪魔にならないように食わしとけ。そうすりゃ奴らは幸せだろうし、俺らのシステムをクソみたいにしないだろうよ。

そして、NFD正規化を持ちだして、正しいunicodeをクソみたいなフォーマットに変換しやがるとは、言い訳も何もあったもんじゃねえよ。正規化はいいことだと考える奴らすらNFDはクソフォーマットだと認めてるし、データ交換のためにいいフォーマットじゃねーよ。離乳食レベルですらねぇ。設計上、ユーザーのデータをぶっ壊し続けてやがる。クソか。

で、Appleのこういうサルどもにファイルシステムのしごとを任せてるわけか? マジで?

ZFSに移行しなかった妥当な理由は色々ある(オホン、オラクル、ゴホン)にしてもだ、大文字小文字を区別するHFS+に移行させてから、それで将来的にはもっとマシなファイルシステムに移行させることもできただろうが、そいつはしなかったんだよな。大文字小文字を区別する方法があるが、Appleは隠して使わせないようにしている。

アホすぎてやってられねーぜ。

で、クソみたいな方針決定をする奴らと、そのクソを実装する奴らがいる。Johnが文句を言っている「よっしゃ、俺らはまともな機能を実装しないぜ」ってのより、「俺らはクソを実装するぜ」ってほうがはるかにひどい。

文句終わり

まあ、実際HFS+のUnicode正規化はクソだ。

2015-01-12

アンチウイルスソフトウェアの脆弱性

Breaking av software

市場に出回っているアンチウイルスソフトウェアの脆弱性についての研究発表のスライド資料が公開されている。

アンチウイルスソフトウェアは、セキュリティ向上のために重要だという意見があるが、このスライド著者は疑問を投げかけている。そもそも、ソフトウェアの追加は、攻撃できる箇所が増えるということだ。アンチウイルスソフトウェアは果たしてセキュアに作られているのか。

特に、多くのアンチウイルスソフトウェアは、カーネルドライバーを使ったりしている。もし脆弱性があればとんでもないことだ。

アンチウイルスソフトウェアの攻撃手段としては、細工されたファイルフォーマットをスキャンさせる事が大半だ。アンチウイルスソフトウェアは、様々なフォーマットのファイルをパースする必要がある。もし、そのパーサーにバッファーオーバーフローなどの不具合があれば任意のコードを実行させることができるし、パースに時間がかかりすぎれば、DOS攻撃に使える。

傑作なのは、既存のほとんどのアンチウイルスソフトウェアが、10年以上前の古臭い手法である、ゼロパディングされた巨大なファイルに展開される圧縮ファイルを現実的な時間で処理できずにリソースを浪費するということだ。

さて、アンチウイルスソフトウェア自体のセキュリティはどうかというと、これまた悲惨だ。

多くのアンチウイルスソフトウェアは、アップデートに素のHTTPを使い、しかもアップデートファイルは署名されていない。信じられない。

多くのアンチウイルスソフトウェアは、ASLRを無効にしている。これは、ヒューリスティックな検証をするために、ファイルをエミュレーター上で実行して挙動を確認するために、ASLRが邪魔になるからだ。問題は、一部のアンチウイルスソフトウェアは、システムのすべてのプロセスにASLRが無効化されたDLLを注入する。セキュアであるべきソフトウェアのセキュリティが聞いて呆れる。

多くのアンチウイルスソフトウェアは、ファイルのパースやネットワークパケットの検証に、エミュレーターやVMどころか、基本的なサンドボックスすら設けておらず、検証がroot権限で行われている。root権限が必要なのはネットワークパケットのキャプチャだけであり、そのデータの検証は、もっと権限の低いプロセスで行うべきである。

また、この研究者は、昔のコードを削除するべきであると主張している。MS-DOS時代のマルウェアや、もはや誰も使っていないファイルフォーマット用のチェックは、現代においては不必要であるし、長年誰も触っていない昔のコードには、脆弱性のある可能性が高い。アンチウイルスソフトウェア自体に脆弱性があるのと、大昔のもはやほとんど問題にならないマルウェアを検出できるのと、どちらがいいというのか。

筆者は、もうアンチウイルスソフトウェアの時代ではないと思っている。

2015-01-09

[[deprecated]] はテンプレート名には使えない

[[deprecated]]属性はテンプレートの特殊化に対して指定できる。テンプレート名に対しては指定できない。

// テンプレートAの特殊化をdeprecated扱い
template < typename T >
class [[deprecated]] A { } ;

template < template < typename > class >
class B { } ;

// deprecated
A<int> a ; 

// 規格上何もなし
B<A> b ; 

どうも現在主流のコンパイラー実装上の都合があるそうだ。

ドワンゴ広告

最近ネタ切れになってきた。ドワンゴでは正月に一気に有給を取得する社員が多いらしく、まだ空席が目立つ。

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

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

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

2015-01-07

巻末C++14の新機能をとりあえず書いた

EzoeRyou/cpp14-appendix

巻末: C++14の新機能

C++14のコア言語の新機能を解説するために、急遽巻末としてC++14の新機能を書いているが、とりあえずC++14で追加されたコア言語の新機能としては、全て網羅した解説を書き終えた。

文法と意味を解説するのは簡単だが、さて、これをどうやって改良しよう。

ドワンゴ広告

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

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

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

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

2015-01-06

[[deprecated]]の文面の疑問点

[[deprecated]]の解説を書くためにCC++14の文面を解釈していて、以下のような疑問を持った。

§ 7.6.5 paragraph 2

The attribute may be applied to the declaration of a class, a typedef-name, a variable, a non-static data member, a function, a namespace, an enumeration, an enumerator, or a template specialization.

テンプレートの特殊化(template specialization)という用語の使用がとても気になる。

何故ならば、テンプレートの特殊化の宣言というものは、明示的なテンプレートの特殊化ぐらいしか思いつかないからだ。通常のテンプレートの特殊化は、暗黙のテンプレートの実体化された結果であり、宣言は存在しない。プライマリーテンプレートの宣言はテンプレートの宣言であり、テンプレートの特殊化の宣言ではない。

すると、テンプレート宣言にはdeprecated属性が使えないのだろうか。

「テンプレートの特殊化」という用語を使用することのもうひとつの問題は、template-idに限定してしまうということだ。template-nameはテンプレートの特殊化ではない。

// テンプレートの特殊化の宣言ではないが
template < typename T >
class [[deprecated]] A { } ;

template < template < typename > typename T >
class B { } ;

// 警告
// template-idはテンプレートの特殊化
A<int> ;

// 警告なし
// template-nameはテンプレートの特殊化ではない
B<A> b ;

現在のClangの実装は、正しく(?)A<int>とB<A>のどちらとも警告を出さない。GCCはどちらとも警告を出す。

こういう細かい文面解釈の問題が気になってしまってなかなかC++14解説の執筆が進まない。

ドワンゴ広告

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

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

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

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

2014-12-29

近況

私の住んでいる妖怪ハウスに海外から不思議な荷物が贈られてきた。宛名はRYOU EZOEになっている。

開けてみると、DON'T TRACK USと書かれたTシャツが入っていた。

おそらくは、Don't Track Us T-Shirt – Duck Duck Goではないかと思われる。

29日は秋パンに今年最後のボルダリングに行く。

30日は、青春18切符で京都まで移動する予定だ。

どうも最近、C++以外のブログ記事がおろそかになってしまっているようだ。なにか書こうと思ったが、夜も遅いのでもう寝なければならず、長文が書けない。

2014-12-24

C++14の新機能の解説を戻り値の型推定まで書いた

年末だというのに忙しくC++14の新機能の参考書を書いている。とりあえず関数の戻り値の型推定まで書いた。

巻末: C++14の新機能

C++11: Syntax and Feature

もちろんクールなキッズが皆使うというGitHubで管理している。

EzoeRyou/cpp14-appendix

EzoeRyou/cpp-book

ところで、興味深いニュースがあるようだ。

ドワンゴ、IT技術書出版新ブランド「アスキードワンゴ」を設立 - 週アスPLUS

IT技術書出版ブランド「アスキードワンゴ (ASCII DWANGO)」立ち上げのお知らせ|ニュース|広報情報|株式会社ドワンゴ

2. C++11/14の参考書(仮題)

ドワンゴ所属のエンジニアによるC++11/14の文法と機能についての解説書です。

ドワンゴ所属のエンジニア、はて。C++11/14の解説書、はて。

紙に印刷された本の需要はどのくらいあるのだろうか。

ドワンゴ広告

この記事はドワンゴ勤務中に執筆した。

今日も有給取得者が多い様子。今夜もドワンゴのボルダリング部の恒例のボルダリングに行く予定。

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

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

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

2014-12-22

C++14の新機能の解説

C++11のコア言語の参考書をすでに書いたが、時代はすでにC++14になっている。C++14の新機能の参考書も書きたいが、既存の参考書を更新するのは骨が折れる。

そこで、C++14の新機能だけを解説する短い文書を巻末という形で書くことにした。まだ執筆途中だが、GitHubで公開しながら編集することにした。

EzoeRyou/cpp14-appendix

巻末: C++14の新機能

ドワンゴ広告

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

今日は社内に人が少ない。有給取得者が多そうだ。

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

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

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

2014-12-21

各言語のARVv7とx86-64での実装のパフォーマンス比較

LPATHBench/writeup.md at master · logicchains/LPATHBench

同じ箇所に二回訪れずに最も長い経路を見つけるコードで。各言語の実装を、ARMv7とx86-84においてベンチマークしてみた結果が公開されている。

記事に書いてあるように、これは、ARMv7とx86-64のプロセッサーのベンチマーク比較ではなく、各言語の実装が、ARMとx86-64でどれだけ効率的に実装されているのかを比較している。

たとえば、C++はARMにおいてはx86-64の74%のパフォーマンスを出している。しかし、ARM版OpenJDKはなんと29%の速度しか出ていない。OracleによるJava実装は、40%ぐらいのパフォーマンスを出している。

コードはとても短いが、パフォーマンス改善のためのpull requestがどんどんよせられたらしく、その結果が興味深い。Javaでクラスではなく配列を使うようにしたら速くなったとか、C#のイテレーターは結構オーバーヘッドがあるだとか。

luajitが恐ろしく速いのが興味深い。

2014-12-19

.git/configが書き換えられるgitの脆弱性

WindowsとMac OS X用の公式gitクライアントの脆弱性により、.git/configが書き換えられ、任意のコマンドが実行されてしまう問題が修正されたようだ。

ANNOUNCE Git v2.2.1 and updates to older maintenance tracks

何が問題なのかというと、WindowsやMac OS Xのファイルパスの取り扱いで、異なるが同一として扱われる文字があることだ。

たとえば、Windowsでは、".git~1/config"というファイルパスは、".git/config"というファイルパスと同等のものとして扱われる。Mac OS XのHFS+でも、U+200cのような文字が無視されるので、".g/u200cit/config"が、".git/config"と同等に扱われる。

この結果。そのような名前のレポジトリをcloneしたWindowsやMac OS X環境では、.git/configが上書きされ、任意のコマンドを実行可能になってしまう。

GNU/Linuxでcase-sensitiveなファイルシステムを使った環境では、この問題には影響されない。

修正内容として、WindowsとMac OS X環境では、そのような一部の特殊な文字を拒否するようになったということだ。また、GNULinuxのような環境やでも、クロスプラットフォーム対応のため、そのような文字を拒否するオプションが追加されたそうだ。

2014-12-18

12月25日、妖怪ハウスでケーキ会、12月27日にボドゲ会

特に何をする予定もないし、何も用意はしないが、年末は暇なので、妖怪ハウスで消極的に人を集めてみようと思う。

12月25日はクリスマスなので、妖怪ハウスにケーキでも用意しておこうと思う。

12月27日は、今年最後のボードゲーム会を開きたいものだ。

人が集まるかどうかはわからないが、告知だけしておく。

妖怪ハウスの住所は、中野区野方5-30-13 ヴィラアテネ401となる。


View Larger Map

2014-12-17

プレシジョンダイス

筆者は、ダイスの目にはさんざん悩まされてきた。多くのボードゲームではダイスを使う。問題は、そのダイスの目が明らかに偏っているのではないかということが度々起こるからだ。

例えばカタンだ。カタンでは、六面ダイスを二個使い、目の合計によって該当する土地の資源を得る。六面ダイスを二個振った目の合計値の確率は、2と12が最も少なく1/36、7が最も多く21/36となっている。ところが、今まで経験したカタンでは、よく出るはずの6,7,8がなかなか出なかったり、なかなか出ないはずの2や12が多く出たりすることがたびたびあった。

これは、ダイスの加工精度の問題である。ダイスの加工精度に問題があると、当然出目も偏ってくる。

プレシジョンダイスと呼ばれているものがある。表面に目の凹みがなく、よく研磨されている加工精度のいいダイスである。今、プレシジョンダイスを買うには、どうやらバックギャモン協会のWebサイトから購入するのが手っ取り早いようだ。

プレシジョンダイス - SHOP | 日本バックギャモン協会

そこで、実際に購入してみた。ついでにダイスを降る際も、ダイスカップとダイストレイを利用するようにしてみた。

先週から何度かカタンや街コロをしているが、ダイスの出目に違和感のある偏りは見られなかった。

これから六面ダイスを使うボードゲームの際には積極的に使っていきたい。

2014-12-16

C++14に入る機能テストマクロの一覧

現実のコンパイラーにおいて、C++11やC++14、そして今制定されようとしている各種TSやC++1zなどの機能は、個々に実装される。その場合、あるコンパイラーのあるバージョンは、rvalueリファレンスを実装している、その次のバージョンはconstexprを実装したという、中途半端に機能を実装した状態になる。

その結果、現実には、往々にして以下のようなコードが書かれることになる。

#ifndef __USE_RVALUE_REFERENCES
  #if (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 3) || \
      _MSC_VER >= 1600
    #if __EDG_VERSION__ > 0
      #define __USE_RVALUE_REFERENCES (__EDG_VERSION__ >= 410)
    #else
      #define __USE_RVALUE_REFERENCES 1
    #endif
  #elif __clang__
    #define __USE_RVALUE_REFERENCES __has_feature(cxx_rvalue_references)
  #else
    #define __USE_RVALUE_REFERENCES 0
  #endif
#endif

標準規格としては、一部機能だけ実装することを推奨するのではないが、現実がこうである以上、ある機能が実装されているかどうかを調べる標準のプリプロセッサーマクロが存在するべきである。

そこで提案されているのが、Feature-testing recommendations for C++だ。

この提案では、C++の主要機能が実装されている場合、特定の名前のプリプロセッサーマクロが事前に定義されることとなる。例えば、rvalueリファレンスの場合、__cpp_rvalue_referencesという名前のプリプロセッサーマクロが定義される。

マクロの名前は、名前の衝突を避けるため、プレフィクスが付けられる。コア言語機能ならば__cpp_、ライブラリー機能ならば__cpp_lib_である。機能名は、その機能の論文のタイトルから採用される。

マクロの値は、その機能がドラフトに採用された年と月を表現している。これにより、標準規格策定中にドラフトが更新された場合、値も更新される。たとえば、C++11の機能である__cpp_rvalue_referencesの場合、2006年の10月にドラフト入りしたので、その値は200610となる。

特定のヘッダーファイルがあるかどうかを調べるプリプロセッサーの機能、__has_includeが追加される。これは、#ifや#elifの中で使うことができる。

#ifdef __has_include
#  if __has_include(<optional>)
#    include <optional>
#    define have_optional 1
#  elif __has_include(<experimental/optional>)
#    include <experimental/optional>
#    define have_optional 1
#    define experimental_optional
#  else
#    define have_optional 0
#  endif
#endif

機能テスト推奨に対応している実装で、#ifdefで__has_includeを記述すると、定義済みだと評価される。#ifや#elifで__has_include(ヘッダー)と記述すると、そのヘッダーが存在する場合はtrueとなる。

同様に、特定のattributeがあるかどうかを調べるプリプロセッサーの機能、__has_cpp_attributeが追加される。

#ifdef __has_cpp_attribute
#  if __has_cpp_attribute(deprecated)
#    define ATTR_DEPRECATED(msg) [[deprecated(msg)]]
#  else
#    define ATTR_DEPRECATED(msg)
#  endif
#endif

具体的なマクロ名

実際に論文を読めばいいと思うのだが、とりあえずここにも書いておく。ほとんどの機能は、このブログやC++11の参考書で解説したはずだが、まだ知らない読者はいるのだろうか。なにか新たに解説して欲しい機能があればコメントで言えば解説するつもりだ。

C++98機能

機能 セクション番号 マクロ名 値 ヘッダー
実行時型情報 5.2 __cpp_rtti 199711 predefined
例外 15 __cpp_exceptions 199711 predefined

C++11機能

文書番号 文書名 セクション番号 マクロ名 値 ヘッダー
N2249 ユニコード文字型 2.13 __cpp_unicode_characters 200704 predefined
N2442 生文字列リテラルとUnicode文字列リテラル 2.13 __cpp_raw_strings 200710 predefined
__cpp_unicode_literals 200710 predefined
N2765 User-defined Literals 2.13, 13.5 __cpp_user_defined_literals 200809 predefined
N2927 lambda式 5.1 __cpp_lambdas 200907 predefined
N2235 constexpr 5.19, 7.1 __cpp_constexpr 200704 predefined
N2930 Range-Based for 6.5 __cpp_range_based_for 200907 predefined
N1720 static_assert 7 __cpp_static_assert 200410 predefined
N2343 Decltype 7.1 __cpp_decltype 200707 predefined
N2761 属性(attribute) 7.6 __cpp_attributes 200809 predefined
__has_cpp_attribute(noreturn) 200809 predefined
__has_cpp_attribute(carries_dependency) 200809 predefined
N2118 rvalueリファレンス 8.3 __cpp_rvalue_references 200610 predefined
N2242 可変長テンプレート(Variadic Templates) 8.3, 14 __cpp_variadic_templates 200704 predefined
N2672 初期化リスト(Initializer List) 8.5 __cpp_initializer_lists 200806 predefined
N1986 コンストラクターのデリゲート 12.6 __cpp_delegating_constructors 200604 predefined
N2756 非staticデータメンバーの初期化子 12.6 __cpp_nsdmi 200809 predefined
N2540 継承コンストラクター 12.9 __cpp_inheriting_constructors 200802 predefined
N2439 リファレンス修飾子(*thisへのムーブセマンティクス) 13.3 __cpp_ref_qualifiers 200710 predefined
N2258 テンプレートエイリアス 14.5 __cpp_alias_templates 200704 predefined

C++14機能

まだこのブログで詳細に解説していない機能もあるはずだ。

文書番号 文書名 セクション番号 マクロ名 値 ヘッダー
N3472 2進数リテラル 2.14 __cpp_binary_literals 201304 predefined
N3781 シングルクオートによる数値区切り 2.14 __cpp_digit_separators 201309 predefined
N3648 汎用lambdaキャプチャー 5.1 __cpp_init_captures 201304 predefined
N3649 ジェネリックlambda式 5.1 __cpp_generic_lambdas 201304 predefined
N3778 サイズ付き解放関数 5.3, 18.6 __cpp_sized_deallocation 201309 predefined
N3652 constexpr関数の制限緩和 5.19, 7.1 __cpp_constexpr 201304 predefined
N3638 関数の戻り値の型推定 7.1 __cpp_decltype_auto 201304 predefined
__cpp_return_type_deduction 201304 predefined
N3760 [[deprecated]]属性 7.6 __has_cpp_attribute(deprecated) 201309 predefined
N3653 アグリゲート初期化とメンバー初期化子の同時利用 8.5 __cpp_aggregate_nsdmi 201304 predefined
N3651 変数テンプレート 14, 14.7 __cpp_variable_templates 201304 predefined
N3658 コンパイル時整数シークエンス(index_sequence) 20 __cpp_lib_integer_sequence 201304 <utility>
N3668 exchange()関数 20 __cpp_lib_exchange_function 201304 <utility>
N3670 tupleで型を指定できるget 20.2-20.4 __cpp_lib_tuples_by_type 201304 <utility>
N3887 テンプレートエイリアスを使ったtuple_element_t 20.3-20.4 __cpp_lib_tuple_element_t 201402 <utility>
N3656 make_unique 20.7 __cpp_lib_make_unique 201304 <memory>
N3421 greater<>のテンプレート実引数の省略 20.8 __cpp_lib_transparent_operators 201210 <functional>
N3462 std::result_ofをSFINAEフレンドリーにする変更 20.9 __cpp_lib_result_of_sfinae 201210 <functional>
N3545 integral_constantにconstexprなoperator ()を追加する変更 20.9 __cpp_lib_integral_constant_callable 201304 <type_traits>
N3655 エイリアステンプレートでメタ関数をラップして::typeを_tにする変更 20.9 __cpp_lib_transformation_trait_aliases 201304 <type_traits>
LWG 2112 is_finalの追加 20.10 __cpp_lib_is_final 201402 <type_traits>
LWG 2247 std::nullptr_t可動化を調べるis_null_pointer 20.10 __cpp_lib_is_null_pointer 201309 <type_traits>
N3642 chronoとstringに対するユーザー定義リテラルの追加 20.11 __cpp_lib_chrono_udls 201304 <chrono>
21.7 __cpp_lib_string_udls 201304 <string>
N3657 連想コンテナでkey_typeに変換可能な型から検索する機能 23.4 __cpp_lib_generic_associative_lookup 201304 <map>
<set>
N3644 Null Forward Iterators 24.2 __cpp_lib_null_iterators 201304 <iterator>
LWG 2285 make_reverse_iterator 24.5 __cpp_lib_make_reverse_iterator 201402 <iterator>
N3671 equal, mismatch, is_permutationで二つのrangeをとるオーバーロード 25.2 __cpp_lib_robust_nonmodifying_seq_ops 201304 <algorithm>
N3779 std::complexのユーザー定義リテラル 26.4 __cpp_lib_complex_udls 201309 <complex>
N3654 quotedライブラリ 27.7 __cpp_lib_quoted_string_io 201304 <iomanip>
N3659 shared_mutex 30.4 __cpp_lib_shared_mutex
__has_include(<shared_mutex>)
1 predefined
N3891 shared_mutexをshared_timed_mutexに改名 30.4 __cpp_lib_shared_timed_mutex 201402 <shared_mutex>

疲れた。

ドワンゴ広告

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

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

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

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

Google Newsに強制課金する法律、あっけなく失敗

Spanish Newspaper Publishers' Association Now Asks Government To Help Stop Google News Closure | The Spain Report

スペインでは、Webサイトが報道機関のWebサイトをわずかでも引用する場合、報道機関に対し対価を支払わなければならないという法律を、報道機関のロビー活動により成立した。それを受けて、Googleは、Google Newsはそれ自体が金を生み出してはいないので、スペイン版を廃止すると宣言した。

どうやら報道機関のロビー団体はスペイン政府にGoogle Newsを廃止しないように何か行動をせよと言っているらしい。不思議なことだ。

ちなみに、ドイツでも同等の法律ができている。そのときもGoogle Newsは廃止を宣言した。その結果、Webトラフィックが激減したため、報道各社は、Googleと特別に取引して、Googleは対価を支払わなくてもよいということになった。その結果、Googleの独占的地位をますます強固たるものにしてしまった。

この件に関してはGoogleの方が役者は上だということだろう。

それにしても、市場をほぼ独占している最大手のGoogleが自らやめると宣言してくれたのだから、今から報道各社で検索サービスを開発して公開すれば、市場シェアを取れるはずだが、それはしないらしい。もちろん、Googleほどの効率を出すのは難しいだろうし、実際にGoogleが主張する通り、検索サービス単体は直接利益にならないのだろうが。

2014-12-14

Microsoftがbitcoinによる支払いを受け付けるらしい

How do I use Bitcoin with my Microsoft account?

Microsoftが米国でbitcoinによる支払いを受け付けるらしい。

なんともまあ、面白い流れだ。

2014-12-12

C++1zに採択された新機能

C++1zともC++17とも呼ばれているC++の次の規格には、まだ大きな機能は採択されていない。それでも、いくつかドラフト入りしている新機能はあるので、ここではその機能を紹介していく。

いつも通り、ここに書かれている内容はまだドラフト段階の機能であり、今後変更されたり、取り除かれたりする可能性もある。

N3928: メッセージ無しstatic_assert

C++11で追加されたstatic_assertには、文法上、必ず文字列リテラルを記述しなければならなかった。

static_assert( expr, "Captain Obvious To the Rescue! expr is false.") ;

この文字列リテラルは、実装が診断メッセージ(例えばコンパイラーのエラーメッセージ)に使うことができる。しかし、C++14までは、文字列リテラルが文法上必須で、必ず記述しなければならなかった。

そこで、文字列リテラルを記述しなくても良い文法が追加された。

static_assert( expr ) ;

N4086: トライグラフの除去??!

トライグラフが文面から削除された。

N4051: テンプレートテンプレートパラメーターにtypenameキーワード

C++14まで、テンプレートテンプレートパラメーターの文法は、classキーワードしか使えなかった。

template < 
    template < typename T >
    class U // typenameキーワードは不可
> struct X ;

typenameも使えるようになる。

template < 
    template < typename T >
    typename U // typenameキーワードが使える
> struct X ;

N4295: Fold式

パラメーターパックの中身すべてに対して演算子を適用したい場合、再帰的なテンプレートを書く必要がある。例えば、引数をすべてoperator +で合計する関数テンプレートを書くと、以下のようになる。

template < typename T >
T sum( T && t )
{
    return t ;
} 

template < typename T, typename ... Types >
T sum( T && t, Types && ... args )
{
    return t + sum( std::forward<Types>( args ) ... ) ;
}

いかにも面倒だ。やりたいことは、a1 + a2 + a3 + ... + aNということなのに、この記述はあまりにも冗長すぎる。

そこで提案されているのが、fold式だ。パラメーターパックpにたいして、(p + ...)と書くと、p1 + p2 + p3 + ... pNとパック展開してくれる。

fold式を使うと、以下のように書ける。

template < typename ... Types  >
auto sum( Types && ... args )
{
    return (args + ...) ;
}

fold式では、括弧は必須である。

template < typename ... Types  >
auto sum( Types && ... args )
{
    // エラー
    return args + ... ;
}

fold式には、left foldとright foldが存在する。

(... op e)は、left foldである。(e1 op e2) op e3) op e4のように展開される。

template < typename ... Types  >
auto sum( Types && ... args )
{
    return (... + args) ;
}

int main()
{
    // ((1 + 2) + 3) + 4
    sum( 1, 2, 3, 4 ) ;
}

(e op ...)は、right foldである。e1 + (e2 + (e3 op e4))のように展開される。

template < typename ... Types  >
auto sum( Types && ... args )
{
    return ( args + ... ) ;
}

int main()
{
    // 1 + ( 2 + ( 3 + 4 ) )
    sum( 1, 2, 3, 4 ) ;
}

fold式には、単項fold(unary fold)と二項fold(binary fold)が存在する。

上記の、(... op e)と(e op ...)は、単項fold式である。

二項fold式とは、(e1 op1 ... op2 e2)のことである。op1とop2は同じfold演算子でなければならず、e1とe2のどちらか片方のみが未展開のパラメーターパックでなければならない。

e2がパラメーターパックである場合、left foldになる。e1がパラメーターパックの場合、right foldになる。

template < typename ... Types >
void sum( Types && ... args )
{
    // binary left fold
    auto a = ( 0 + ... + args ) ;
    // binary right fold
    auto b = ( args + ... + 0 ) ;

    // エラー、両方がパラメーターパック
    auto c = ( args + ... + args ) ;
    // エラー、両方が非パラメーターパック
    auto d = ( 0 + ... + 0 ) ;
}

fold式に使える演算子はfold演算子である。これは以下の通り。

    +  -  *  /  %  ^  &  |  =  <  >  <<  >>
    +=  -=  *=  /=  %=  ^=  &=  |=  <<=  >>=
    ==  !=  <=  >=  &&  ||  ,  .*  ->*

パラメーターパックが空の場合、一部のfold演算子については、以下のようにデフォルトの値が定められている。

Table N. Value of folding empty sequences
Operator Value when parameter pack is empty
* 1
+ int()
& -1
| int()
&& true
|| false
, void()
template < typename ... Empty >
void f( Empty ... e )
{// eは空のパラメーターパックとする

    auto x1 = ( e * ... ) ; // 1
    auto x2 = ( e + ... ) ; // int()
    auto x3 = ( e & ... ) ; // -1
    auto x4 = ( e | ... ) ; // int()
    auto x5 = ( e && ... ) ; // true
    auto x6 = ( e || ... ) ; // false
    auto x7 = ( e , ... ) ; // void()
}

これ以外のfold演算子で、空のパラメーターパックをfold式でパック展開しようとすると、ill-formedになる。

N4267: u8文字リテラル

charひとつで表現できるUTF-8文字リテラル。

char A = u8'A' ; // 0x41

主な使い方として、確実にASCII文字の数値をわかりやすいリテラルでソースコードに記述できる。

N4230: 名前空間のネスト

namespace A { namespace B { namespace C {
} } }

を、

namespace A::B::C {
}

のように書ける。

N4266: 名前空間と列挙子に属性

名前空間と列挙子に属性を指定することができる。C++14までは、文法上の問題で指定できなかった。

具体的には、[[deprecated]]を指定できるようになった。

namespace [[deprecated("Use new_lib")]] lib { }
namespace new_lib { }

enum struct E { value [[deprecated("Use VALUE.")]], VALUE } ;

ドワンゴ広告

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

今日は社内が盛り上がっていた。

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

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

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

2014-12-11

Tarse Range-Based forがClangから取り除かれていた

C++1zには、N3994: Tarse Range-based forが提案されていた。これは、Range-based forで型を書かずにすむようになる小粒な新機能だ。

任意のコンテナーを受け取って、その要素をすべて標準出力に出力する関数テンプレートを書くとする。これは以下のように書ける。

template < typename Container >
void print( Container const & c )
{
    for ( typename Container::value_type & elem : c )
        std::cout << elem << '\n' ;
}

いちいち型を書くのが面倒だ。これにはautoを使えばよい。

template < typename Container >
void print( Container const & c )
{
    for ( auto && elem : c )
        std::cout << elem << '\n' ;
}

しかし、auto &&すら書くのが面倒ではないか。N3994では、auto &&を省略できる新機能、Tarse Range-based forを提案していた。これはClangとGCCで実装されていたが、今試すとSNV HEADのClangでは動かない。はてどうしたことか。

調べると、C++1zには採用されない見込みになったので、取り除かれたそうだ。

[llvm-project] Revision 222865

ドワンゴ広告

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

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

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

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

2014-12-10

2014-11-post-Urbanaのレビュー: N4332-N4339

N4332: Networking Library Proposal (Revision 3)

Boost.Asioを土台にしたネットワークライブラリの提案。

N4333: Concepts Lite

Concept Lite TSのドラフト

N4334: Wording for bool_constant

std::integral_constantのエイリアステンプレート、bool_constantの提案。既存のtrue_typeとfalse_typeはbool_constantで書き直される。

template <bool B>
using bool_constant = integral_constant<bool, B>;

typedef bool_constant<true> true_type;
typedef bool_constant<false> false_type;

新しいクラステンプレートではなくエイリアステンプレートなので、integral_constantを期待している既存のコードでも互換性の問題は生じないはずだ。

N4335: C++ Extensions for Library Fundamentals, Working Draft

標準ライブラリに対する拡張TSのドラフト

機能テストマクロ、optional, any, string_view, polymorphic_allocator, memory_resource, futureの拡張、新しいアルゴリズムなど

N4336: C++ Extensions for Library Fundamentals, Version 2, Working Draft

N4335とは異なるが、これも標準ライブラリに対する拡張TSのドラフト。

not_fn、observer_ptr, container_erasure, ostream_joiner, GCDとLCMなど。

N4337: Editor's Report — Library Fundamentals TS

標準ライブラリに対する拡張TSのドラフト編集者の報告書。

N4339: Agenda and Meeting Notice for WG21 Concepts Meeting

2015年1月26日にBloombergで行われるConcept会議の日程表。

ドワンゴ広告

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

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

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

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

2014-11-post-Urbanaのレビュー: N4320-N4331

N4320: Make exception-specifications be part of the type system

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

関数ポインターを経由した例外指定の偽装がコンパイル時に検出できるようになる。

void (*p)() throw(int);
void (**pp)() throw() = &p;   // エラー

struct S { typedef void (*p)(); operator p(); };
void (*q)() noexcept = S();   // エラー

N4321: Towards Implementation and Use of memory_order_consume

memory_order_consumeの効果とLinuxカーネルでは使いにくい問題点などを解説したうえで、Linus Torvaldsでも満足する機能を設計している。

N4322: Linux-Kernel Memory Model

LinuxカーネルにおけるメモリモデルをC++の規格と比較してまとめたもの。

N4323: Out-of-Thin-Air Execution is Vacuous

複数のスレッドからのメモリアクセスによって競合を起こした場合、まったく脈絡のない値が現れることが規格上許されている。しかし、全く脈絡のない値が現れるのは問題である。この虚空から現れいでたる(Out-of-Thin-Air)値についての具体例の考察。

N4324: Use Cases for Thread-Local Storage

タイトル通り、TLSの利用例

TLS(Thread Local Storage)は長年利用されているにもかかわらず、その必要性が一般に理解されていない。C++標準化委員会の会議においてすら、TLSの存在価値に懐疑的な意見が出された。SIMDやGPGPUの専門家はTLSの存在意義に懐疑的で、逆にベクトルループ内でTLSが正しく動かないことに不満を持つものもいる。

この文書はその意見に答えて、TLSの既存の利用例や、SIMDやGPGPUにおけるTLSサポートの難しさ、TLSの代替機能などを考察している。

TLSの利用例として、例えばLinuxカーネルでは、CPUごとに必要な変数としてTLSを用いている。CPUごとに500以上もの静的なTLS変数と、100以上の動的なTLS変数が存在する。

TLSの最も主要な利用例としては、統計カウンターである。スレッドごと、CPUごとにカウンターを分割して、個別にカウンターを更新した後、最後に加算してすべてのスレッドのカウンターの値を得る。

TLSの他の利用例としては、オーバーヘッドの低いログやトレースを実装するために使われている。

TLSはメモリーアロケーターのスレッドごとのキャッシュにも使われている。

等など

なぜTLSはSIMDやGPGPUでは問題になるのか。TLSを初期化、破棄するにはコストがかかる。特にTLS変数がコンストラクターやデストラクターを持つ場合は、ミリ秒単位の時間が使われるかもしれない。これは、ひとつの実行単位がマイクロ秒単位のSIMDにとっては、無駄が多すぎる。GPGPUの実行単位はもう少し長いが、それでも問題だ。GPGPUの場合は、実行媒体が非常に多いので、GPGPUスレッドごとにTLS変数を作るのは、メモリーフットプリント上好ましくない。

TLSとSIMD/GPGPUは共存できるのか。SIMDレーンごとにTLS変数を初期化破棄するのはいかにも無茶だ。しかし、SIMDベクトルループ内でTLS変数の利用を禁止すると、従来のライブラリ、とくにerrnoに依存するようなライブラリが軒並み使えなくなってしまう。

N4325: C++ Standard Evolution Active Issues List
N4325: C++ Standard Evolution Completed Issues List
N4337: C++ Standard Evolution Closed Issues List

C++に提案中の新機能に対する議論中の問題、解決済みの問題、却下された問題の一覧。

N4328: C++ Standard Library Issues History for C++14

標準ライブラリのC++14に対する更新履歴の一覧。なかなか新しい文書だ。さすがに更新履歴が長くなってきたから分離したのだろうか。

N4329: C++ Standard Library Active Issues List
C++ Standard Library Defect Report List
N4331C++ Standard Library Closed Issues List

標準ライブラリで議論されている問題、解決済みの問題、却下された問題の一覧。

ドワンゴ広告

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

ドワンゴにはボルダリング部があり、毎週水曜日に部員は仕事後にボルダリングに行っている。

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

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

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

2014-12-09

2014-11-post-Urbanaのレビュー: N4310-N4319

N4310: Technical Specification for C++ Extensions for Parallelism, Working Draft

Parallelism TSのドラフト

[PDf] N4311: Parallelism TS Editor's Report

Parallelism TSの編集者の報告書

[PDF] N4312: Programming Languages -- Technical Specification for C++ Extensions for Parallelism

N4311と内容は同じ。

N4313: Improvements to the Concurrency Technical Specification, revision 1

<future>にwhen_all, when_any_result, when_any, make_ready_future, make_exceptional_futureを追加する提案。

N4314: Data-Invariant Functions (revision 2)

入力の値におって、処理時間が異なるなどの外部から計測可能な挙動の違いが存在する場合、外部からその違いを計測することによって、秘密の情報が漏れてしまうことがある。暗号処理の実装において、サイドチャネル攻撃として知られている問題だ。

最近のOpenSSLのパッチは、そのような問題に対処するために入力によって処理速度が変化しないようにプラットフォーム依存の方法で対処している。

そのような仕組みは標準に存在するべきである。この論文では、入力の値によって外部から計測可能な挙動の変化が生じないような基本的な演算を提供するライブラリ、std::constant_time::value<T>を提供している。

// 入力の値によって処理時間やメモリアクセスパターンに差がでないことが保証されている
auto equal( std::constant_time::value<int> a, std::constant_time::value<int> b )
{
    return a == b ;
}

N4315: make_array, revision 3

std::arrayを作るstd::make_arrayの提案。

// 面倒
std::array<int, 5> a1 = { 1, 2, 3, 4, 5 } ;
// 簡単
auto a2 = std::make_array( 1, 2, 3, 4, 5 ) ;

// 面倒
atd::array<char, 4> a3 = { 'a', 'b', 'c', '\0' } ;
// 簡単
auto a4 = std::to_array("abc") ;

// std::array< char const *, 1 >
auto a5 = std::make_array("abc") ;

これは自分で実装してみるとVariadic Templatesの練習になって面白い。

リファレンス実装が公開されている。

N4316: std::rand replacement, revision 2

std::randの代替品、std::randintの提案

前回の変更点は、seed関数がseed_initからreseedに解明されたことだ。

[PDF] N4317: New Safer Functions to Advance Iterators

イテレーターを進めるadvanceとnextとprevに終端をチェックする安全版を追加する提案

例えば、イテレーターを三つ進めるごとに処理をしたいとする。以下のように書くと問題がある。


for ( auto i = begin(con), e = end(con) ; i != e ; std::advance( i, 3 ) ) 
    process( *i ) ;

このループは、イテレーターを三回進めるごとに終端のチェックを行っている。イテレーターの指す要素の数によっては、終端を飛び越してしまう可能性がある。

終端をチェックしつつイテレーターを進めるadvance, next, prevを追加しようという提案。この提案を使えば、以下のように書ける。


for ( auto i = begin(con), e = end(con) ; i!= e ; std::advance( i, e, 3 ) )
    process( *i ) ;

advance, next, prevに、終端のイテレーターを取るオーバーロードが追加される。

[PDF] N4318: Proposal to add an absolute difference function to the C++ Standard Library

絶対値の差を計算するabs_diff(a, b)の提案。

operator <とoperator -をサポートする型のオブジェクトの絶対値の差を計算してくれる。

int main()
{
    // 2
    std::cout << abs_diff( 3, 5 ) << '\n' ;
}

従来のabsでこれをやろうとすると、abs(a - b)のようになる。しかし、もし型が符号なし整数型で、bの方が大きい場合、悲惨なことになる。


int main()
{
    std::cout << abs( 3 - 5 ) << '\0' ;
}

結果は好ましい物にならないだろう。

[PDf] N4319: Contracts for C++: What are the Choices

会議の結果、contract programming(契約プログラミング)をコア言語で直接サポートすべきという意見に固まったが、contractに対するそれぞれの意見や経験がバラバラである。Microsoftの既存の経験から、contractの土台を解説している。

なぜcontractが必要なのか。

  • 実行時チェックによって予期しないプログラムの挙動を早期に発見できる
  • テストのサポート
  • 関数とデータ構造のドキュメント
  • 静的に証明可能な契約による最適化
  • 静的解析によるコンパイル時にバグの発見

contract systemに求めるもの

  1. あるコードに対する要求(required)と保証(ensured)を直接表現できること。たとえば、関数を呼び出す前と呼び出した後の条件や、通常時と異常時の挙動など。
  2. アノテーションによるオーバーヘッドを最小化する。contractは既存の機能と強調して働き、将来の発展を妨げるものであってはならない。
  3. 静的と動的な解析ツールや、コンパイラーの最適化の役に立つ
  4. あまりに冗長な文法ではないこと

論文著者は、contractが受け入れられるには、軽量な文法であることが必要であるとしている。

論文は、既存のマイクロソフト製品におけるContract機能を紹介している。

.NETのCodeContracts

public void TurnReactorOn()
{
    Contract.Requires(this.state == State.Off);
    Contract.Ensures(this.state == State.On);
    ...
}

このコードは、関数が呼び出される前はreactorが動いていないことを要求し、関数が呼び出された後はreactorが動いていることを保証するものである。これはライブラリで実装されている。実行時のチェックと、静的解析ツールによるチェックが行われる。

SAL

SALはプリプロセッサーマクロを多用した契約の記述だ。

void * memcpy(
_Out_writes_bytes_all_(count) void *dest,
_In_reads_bytes_(count) const void *src,
size_t count
);

この宣言は、destに書き込まれるバイト数とsrcから読み込まれるバイト数のアノテーションだ。MSはSALにより数多くのバグを発見して修正できているとしている。

System C#

System C#はC#の拡張で、言語機能としてcontractをサポートしている。

public string Format(int x)
requires x >= 0
ensures return != null
{
...
}

このコードは、関数が呼び出される前に、xが0ではないことを要求し、関数の戻り値がnullではないことを保証している。

ドワンゴ広告

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

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

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

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

2014-11-post-Urbanaのレビュー: N4301-N4309

[PDF] N4301: Working Draft, Technical Specification for C++ Extensions for Transactional Memory

トランザクショナルメモリーのTSドラフト

[PDf] N4302: Technical Specification for C++ Extensions for Technical Specification for C++ Extensions for Transactional Memory

N4301とほぼ同一。

N4303: Pointer safety and placement new

以下のコードは、C++14で挙動が定義された。

#include <new>
#include <cassert>

struct A { char buf[1]; };
struct B { char buf[1]; };

int main(void) {
    A a{ { 0 } };
    B *bp = static_cast<B *>(static_cast<void *>(&a));
    new (bp) B{ { 1 } };
    assert(bp->buf[0] == 1);
}

C++14では、static_castはaの先頭アドレスを指すことが明記されたので、上のコードは動作する。

ただし、これによって、コンパイラーのポインターの値によるエイリアシングの判定に誤りが生じる。そこで、この論文では、launderという関数を追加している。この関数にポインターの値を通すことによって、コンパイラーのアドレス値の解析を阻止するのだとか。

template <typename T> constexpr T* launder(T* p) noexcept;
Required behavior: An invocation of this function may be used in core constant expressions whenever the value of its argument may be used in a core constant expression.

Requires: p represents the address A of a byte in memory and an object Obj whose cv-unqualified type is remove_cv_t<T> is located at the address A.

Returns: A value of type T * which points to Obj.

そういう明示的なものが使われるとは思えない。

N4304: C++ Standard Core Language Active Issues
N4305: C++ Standard Core Language Defect Reports and Accepted Issues
N4306: C++ Standard Core Language Closed Issues

コア言語で現在議論中の問題、解決済みの問題、却下された問題の一覧。

[PDF] N4307: National Body Comment -- ISO/IEC PDTS 19568 -- Technical Specification: C++ Extensions for Library Fundamentals

Library Fundamentals TSに対するNBコメント集。日本からはmake_applyの追加というコメントが出ている。

[PDF] N4308: National Body Comment -- ISO/IEC PDTS 19570 -- Technical Specification: C++ Extensions for Parallelism

Parallelism TSに対するNBコメント集。

Return type deduction for explicitly-defaulted and deleted special member functions

特別なメンバー関数をdefault化、delete定義するときに、戻り値の型推定を許す提案。

以下のコードが、

struct X
{
    X & operator = ( X const & ) = default ;
    X & operator = ( X && ) = delete ;
}

以下のように書ける。

struct X
{
    auto operator = ( X const & ) = default ;
    auto operator = ( X && ) = delete ;
}

うーん・・・・、なんとも言いがたい。あって邪魔にはならない機能だが。

ドワンゴ広告

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

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

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

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