2014-10-pre-Urbanaのレビュー: N4142-N4149
ISO/IEC JTC1/SC22/WG21 - Papers 2014 mailing2014-10
C++標準化委員会の残りの論文はあと100本ほど。年々、発行される文書の数が増えてきている気がする。
[そろそろネタが尽きたがPDF] N4142: Atomic Operations on a Very Large Array
atomic_array<T>の提案
HPC(High Performance Computing)用途で、長大な配列をアトミックに操作したい場合に使える。
atomic_arrayは、すでに確保された配列のアドレスで初期化される。atomic_arrayのオブジェクトを経由して配列にアクセスすれば、その配列は何らかの方法でアトミックに操作できる。atomic_arrayオブジェクトが生存している間は、atomic_array以外の方法で元の配列にアクセスした場合の挙動は未定義。atomic_arrayオブジェクトが破棄された後は、通常通りアクセスできる。
論文にかかれている利用例としては。
- 長大な配列をnewやmallocなどの何らかの方法で確保する
- シリアル、あるいはパラレルだが競合しない方法で、配列の要素の値を初期化する
- 配列をatomic_array<T>オブジェクトでラップする
- atomic_array<T>のインターフェースを経由して、パラレルアルゴリズムで配列を書き変える
- atomic_array<T>オブジェクトを破棄する
- 競合しない方法で配列にアクセスする
atomic_arrayは既存の配列をラップしてアトミックな操作を提供するライブラリで、配列を所有しない。
N4143: Executors and schedulers, revision 4
executorとschedulerに基づく並列実行ライブラリの提案。
リファレンス実装が二つ示されている。
ひとつは、最小限の実装
https://github.com/ccmysen/executors_r4
もうひとつは、タイマーなどの追加的な機能も含む実装
https://github.com/arturl/executors
見方によっては、std::asnyncの高機能版とも言える。
N4144: Searching and Manipulation of Parameter Packs
パラメーターパックを扱うためのライブラリの提案。
パラメーターパックを格納するpacker
using type = std::packer<char, short, int, long> ;
あるpackerに指定した型が含まれるかどうか調べるis_contained_in/is_contains_type
template < typaname ... Types >
void f( Types ... args )
{
// Typesにint型が含まれているかどうか
if ( std::is_contained_in_v< int, std::packer< Types ... > > )
/* ... */ ;
// Types にint型とdouble型が含まれているかどうか
if ( std::is_contains_types_v< std::packer< Types ... > , std::packer< int, double > > )
/* ... */ ;
}
packerの最後に型を付け加えるadd_to、packerに指定した型が含まれていない場合に型を付け加えるunique_add_to
template < typename ... Types >
void f( Types ... args )
{
// std::packer< types ..., int >
using append_int = std::add_to_t< int, std::packer< Types ... > > ;
// Typesにint型が含まれていない場合のみ追加する
using append_if_unique = std::unique_add_to_t< int, std::packer< Types ... > > ;
}
型をとりのぞくremove_from、重複した型を取り除くuniqueify
using type = std::packer< char, short, int, short, long > ;
// std::packer< char, int, long >
using removed = std::remove_from_t< short, type > ;
// shortがひとつしか入っていないpacker
// 結果の型の並びは未規定
using unique = std::uniqueify_t< short, type > ;
自前で書くのは面倒なテンプレートメタプログラミングなので、重宝しそうだ。
N4145: Data-Invariant Functions
暗号化処理を実装するにあたって、side-channel attackを防ぐという極めて難しい問題がある。これは、物理的に観測可能実行特性が、秘密のデータによって変わることを利用した攻撃である。たとえば、秘密のデータの値によって、処理時間や電力消費量や、CPUの電圧レギュレーターのノイズなどに変化があり、それを攻撃者が観測できる場合、それらの観測可能な物理的特性によって、秘密のデータを推測することができてしまう。なぜならば、その観測可能な物理的特性の変化は、秘密のデータの値の変化によりもたらされるものだからだ。
opensslの最近のパッチは、一部のif文を、結果の真偽によってタイミングに変化をもたらさないように置き換えるものである。これは将来のコンパイラーの最適化の進化などで無効化されてしまう可能性がある。
この論文では、物理的な実行特性が、入力の値によって変化しないことを保証するライブラリを提案している。このライブラリは、std::constant_time::value<T>の形で使い、比較演算子がオーバーロードされている。
これはなかなか興味深いライブラリの提案だ。
そういえば、最近Hacker Newsでこういう話題が出ていた。
Making sure crypto remains insecure [pdf] | Hacker News
諜報機関が意図的に弱い暗号を使わせるために、TLSやAESのようなサイドチャネル攻撃に弱い暗号を標準化するよう働きかけようという思考実験的なプレゼン資料だ。
N4146: Disposition of Comments, ISO/IEC DIS 14882 C++ 2014
C++14 DISに対する日本からのコメントに対する対応。文面上の細かな指摘が多い。
N4147: Inline variables, or encapsulated expressions
インライン変数の提案。
この論文で提案しているインライン変数とは、変数を表現するオブジェクトを持たず、変数を使った時点で、毎回初期化子が評価される変数である。
int f()
{
static int count ;
return ++count ;
}
inline int counter = f() ;
int main()
{
counter ; // 1
counter ; // 2
counter ; // 3
}
つまり、inline変数であるcounterという変数名を使うということは、毎回初期化子である"f()"を評価することと同じ意味になる。もちろん、初期化子を評価した副作用も起こる。また、グローバル変数counterを保持するint型のオブジェクトは存在しないので、メモリを消費しない。
論文では、inline変数によって解決できる様々な問題を列挙している。文法やスコープに従う礼儀正しいマクロや、翻訳単位ごとに定義を書きたいがODR違反は回避したい場合、関数を書くのがあまりにも冗長で面倒な式を複数回使いたい場合などを挙げている。
また、この提案に対する対案として、Expression Aliasなる案も出されたそうだ。これは、例えば、
using name = expression ;
などという文法で、nameを使うと使った場所でexpressionが評価されるというものだ。
それにしても、Future Workで、「ファンクターオブジェクトを使わざる第一級市民たる遅延評価がstd-proposals MLに浮上した。これは関数の引数をinlineと宣言する。評価の意味的には似ている。inline関数には役立つだろう。そういう変数をABI定義されたインターフェースに渡す際には、ポリモーフィックファンクターに変換される。まだ利点は不明なものの、[&]()と書かずに済む」と書かれているのは興味深い。
N4148: Disallowing Inaccessible Operators From Trivially Copyable
コピー/ムーブコンストラクター、コピー/ムーブ代入演算子、デストラクターがdeletedかアクセス不可の場合であっても、torivially copyableの条件に合致してしまう。
// コピーもムーブもしてほしくないクラス
struct X
{
X( X const & ) = delete ;
X( X && ) = delete ;
void operator = ( X const & ) = delete ;
void operator = ( X && ) = delete ;
} ;
int main()
{
std::is_trivially_copyable_v< X > ; // true
}
これは、トリビアルにコピー可能なことをコンパイル時に判断して、std::memcpyなどの高速な方法に切り替えるメタプログラミングに置いて問題になる。そのため、コピー/ムーブコンストラクター、コピー/ムーブ代入演算子、デストラクターがdeletedかアクセス不可の場合、trivially copyableの条件を満たさないように、trivially copyableの定義を書き変える提案。
N4149: Categorically qualified classes
クラスが一時オブジェクトとなることを禁止できる機能と、永続的なストレージ上に確保されることを禁止できる機能の追加。
// 一時オブジェクトを禁止
struct X & { } ;
X f() ;
// 永続ストレージ上への確保を禁止
struct Y && { } ;
Y g() ;
int main()
{
X x ; // OK
f() ; // エラー
X x = f() ; // OK、初期化子の中ではよい
Y y ; // エラー
g() ; // OK
}
一時オブジェクト禁止はどう使うかというと、std::asyncのように関数の戻り値を無視してほしくない場合や、scoped_guardのようなRAIIラッパーで、そもそも一時オブジェクトとして使うことを想定していないクラスに利用できる。
永続ストレージ上への確保の禁止はどう使うかというと、Expression Templateのplaceholder型のような、直接、名前付き変数として使うことを想定していないクラスに利用できる。これはN4035で提案されているものと同等の機能も実現する提案である。
両方とも、文脈上暗黙に型変換が起こる場合は、この制約から逃れることが出来る。
&と&&を両方指定すると、どちらも禁止するクラスを書くことができる。この場合、クラスは初期化の中で他の型に変換される場合でしか使うことができない。
struct X & &&
{
operator int () { return 0 ; }
} ;
int x = X() ; // OK
また、std::as_temporary<T>というtype traitsが追加される。これはネストされた型名::typeが、Tがlvalue修飾子を持たぬ場合はT、持つ場合は変換関数の型、それ以外の場合はtypeは定義されないという仕様になる。
クラスのサブオブジェクトとして使用する場合には、この制約は適用されない。この理由について論文は、クラスの完全な型がオブジェクトがどう使われるかについて責任を持つべきだからとしている。
ドワンゴ広告
この記事はドワンゴ勤務中に書かれた。
ドワンゴは本物のC++プログラマーを募集しています。
CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0