2017-11-22

C++標準化委員会の文書: P0770R0-P0779R0

P0770R0:A Proposal to Specify Behavior in Case of Exception Allocation Failure

例外オブジェクトのストレージは未規定の方法で確保されると規格にある。動的確保する実装の場合、確保に失敗した場合に対処する方法がない。

そこで、例外処理が失敗した場合に絶対に失敗せずに投げられる何らかの例外型を追加する提案。bad_allocを再利用するのがよいとしている。

[PDF] P0771R0: std::function move operations should be noexcept

std::functionのムーブ処理をnoexceptにする提案。すでにanyやshared_ptrはnoexceptなのでstd::functionをnoexceptにしない理由はない。

[PDF] P0772R0: Execution-Agent Local Storage

スレッドより軽い実行媒体がTLSを使うとスレッドのTLSが使われてしまう。ExecutorにTLSを要求するAPIを用意して、ExecutorがTLSを提供するか拒否するかして、スレッドより下の軽い実行媒体にもTLSを提供しようという提案。

P0773R0: Towards meaningful fancy pointers

ポインター風に振る舞うクラス型fancy pointerについての考察。ポインターにメタ情報を付加したり、アドレスにnear/far/segmentといった概念があったりする場合に必要。

[PDF] P0774R0: Module Declaration Location

module宣言の文法を変える提案。相変わらず文法が安定しない。

[PDF] P0775R0: Module Partitions

一つのソースファイルに複数のモジュールを書くことができるようにする提案。

P0776R0: Rebase the Parallelism TS onto the C++17 Standard

Parallerism TSはC++14の文面に合わせて書かれていたので、C++17の文面に合わせて書き直す提案。

[PDF] P0777R0: Treating Unnecessary decay

標準ライブラリでdecayを使っているがremove_cvrefですむ場所でremove_cvrefを使うよう書き換える提案。

[PDF] P0778R0: Module Names

モジュールが未だにこんな問題を抱えてるとは、C++20に入るのはおぼつかないのではないだろうか。

モジュールでimport foo ;と書くと、「これはモジュール名fooをimportしろ」という意味だ。ではモジュール名fooに対応するモジュールが定義されているソースファイルは何だろうか。規格は何も定めていない。

ヘッダーファイルでは、ヘッダー名は実装が実ファイルへのマッピングをするヘッダーか、直接物理のファイル名を意味している。ヘッダーを実装しているC++コンパイラーは存在せず、既存のすべてのコンパイラーはヘッダー名をファイル名として解釈し、所定のincludeディレクトリー群からヘッダー名のファイル名を探し出す。

モジュールの実装では、モジュール名、モジュールソースファイル、モジュールバイナリファイルが存在するはずだ。モジュールバイナリファイルは規格の規定するところではないが、モジュールソースファイルを処理した結果の何らかのファイルだ。モジュールの目的がコンパイルの高速化にあるので、当然このような実装になるはずだ。

すると、モジュール名fooをimportしたとき、C++コンパイラーはモジュール名fooに相当するモジュールバイナリファイルを探し出して使う。もしモジュールバイナリファイルがない場合、モジュールソースファイルを探してモジュールバイナリファイルを生成する。要するに今のビルドシステムがやっている依存関係の解決をC++コンパイラーが内部で直接やるようになる。ここでもやはり、モジュール名とモジュールソースファイルのマッピングが必要になる。

このモジュール名とファイル名のマッピングは、現状のままでは実装ごとに異なることになり、ポータビリティのかけらも存在しない使いづらいものになる。

モジュール機能を提供している他のプログラミング言語の例を列挙して比較してみるが、そもそも多くのプログラミング言語は単一の実行環境、単一の実装といったC++とは異なる状況にあるので参考にできないものも多い。

この文書では、モジュール名の文法を識別子から文字列リテラルにしてファイル名とマッピングするルールを追加するように提案している。

[PDF] P0779R0: Proposing operator try() (with added native C++ macro functions!)

なかなか野心的な提案。

expected<T,E>を返す関数が内部でexpected<U,E>を返す関数を呼び、エラーを返した場合はそのエラーを含むoptionalを伝播して返す処理を考える。

template < typename T >
using expected = std::expected<T, std::error_code > ;

expected<int> get_int() noexcept ;

expected<float> get_float() noexcept
{
    auto r = get_int() ;

    // 結果がエラーならば伝播する
    if ( !r )
        return unexpected( r.error() ) ;

    // floatがintを完全に表現できなければエラーを返す
    auto f = float( *r ) ;

    if ( int(f) != *r )
        return unexpected( std::errc::result_out_of_range ) ;
    else
        return f ;
}

このとき、エラーを伝播させるのがとてもだるい。いちいちエラーかどうか調べてエラーならエラーを返す処理を書かなければならない。

このボイラープレートコードを回避するためにCプリプロセッサーマクロを書くのも醜悪だ。

このコードは、コルーチン提案をまねてoperator tryをオーバーロードできるようにすれば解決できる。

template <class T, class E>
constexpr auto operator try(std::expected<T, E> v) noexcept
{
    struct tryer
    {
        std::expected<T,E> v ;

        constexpr bool try_return_immediately() const noexcept { return !v.has_value() ; }
        constexpr bool try_return_value() { return std::move(v).error() ; }
        constexpr try_value() { return std::move(v).value() ; }
    } ;
    return tryer{ std::move(v) } ;
}

これさえあれば、

auto r = get_int() ;
if ( ! r )
    return unexpected( r.error() ) ;
auto i = int(*r) ;

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

int i = try get_int() ;

しかし、operator tryも冗長なコードを大量に書かなければならない。そもそもコルーチン提案自体が冗長なコードを多数書かなければならない。この問題は、ネイティブ言語マクロを導入すれば解決できる。

結局問題は、関数の呼び出し元の文脈でreturnしたいので、Cぷりプロセッサーに変わるネイティブなマクロがあれば、この問題は解決できるし、コルーチンが常用な問題も解決できるし、range-based forも実装できるし、割とあらゆるものがマクロで実装できることになる。

興味深いのはその提案している文法で、文字#を使っている。#はCプリプロセッサーにかけたあとのC++のソースファイルに残らない文字で、既存の実装はプリプロセス後に文字#があるとエラーを吐く。なので安全に機能拡張に使うことができる。

夢が広がる話だ。

ドワンゴ広告

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

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

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

No comments: