2014-12-08

2014-11-post-Urbanaのレビュー: N4293-N4298

N4293: C++ language support for contract programming

contractを言語でサポートする提案で、Urbanaの会議で議論した方針の結果をまとめたもの。

contractとは、処理を実行する上での前提条件(古典的にはassertマクロなどでチェックされていたもの)、処理を実行したあとの状態の保証、ある型の値は有効な状態になっていることだ。

これらのcontractは、既存のコードでは、assertマクロや、戻り値や例外などのエラー処理で処理されたりするものもあれば、単に未定義の状態になるとして済ませているものもある。

現在議論中のcontractは、コア言語によるサポートを必要とするものだ。ライブラリによる実装では、関数の本体の中に隠れてしまい、コンパイラーやツールからは使いにくくなってしまう。

ビルドモード(デバッグビルドやリリースビルドなど)を規格に含めるかどいうかという議論では、この提案では含めないということになった。

論文で提示されている文法は、提案する機能を示す目安であって、具体的な提案ではないが、例えば以下のような機能が欲しいとしている。

double square_root( double x )
expects( x >= 0.0 )
ensures( square_root >= 0.0 )

この例では、square_rootという関数のdouble型の実引数xは、必ず0.0以上であることを前提条件としていて、この関数の戻り値は必ず0.0以上であることを保証している。ensuresの中で関数名を使うと、関数の戻り値の意味になる。

これはあくまでこのような機能が欲しいという例示のための文法だ。

N4294: Arrays of run-time bounds as data members

実行時にサイズの決まる配列をクラスのデータメンバーとして持つことができる機能の問題点の洗い出しと解決案。

自動ストレージ、つまりスタックから実行時に決定されるサイズのストレージを確保したいという需要はあるが、C++風に型システムの上にサポートするには色々と問題があり、C++14では頓挫した。

既存の方法としては、alloca()とかC99のVariable Length Array、Flexible Array Memberなどがある。

int good_old_C()
{
    // スタックから確保
    // 低級すぎる
    void * p = alloca(100) ;
}

// C99
void vla( std::size_t size )
{
    // Variable Length Array
    // クラスによるラップができない
    char buf[size] ;
}

// C99
// Flexible Array Member
struct FAM
{
    std::size_t size ;
    char buf[] ;
} ;

void fam( std::size_t size )
{
    FAM * ptr =(FAM *) std::malloc( size ) ;
    ptr->size = size ;
}

C++でも実行時サイズ配列をサポートしたいが、型システムの中でやりたい。

前回の提案は、クラスの最後のデータメンバーが実行時サイズ配列で、クラスの最初のデータメンバーがconstな整数型でなければならず、最初のデータメンバーが実行時サイズ配列の要素数の指定に使われ、最初のデータメンバーの初期化が特別に扱われるというものだった。

// 前回の提案
struct X
{
    std::size_t size ;
    char buf[] ;

    X( std::size_t size ) : size(size), buf[size] { }
} ;

sizeの初期化は、通常のメンバーの初期化とは別に扱われる。

今回の提案では、配列コンストラクターという文法を提案している。

// 実行時サイズオブジェクト
struct X
{
    // 配列コンストラクター
    X[]() : buf[10] { }
    X[]( std::size_t size ) : buf[size] { }

    char buf[] ; // 実行時サイズ配列
} ;

配列コンストラクターはinlineでなければならない。

配列コンストラクターから通常のコンストラクターに転送することもできる。これはC++11の機能だ。

struct X
{
    // 配列コンストラクター
    X[]() : buf[10], X{} { }
    X[]( std::size_t size ) : buf[size], X{size1} { }

    // 通常のコンストラクター
    X() { }
    X( std::size_T size) { }

    char buf[] ; // 実行時サイズ配列
} ;

転送せずに、配列コンストラクターだけですべてを済ませることもできる。ただし、配列コンストラクターはinlineでなければならない。

今回の提案では、実行時サイズ型と実行時サイズオブジェクトに対してsizeofは一切サポートされない。

実行時サイズ配列の提案は文法や機能の設計がめまぐるしく変わっている。これもまだ決定ではないので、まだ変わるだろう。

sizeofがサポートされないのであれば、ある型が実行時サイズ型であるかどうかを調べるtraitsが欲しいところだ。

N4295: Fold expressions

Folding Expressionの文面案。

Folding Expressionとは、パラメーターパックpに対して、p + ...と書くと、p1 + (p2 + (p3 + p4))のように展開してくれる式のことだ。

例えば、実引数をすべて足し合わせる関数テンプレートsumを書きたいとする。従来ならば、以下のように書ける。

// 従来のパック展開による実装
template < typename T >
auto sum( T && t )
{
    return std::forward<T>(t) ;
}

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

見ての通り、極めて冗長だ。Folding Expressionを使えば、以下のように簡潔に書ける。

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

なお、ClangのSVNにすでに実装されているようだ。

Clang - C++1z, C++14, C++11 and C++98 Status

これはかなり確実に入りそうだ。

N4296: Working Draft, Standard for Programming Language C++

現在の最新のC++標準規格のドラフト

N4297: Editor's Report -- Working Draft, Standard for Programming Language C++

ドラフト編集者の編集報告書。

N4298: Agenda and Meeting Notice for WG21 Ballot Resolution Telecon Meeting

2014年12月5日に行われた電話会議の予定表。

ドワンゴ広告

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

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

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

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

No comments: