本の虫

著者:江添亮
ブログ: http://cpplover.blogspot.jp/
メール: boostcpp@gmail.com
Twitter: https://twitter.com/EzoeRyou
GitHub: https://github.com/EzoeRyou

アマゾンの江添のほしい物リストを著者に送るとブログ記事のネタになる

筆者にブログのネタになる品物を直接送りたい場合、住所をメールで質問してください。

C++標準化委員会の文書: P0270R0-P0279R0

P0270R0: P0270R0: Removing C dependencies from signal handler wording

シグナルハンドラーの文面を書き直す提案。

現在のC++規格におけるシグナルハンドラーは、C規格との共通項のみ使えると定義している。

しかし、現在のC++規格が参照しているC規格はC11である。ということは、thread_localが共通項になるが、thread_localは使えるべきではない。

C言事の共通項というと、Cリンケージを指定するextern "C"も使えないということになるが、それではシグナルハンドラー自体が宣言できない。

nullptrを使えない技術的な理由は何もない。

など、様々な問題があるので、この機会に文面を新たに書きなおす提案。

一般に、シグナルハンドラーのなかでは、ロックを発生させない操作のみ許されるべきだとしていて、POF(Plain-Old-Function)や、 signal-safe functionという用語を定義している。

現実的には、シグナルハンドラーの挙動は何も変わらない。既存のコードは動くのもの動かないものも含めてそのままだ。

この文面の変更により、例えばlambda式がシグナルハンドラーの中で使えるようになった。というのも、lambda式の実装にはメモリの動的確保が必要ないから、禁止する理由がないためだ。

P0271R0:

T型を特定の型リストで構築した時に、実際に呼ばれるコンストラクターの実引数の型を知るために、実際に呼ばれるコンストラクターの型を返すtype traits、std::direct_init<T>の提案。

std::variantで例外を投げる変換関数を持った型が渡された場合でも例外安全にvariantを実装するために必要。

P0272R1:Give 'std::string' a non-const '.data()' member function

あまりにもひどいクソ提案なので解説する価値なし。CreateProcessの第二引数は書き換えを行うし第一引数にNULLを渡すべきではないしCreateProcessAを使うべきでもない。

[PDF] P0273R0: Proposed modules changes from implementation and deployment experience

Clangでモジュールを実装して使用した経験から、モジュール規格の改良提案。

モジュール宣言はソースファイルのどこにでも書いていいことになっていたが、その仕様だと、ツールはソースファイル全体をスキャンしてモジュール宣言があるかどうか確認しなければならない。モジュール宣言はソースファイルの最初の宣言に書く制約をつける。

異なるモジュールで名前が衝突した場合、異なるエンティティが与えられるが、名前の衝突に対する対策としては名前空間がすでにある。モジュールでも名前衝突を回避する機能を別に提供すると、名前空間が使われなくなる。名前の衝突を回避する方法としては名前空間のみが提供すべきだ。モジュールの外にでるエンティティは名前が衝突した場合エラー。モジュールの外に出ないエンティティは別々になるようにする。

モジュールの循環参照は禁止されているし、その理由は妥当なものだが、それだと全モジュールに同じ宣言を書かなければならず、結局一つのテキストファイルに書いて#includeする方法から逃れることができない。これではモジュールの意味がない。

これを解決するために、モジュールパーティションという機能を提供する。

[PDF] P0274R0:Clump - A Vector-like Sequence Container with Embedded Storage

組み込みのストレージをオブジェクトに持った連続したメモリー上に確保されるコンテナー、clumpの提案。

clumpはvectorに似ているが、stringのsmall string optimizationに似た実装をしている。Boostのsmall_vectorと発想は同じだ。

例えば、以下のようなレイアウトで実装される。

template < typename T, std::size_t N, typename Allocator = std::allocator >
class clump
{
    char buf[ sizeof(t) * N ] ;
    T * ptr ;
} ;

要素数がN以下の場合はオブジェクト自体に埋め込みのストレージを使い、Nより多い要素数を扱うときには、アロケーターによるストレージの動的確保を行う。

vectorとは互換性がないので、置き換えて使うこともできない。例えば、insertはpair<iterator, bool>を返す。boolは、組み込みのストレージに収まったかどうかを返す。

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

Boost.DLLに似たshared library(DLLや.so)を扱うためのライブラリの提案。純粋にライブラリだけの提案となっている。

std::shared_libraryは、ファイルパスを指定してshared libraryを読み込み、シンボル名が存在するか調べたり、型とシンボル名をしていしてshared library内の参照を得たりできる。

