P0011R0: Additions to Filesystem supporting Relative Paths
filesystemライブラリに相対パスを作り出す関数の追加。
以下のように使える。
using namespace std::experimental::filesystem ;
path Path = "/a/d";
path Start = "/a/b/c";
auto RelPath = relative( Path, Start );
assert( RelPath == path("../../d") );
assert( Path == normalize( Start/RelPath ) );
P0012R0: Make exception-specifications be part of the type system, version 4
例外指定を型システムに含める提案。
現在、例外指定は型システムに含まれていない。例外指定された関数へのポインターについて僅かに言及があるだけである。これにより、違法であるべきコードが合法になってしまったり、合法になって欲しいコードが違法になってしまったりする。解決には、例外指定を型システムに含めるしかない。
P0013R0: Logical Operator Type Traits
任意個のtraitsを受け取って論理値を返すand_, or_, not_の追加。それぞれ以下のように宣言される。
template < typename ... B >
struct and_ ;
template < typename ... B >
struct or_ ;
template < typename ... B >
struct not_ ;
以下のように使う。
template < typename T >
void f()
{
constepxr bool b = and_< std::is_copy_constructible<T>, std::is_copy_assignable<T> >::value ;
}
また、これの変数テンプレート版である、and_v, or_v, not_vも提供される。
[PDF注意] P0014R0: Proposal to add the multiline option to std::regex for its ECMAScript engine
std::regexにregex_constants::multilineを追加する提案。^と$とが、文字列全体の先頭と末尾ではなく、各行の先頭と末尾にマッチするようになる。
// マッチ数をカウントする
unsigned int count_match( std::string const & text, std::regex const & re )
{
std::sregex_iterator iter( text.cbegin(), text.cend(), re ) ;
std::sregex_iterator end ;
unsigned int count = 0 ;
for ( ; iter != end ; ++iter )
{ ++count ; }
return count ;
}
int main()
{
std::regex const re1( R"(^[^]*?$)", std::regex_constants::ECMAScript ) ;
std::string text = R"(this is
a
test.)" ;
std::cout << count_match( text, re1 ) ; // 1
// multilineオプション
std::regex const re2( R"(^[^]*?$)", std::regex_constants::ECMAScript | std::regex_constants::multiline ) ;
count_match( text, re2 ) ; // 3
}
P0015R0: A specialization-friendly std::common_type
std::common_typeの実装を変えて、ユーザーが特殊化するときにCV修飾子はリファレンス修飾子の違いによって何種類も明示的特殊化を書かずにすむようにする提案。
P0017R0: Extension to aggregate initialization
アグリゲート初期化を拡張して、基本クラスを持つ型でも初期化できるようにする提案。
C++11のアグリゲート初期化は、基本クラスがある場合には使えない。
struct unique_id { std::uint32_t id ; } ;
struct user : unique_id
{
std::string name ;
} ;
// エラー
user Adams{ "Douglas Adams"} ;
そのため、基本クラスがある場合には、面倒なコンストラクターを書かなければならない。
user( std::uint32_t id, std::string const & name )
: unique_id{id}, name(name)
{ }
このようなボイラープレートコードは書きたくない。そこで、アグリゲート初期化を拡張して、基本クラスもメンバーのように初期化できるようにする。
user Adams { {42}, "Douglas Adams" } ;
複数の基本クラスがある場合は、記述された順番に初期化する。
struct unique_id { std::uint32_t id ; } ;
struct Name { std::string name ; } ;
struct user : unique_id, Name { } ;
user u{ {0}, {"null"} } ;
まあ、便利そうだ。
P0018r00 : Lambda Capture of *this by Value
lambda式で*thisを値でキャプチャできるようにする提案。
struct X
{
int data ;
std::function<int ()> f()
{
return [*] { return data ; }
}
} ;
int main()
{
std::function< int () > f ;
{
X x ;
f = x.f() ;
}// xは破棄された
f() ; // OK、*thisはコピーキャプチャされている
}
キャプチャーデフォルト*は=とほぼ同じだが、thisポインターではなく、*thisを値でコピーキャプチャーする。
*thisだけをコピーキャプチャする、*thisもシンプルキャプチャーとして書ける。
P0019r00 : Atomic View
非アトミック型のオブジェクトに対してアトミック操作をするviewライブラリの提案。
atomic_viewは、非アトミック型の単一のオブジェクトに対してアトミック操作を提供する。
int x = 0 ;
i = 42 ;
{// iへのこれ以前の操作は全て行われる
std::atomic_view<int> ai(i) ;
// atomic<int>風の操作ができる
// aiを介して操作する限りアトミックになる
}// ai破棄、aiへの操作がiに反映される
実装方法として最も汎用的なものは、mutexのような排他的ロックを使うことだ。もっと効率的な方法としては、まずアトミック型にコピーして、アトミック型のオブジェクトを経由して操作を行い、viewオブジェクトが破棄されるときに元のオブジェクトに結果をコピーするというものだ。
atomic_array_viewは、非アトミック型の配列に対するアトミック操作を効率的に行う意図で用意されている。例えば、排他的ロック実装の場合、ロックを要素ごとではなく配列全体に対して単独で用意できる。
想定用途は巨大な配列に対する高パフォーマンス演算だ。以下のような用途を想定している。
- 巨大な非アトミック型の配列を確保する
- 並列の競合しない初期化を非アトミック操作で高速に行う
- atomic_array_viewを構築する
- 並列の競合する書き込みをview経由で行う
- viewを破棄
- 並列の競合しない読み込みを非アトミック操作で高速に行う
atomic_global_viewは、グローバルにアクセス可能なアトミック型のオブジェクトをアトミックに操作するためのviewだ。インターフェースはatomic_viewと同じ。意図としては、アトミック型へのコピーを行わず、元となる非アトミック型を直接操作するものらしい。
P0020r00 : Floating Point Atomic View
非アトミックな浮動小数点数型に対するアトミック操作を提供するatomic_viewに、整数型と同等のメンバーを追加する提案。
add/sub/fetch_add/fetch_sub/operator +=/operator -=が追加される。
ドワンゴ広告
この記事はドワンゴ勤務中に書かれた。
ドワンゴは本物のC++プログラマーを募集しています。
CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0
P0018r00はもうちょっとですね。
ReplyDeleteポインタをコピーキャプチャしたうえで、ラムダを関数ポインタへ変更したいんですよ。
ポインタが局所的グローバル変数のようにふるまってくれれば問題ない感じです。
ラムダはノリのように使いたいのですが、なかなか現状の仕様ではできることが限られていておおむねうまくいってますが、かゆいところに手が届かない感じです。
でも一歩前進です。
ラムダを関数ポインタ化し、キャプチャーした変数をグローバル扱いするには、関数本体のどこかにその変数あるいはその参照が直接埋め込まれる必要があります。
ReplyDeleteすなわち、ラムダのオブジェクトのインスタンスごとに別々の関数の実体が生成される必要があります。
となると、そのようなラムダを関数ポインタ化する際には、新しい関数の実体を生成するためにJITコンパイル(雛形をコピーしてジャンプテーブルを書き換えるだけですが)が必要になるわけです。
こうなると問題が幾つか。具体的には、ラムダそのものの寿命が尽きたり、コピーされたりした時、どのような挙動をするのが望ましいか、という事です。
破壊しなければポインタを取る度にメモリが圧迫されるし、破壊するなら「ムーブのつもりでコピーした」瞬間に元々ポインタを取って作ってあった実体が破壊されて悲惨な事になるし、その辺りをどのように解決するのか?
そもそも、JITコンパイルにより生成された実行コードは当然書き込み可能なメモリ領域に配置されるため、必ずしも実行可能であるという保証はありません(現代的なアーキテクチャであれば、権限がないとできないのが多いはず)。
よって、このようにしてポインタを作るC++ソースコードは、必ず移植不能なコードとなります。これはC++の機能として実装されるべきではなく、せいぜいC++コンパイラが独自に実装すべきものでしょう。
以上。
なるほど、なんとなくわかります。
ReplyDelete関数の多層コピーはテンプレートが実体化されるときに静的には行われています。これを動的にする必要があるというのはわかりました。
うーむ。なるほど。これコンパイルタイムに検出できないかなぁ。うーん。
寿命云々については、自分だったらそんな長期保存を想定していないので言語側で面倒を見る必要はないと思います。つまり何もしません。止まっても知りません。C言語系らしくというか。
ユーザータイムがライブの内にライフサイクルを終えるように設計します。システムタイムにめり込むようなものは基本的に使いたくないです。
ほんのちょっとのことなのに意外とめんどくさいですね。
templare<int N>
Val_T& Storage(){
static Val_T V;
return V;
}
とすることで一応多層コピー的に同じ名前でグローバル変数を多層的に持つことができます。
仕様上の規定はないかもですが、VCではできました。何の保証にもなりませんが。
Enumを食わせることで変なこともできます。
基本的にこれとの組み合わせでできないかと思ったのですが、まぁメモリを圧迫するようですね。
自分はそんな巨大なメモリを使うことはないのでメモリ上の残留はある程度目を瞑る所存ですが、
大局的に見てNGですかね。
なるほど。いい勉強になりました。
解説ありがとうございました。
> P0013R0
ReplyDeleteやっぱりfold expression要りませんでしたね。
まあtrigraphやらの代わりに委員に仕事を作らないとねw