int main()
{
    std::shared_library mylib(std::filesystem::path{"mylib.so"}) ;

    if ( mylib.has("myfunc") )
    {
        auto && myfunc = mylib.get< void(void) >( "myfunc" ) ;
        myfunc() ;
    }

}

shared libraryの遅延読み込みをしたりすることもできる。また、デフォルトではシステムディレクトリーは探さず、オプションを指定する必要がある設計になっている。この理由としてシステムディレクトリを探すのは遅く、ユーザーはたいてい読み込むファイルの場所をわかっているからとしているが、理解できない。

提案ではDLL(Dynamic Load Library)という用語を使っていて、ライブラリにもdllという名称が出てくるが、インターフェース的にはdlopenのラッパーのように感じる。

例えばロード時のオプションのscoped enumは以下の通り。

    // shared library file load modes
    enum class dll_mode {
        default_mode = 0,
        dont_resolve_dll_references,    // DONT_RESOLVE_DLL_REFERENCES
        load_ignore_code_authz_level,   // LOAD_IGNORE_CODE_AUTHZ_LEVEL
        rtld_lazy,                      // RTLD_LAZY
        rtld_now,                       // RTLD_NOW
        rtld_global,                    // RTLD_GLOBAL
        rtld_local,                     // RTLD_LOCAL
        rtld_deepbind,                  // RTLD_DEEPBIND
        append_decorations,             // See [dll.dll_mode]
        search_system_directories       // See [dll.dll_mode]
    };

DONT_RESOLVE_DLL_REFERENCESとLOAD_IGNORE_CODE_AUTHZ_LEVELについてはWin32 APIのLoadLibraryEx由来、RTLDについては、dlopen由来のオプションだ。

append_decorationsというのは、ロードするファイルパスに、実装のデフォルトのプレフィクスやポストフィクスを付けてロードするオプションだ。例えば、"foobar"という共有ライブラリをロードしようとすると、Windowsならば"foobar.dll"を、GNU/Linuxならば"foobar.so"を読み込もうとする。ロードが失敗した場合、元の文字列でロードを試みる。

search_system_directoriesは、システムディレクトリからもファイルを探すオプションだ。

果たして入るだろうか。

P0276R0: A Proposal to add Attribute [[visible]]

共有ライブラリでシンボル名としてexportしたいエンティティに指定できる[[visible]]の追加

現状では、MSVCはエスクポートするシンボル名に__declspec(dllexport)を書かなければならない。GCCでは__attribute__((visibility("default"))を書かなければならない。また、シンボル名を使う際には、MSVCでは__declspec(dllimport)を書かなければならない。GCCでは書く必要がない。

この結果、以下のような汚らしいプリプロセッサーマクロが書かれることになる。

#if EXPORTING
    #   if MSVC
    #       define API __declspec(dllexport)
    #   else
    #       define API __attribute__((visibility("default")))
    #   endif
    #else
    #   if MSVC
    #       define API __declspec(dllimport)
    #   else
    #       define API
    #   endif
    #endif

この提案は既存の拡張機能を追認するものだ。

[PDF] P0277R1: const Inheritance

const派生の追加提案。const派生すると、基本クラスを改変できなくなる。派生クラスは、基本クラスのコンストラクターは呼べるが、非staticデータメンバーは変更できず、const修飾された非staticメンバー関数しか呼べなくなる。

P0278R0: P0278r0 - volatile solutions

volatileの定義を見直す提案。以下の変更をする。

  1. 未初期化のvolatileオブジェクトからの読み込みは未定義
  2. 実装はvolatileオブジェクトの値を推定してはいけない
  3. const volatileオブジェクトには初期化が必要ない

volatileの利用例として、memory mapped I/Oに使うというものがある。このとき、特定のメモリアドレスに対する読み書きアクセスは、必ず副作用が発生してほしい。

int volatile * p = ... ;

*p = 42 ;
*p = 42 ;
int x = *p ;

例えば、上のコードについて考える。同じアドレスに連続して2回同じ値を書き込んでいる。通常ならば、実装は最適化の際に、書き込みは一回で十分だと考えるかもしれない。書き込んだあとにすぐ読みだしている。実装は最適化の際に、xの値をコンパイル時に決定できる。したがって、実装は最適化の結果、以下のようなコードに変換するかもしれない。

*p = 42 ;
int x = 42 ;

volatileでは、読み書きに伴う副作用を無視しないという意味を持つ。したがって、このような最適化を禁止する。

ということを規定する。

P0279R0: Read-Copy Update (RCU) for C++

RCUライブラリの追加提案。

RCUは、ロックフリーなreader-writerを実現する。

ドワンゴ広告

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

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

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