2016-06-29

C++標準化委員会の文書: P0250R0-P0259R0

P0250R1: Wording improvements for initialization and thread ids (CWG 2046)

実行単位と実行順序についての文面や言葉の使い分けを微妙に変える提案。

標準化委員会の中では、C++は将来的に、プログラムが明示的にスレッドを使っていなかったとしても、実装が自動的に初期化を並列実行することを許可したいコンセンサスがあるが、現行の文面も既存のコードもそのまま対応できるようにはなっていない。とりあえず文面を整理する。

[PDF] P0251R0: Unified Call Syntax Wording

統一関数呼び出し記法の提案。

f( x, y )という式を書いた時に、f( x, y )を満たすfが存在しない場合、x.f( y )を呼び出す機能のことだ。

template < typename T >
void f( T x, T y )
{
    compare( x, y ) ;
}

この例では、もしcompare( x, y )を満たすcompareが存在しない場合、x.compare( y )の呼び出しが試みられる。

当初の提案に上がっていた、x.f(y)をf(x,y)として扱う機能は省かれた。

これにより、ジェネリックコードは渡された型について気にする必要がなくなる。

[PDF] P0252R1: Operator Dot Wording

operator .をオーバーロード可能にする提案。operator .をオーバーロードした型に対するメンバーアクセスは、原則としてoperator .の戻り値に対して適用される。型に同じ名前のメンバー名がある場合を除く。

template < typename T >
class vector_ref
{
    std::vector<T> v ;
public :
    std::vector<T> & operator .() { return v ;}
    std::vector<T> const  & operator .() const { return v ;}

    std::size_t size() const { return 1 ; }
} ;

int main()
{
    vector_ref r ;

    // operator .を経由した
    // vector_ref.vに対する操作
    r.push_back(0) ;

    // vector_ref.sizeを呼ぶ
    r.size() ;
}

これにより、スマートリファレンスを実装することが可能になる。

operator .はクラス型から、リファレンス型を返すことができる。operator .をオーバーロードした型を、リファレンスサロゲート型と呼ぶ。複数のoperator .が記述されている場合、メンバー名によるオーバーロード解決が行われる。

struct A
{
    int x ;
} ;

struct B
{
    int y ;
}

struct C 
{
    A a ;
    B b ;
    A & operator .() { return a ; } 
    B & operator .() { return b ; }
} ;

int main()
{
    C c{ } ;

    c.x = 0 ; // c.a.x
    c.y = 0 ; // c.b.y
}

名前が衝突した場合は曖昧なためill-formedになる。

struct A
{
    int x ;
} ;

struct B
{
    int x ;
}

struct C 
{
    A a ;
    B b ;
    A & operator .() { return a ; } 
    B & operator .() { return b ; }
} ;

int main()
{
    C c{ } ;

    c.x = 0 ; // エラー、曖昧
}

リファレンスサロゲート型によって初期化した場合、オーバーロード解決により最適なoperator .が選ばれる

struct X
{
    int i ;
    double d ;

    int & operator . () { return i ; }
    double & operator . () { return d ; }
} ;

このようなリファレンスサロゲート型に対して、

以下はwell-formedとなる。

X x{ 0, 0.0 } ;
int i = x ; // well-formed
double d = x ; // well-formed

リファレンスサロゲート型に対する代入には、そのようなオーバーロード解決が働かず、operator .も使っていないため、以下はill-formedとなるのか、それともコピー代入演算子の呼び出しもoperator .を使うからwell-formedなのか、よくわからない。


X x{ 0, 0.0 } ;
x = 0 ; // 謎
x = 0.0 ; // 謎

また、operator .と変換関数がある場合、変換関数が優先されるのではないかと思われる。

struct X
{
    int i ;
    int & operator . () { return i ; }
    operator int & () { return i ; }
} ;

X x{0} ;
int i = x ; // call conversion function

と、このようないろいろな疑問を論文著者に聞いてみたところ、実際オーバーロード解決についてあやふやなため、いま開催中のOulu会議で文書自体が却下されたらしい。

[PDF] P0253R1: Fixing a design mistake in the searchers interface in Library Fundamentals

BMサーチなどを実装するために設計されたsearcherの戻り値を、パターンにマッチした先頭へのイテレーターを返すのではなく、マッチしたパターンの先頭と末尾のイテレーターのpairを返すようにする提案。

次のパターンを探したいことがよくあるから最適化のため。

[PDF] P0254R1: Integrating std::string_view and std::string

stringからstring_viewへの変換は、stringの変換関数が担うべきであるため、そのように設計を変更する提案。

[PDF] P0255R0: C++ Static Reflection via template pack expansion

テンプレートのパック展開を用いた静的リフレクション。使いたくないほど文法が汚い。1

[PDF] P0256R0: C++ Reflection Light

現在提案されているC++の静的リフレクション機能を比較して考察している。

T型のデータメンバーのポインターを取得する

N4428提案

int size = std::class_traits<T>::class_members::size;
// テンプレートメタプログラミングにより0からsize-1までのそれぞれのIについて取得する
auto pointer = std::class_traits<C>::class_members::ge<I>::pointer;
// その後、pointerの型をそれぞれ調べて、データメンバーのみを抽出する処理が必要

P0194R0提案

typedef reflexpr(T) meta_T;
typedef std::meta::get_all_data_members_t<meta_T> meta_DMs;
int size = std::meta::get_size_v<meta_DMs>; // get number of data members
// apply template meta to recurrently pick each I, from 0 to size - 1
// テンプレートメタプログラミングにより、0からsize-1までのそれぞれのIについて取得する
typedef std::meta::get_element_t<meta_DMs, I> meta_F;
gauto pointer = std::meta::get_pointer_v<meta_F>;

P0255R0提案

auto pointers = std::make_tuple( typedef< T, is_member_object_pointer >... );

お題:プライベート・ライアンは救えるか?

struct X {
private:
    int ryan;
};

Urbana会議の結論として、privateメンバーにはうっかりアクセスできないようにしたい。既存のコードにアクセス性を与えるような変更ができないようにしたい。reflection_unsafeのように明示的な操作を必要とするようにすべきか。

[PDF] P0257R1: A byte type for increased type safety

1バイトを表現するstd::byte型を標準ライブラリに追加する提案。

現在、1バイトを表現するには、char, signed char, unsigned charが使われているが、char型には、バイト単位のアクセス、数値型、文字型という3つの目的が割り当てられており、混同しやすい。そのため、バイト単位のアクセス専門の型が必要だ。

std::byteは以下のようにscoped enumで定義されている。


namespace std {
    enum class byte : unsigned char { } ;
}

わざわざコア言語に新しいキーワードとして組み込むまでもなく、既存の言語機能だけで実現できるとしている。

また、コア言語に手を加えて、今までのvoid *からchar *に加えて、std::byte *にstatic_castした場合でもバイト単位でのアクセスが保証される。

P0258R1: is_contiguous_layout

is_contiguous_layout traitsの追加。型がstandard layout typeかつ、すべてのビットがオブジェクトの値の表現に使われる場合にtrueを返す。

例えば、パディングビットが存在するような型に対してはfalseが返される。

目的は、バイト列からハッシュ計算をする最適化ができる型かどうか判定するためだ。

[PDF] P0259R0: fixed_string: a compile-time string

constexpr実装できるbasic_fixed_stringの提案。

basic_stringはconstexpr実装できないので、コンパイル時に文字列処理をするためにconstexpr実装できる設計の文字列クラスを追加する。

constexpr auto hello = std::make_fixed_string("hello") ;
constexpr auto world = std::make_fixed_string("world") ;

constexpr auto helloworld = hello + ", " + world ;

static_assert( helloworld == "hello, world" ) ;

ドワンゴ広告

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

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

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

2016-06-28

C++標準化委員会の文書: P0240R0-P0249R0

P0240R0: Why I want Concepts, but why they should come later rather than sooner

P0225R0「何故私はコンセプトをすぐに欲しているのか」に対する反論、「何故私はコンセプトを欲しているが、時期尚早であるのか」と題された文書。

現在のコンセプトの問題は、標準ライブラリがまだコンセプトに対応していないことと、constrained templateのチェックができないことだ。

標準ライブラリはプログラマーの模範となるべきライブラリで、標準ライブラリですら対応できていない言語機能は、有用性が十分に検証されているとは言えない。また、コンセプトの標準ライブラリがないということは、ユーザーごとに基本的なコンセプトですら、非互換な車輪の再発明が行われるので好ましくない。

現在のコンセプトは、contrained templateに対するチェック機能がない。つまり、テンプレートが、指定したコンセプトの要件のみを使っているコードかどうかを、実体化せずにチェックできない。C++0x時代のコンセプトを実装したConceptGCCの経験から言えば、エキスパートであっても、手で要件を全て網羅することはできず、コンパイラーに指摘されるまで気が付かない要件漏れが発生している。この機能なしではコンセプトは使い物にならない。また、現在提案中のコンセプトは、将来の拡張性の低い設計で、将来的にチェック機能を追加する際は、未チェックのコンセプトとチェックされるコンセプトという、2種類の言語機能を入れなければならず、言語機能が分断してしまう。

P0241R0: Remove Future-Related Explicit Specializations for Void

futureとpromiseのvoid型への特殊化を除去する提案。

P0146R1で、void型は完全型になるので、特殊化は不要になる。

P0242R0: Standard Library Support for Void

標準ライブラリ、特にiostreamをvoid型に対応させる提案。

P0146R0でvoidが完全型になるので、void型に対応する必要がある。

例えば、テンプレート引数でvoid型が渡された場合、テンプレートコードはコンパイル時分岐が必要なくなる。

template < typename F >
void call_print( F f )
{
    std::cout << f() << std::endl ;
}

int main()
{
    call_print( []{} ) ;
}

提案では、void型に対する入出力は、何もしないとしている。"void"を読み書きしたりするのは、既存のコードから考えても、余計なお世話であろうとしている。

P0244R1: Text_view: A C++ concepts and range based character encoding and code point enumeration library

UTF-8/UTF-16をコードポイント単位で読めるイテレーターを提供するtext_viewライブラリの提案

using CT = utf8_encoding::character_type;
auto tv = make_text_view<utf8_encoding>(u8"J\u00F8erg");
auto it = tv.begin();
assert(*it++ == CT{0x004A}); // 'J'
assert(*it++ == CT{0x00F8}); // 'ø'
assert(*it++ == CT{0x0065}); // 'e'

U+00F8は、UTF-8でエンコードすると、0xc30'b8になってしまうのだが、コードポイント単位でのイテレーター経由で見ることができる。

tahonermann/text_view: A C++ concepts and range based character encoding and code point enumeration library

実装はGitHubで公開されているが、コンセプト機能を多用しているため、最新の開発版GCCでしか動かない。

P0245R1: Hexadecimal floating literals for C++

16進数浮動小数点数リテラルの提案

int main()
{

    double d = 0xffp0 ;

    // 255.0000, 0x1.fep7
    printf("%f, %a\n", d, d ) ;
}

文法は、プレフィクス0x/0Xに続いて、16進数桁を記述して、.で区切って小数点以下を記述し、その後に2進数指数部を記述する。

16進数浮動小数点数リテラルには2進数exponentの指定が必須だ。2進数exponentはeではなくpで記述する

\[0xApB = A \times 2^B\]

たとえば、0xabcp3は、\(\texttt{0xabc} \times 2^3\)となる。

すでにC言語には入っている。

これを執筆途中に、トークン列のパースの問題に苦しんだ。

本の虫: C/C++で0xf+1は合法なのに0xe+1はコンパイルエラーになるのはなんで?

[PDF] P0246R0:Contract Assert Support Merged Proposal

フォントにLinux Libertineを使っているためstやctやらがligatureで表示されてやや戸惑う。

それはともかく、内容はcontract機能の議論の結果の改定案。contract機能とは、関数にpreconditionやpostconditionとして期待される状態をチェックするコードを記述できる機能で、コンパイル時や実行時にチェックできる。いわば高級なコア言語でサポートされたassertと言える。

[PDF] P0247R0: Criteria for Contract Support

これもLinux Libertineフォントによるligatureがうっとおしい。

内容は、実行時contractチェックが満たすべき要件について。

[PDF] P0248R0: Concepts in C++17

C++17にConceptが入るべきだと主張する文書。

筆者は現行のコンセプトに満足していない。

[PDF] P0249R0: Input Devices For 2D Graphics

イベント処理と割り込み処理のためのライブラリの提案。

BoostのSignalを参照している。また、QtやAllegro(ゲームフレームワーク)も引き合いに出しているほか、jQueryの手軽さに最も影響を受けた設計だとしている。

確かに、ある程度のGUIライブラリでは常に再発明されている機能ではあるが、皆が納得する設計にするのは難しい気がする。

ドワンゴ広告

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

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

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

2016-06-27

C/C++で0xf+1は合法なのに0xe+1はコンパイルエラーになるのはなんで?

以下のコードはwell-formedである。

int x = 0xf+1 ;

以下のコードはill-formedである。

int x = 0xe+1 ;

何故か。

理由は、0xf+1は、0xf, +, 1という3つのトークンとして認識されるが、0xe+1は、一つのpp-numberトークンとして認識されるためだ。これには、浮動小数点数リテラルの指数の文法が影響している。

具体的には、"数値 e 符号"という組み合わせの文字列が引っかかる。

そのため、16進数のeに1を加えた数を表現する場合は、eと+の間に空白を開けなければならない

int x = 0xe +1 ;

-の場合も同様。

ドワンゴ広告

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

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

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

2016-06-24

C++標準化委員会の文書: P0230R0-P0239R0

[PDF] P0230R0: SG14 Games Dev/Low Latency/Financial Meeting Minutes 2015/10/14-2015/02/10

ゲーム開発、低レイテンシー、Financialな業界からC++に提案をする会議の議事録。

[PDF] P0231R0: Extending the Transactional Memory Technical Specification to Support Commit Actions

トランザクショナルメモリーにコミット操作を追加する提案。コミット処理の結果が反映されるのはは、処理が終わったあとまで遅延される。

[PDF] P0232R0: A Concurrency ToolKit for Structured Deferral/Optimistic Speculation

RCUとハザードポインターの特性を面白い喩え話を用いて説明している。

シュレディンガーは動物園を持っている。動物園には様々な動物がいる。シュレディンガーは動物たちを管理するメモリ内データベースを持っている。動物の出産や購入はデータベースへの追加、死亡や売却はデータベースへの削除として操作される。動物にはネズミや昆虫も含まれるので、このデータベースは頻繁に更新される。このデータベースに対して、動物の状態を調べるためにクエリーすることができる。ただし、猫に対するクエリーが常に多い状態になっている。これは、おそらく動物園内のネズミが点滴の情報を調べるためにクエリーしているのだろうとシュレディンガーは推測している。

この頻繁に更新され、一部の情報に高頻度のクエリーが飛ぶ利用特性を持つデータベースの実装として、グローバルロック、バケットごとのロック、RCU、ハザードポインターによる実装を比較して、それぞれの特性を示している。

また、文書ではリファレンスカウントもRCUやハザードポインターと似たような特性を持つだろうとしている。

将来的には、C++にRCUやハザードポインターのライブラリを追加したいそうだ。

[PDF] P0233R0: Hazard Pointers: Safe Reclamation for Optimistic Concurrency

ハザードポインターの解説文書。

[PDF] P0234R0: Towards Massive Parallelism(aka Heterogeneous Devices/Accelerators/GPGPU) support in C++

GPGPUのような大規模な並列処理を行う異なるアーキテクチャが混在したコンピューター環境をC++でサポートするにあたって、現状の提案の一覧や、既存の実装を解説している。

[PDF] P0235R0: A Packaging System for C++

ブリザードエンタテイメント社によるC++のためのパッケージシステムの提案。

パッケージシステムというのは、Pythonにおけるpipや、Rubyにおけるgemや、node.jsにおけるnpmのような仕組みのことだ。ソフトウェアのライブラリを手軽に配布、入手、インストール、使用できるためのシステムのことだ。

提案はディレクトリ構造にまで言及するなど、かなり具体的なものになっている。

筆者の意見では、パッケージシステムはOSが提供するものであって、言語がそれぞれ提供するのは間違っている気がする。しかし、成功しているプログラミング言語はだいたいパッケージシステムを持っているのも事実だ。

文書の提案する仕組みに基づいたパッケージシステムをClangに実装したものが、ブリザードのGitHubで公開されている

https://github.com/Blizzard/clang

[PDF] P0236R0: Khronos's OpenCL SYCL to support Heterogeneous Devices for C++

OpenCL SYCLとしてC++によるOpenCLのラッパー規格の紹介。

[PDF] P0237R0: On the standardization of fundamental bit manipulation utilities

unsigned charの配列をビット列のコンテナーとして扱えるラッパークラスの提案。ビットに対するリファレンス、ポインター、イテレーターを提供している。

これにより、例えば立っているビットの数を数えたいときは、イテレーターをstd::countに渡せばよい。std::countは、std::bit_iteratorに対して、アーキテクチャがサポートしていれば、popcntを呼び出すなどの最適化も行える。

P0238R0: Return type deduction and SFINAE

戻り値の型の型推定に失敗した場合、ハードエラーではなくSFINAEにする提案。

[PDF] P0239R0: valueless_by_exception

variantのcorrupted_by_exceptionをvalueless_by_exceptionに改名する提案。

ドワンゴ広告

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

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

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

2016-06-21

Chrome 51のV8の興味深いバグ

以下のコードを実行した結果を予想してみてほしい。


function foo()
{
    return typeof null === "undefined" ;
}

for ( var i = 0 ; i < 1000 ; ++i )
{
    console.log( foo() ) ;
}

typeof nullの結果は"object"なので、"undefined"と===で比較するとfalseになる。したがって、関数fooは必ずfalseを返すはずである。1000回実行しようと常にfalseを返す関数は常にfalseを返すはずである。

では実際に実行して確かめてみよう。

コンソールにコピペするのとは挙動が違うが、何度もクリックすると、なぜかtrueを返すようになる。おそらく、コンソールにコピペすると毎回JITが走るので、挙動が違うのだろう。

ちなみに、workaroundとしては、typeof null === undefinedとかtypeof null === "undefined" + ""などがあるらしい。

参考

Javascript developers, be warned about this crazy JIT bug in V8!

Issue 604033 - chromium - JIT compiler not preserving method behavior - Monorail

ドワンゴ広告

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

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

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

C++標準化委員会の文書: P0220R0-P0229R0

P0220R1: Adopt Library Fundamentals V1 TS Components for C++17 (R1)

Library Fundamentals V1の規格への取り込み

Library Fundamentasについては、先日の勉強会資料でまとめている。

EzoeRyou/cpp17lib-slide: 勉強会のスライド資料

P0221R1: Proposed wording for default comparisons, revision 3

デフォルトの比較演算子を生成する提案。

デフォルトの比較演算子の挙動については、すでに解説してきた。現在問題になっているのは、スライシングだ。例えば以下の例をみてみよう。


struct B { int state ; } ;
struct D : B { int state ; }

void f()
{
    B b ;
    D d ;
    b < d ; // well-formed
}

b<dはwell-formedになる。なぜならば、オブジェクトdは、基本クラスBのサブオブジェクトへのリファレンスに、暗黙に変換されるからだ。つまり、上のコードは以下の意味を持つ。


b < static_cast< const B & >( d ) ;

この提案では、このスライシングを禁止する破壊的変更を伴う提案をしている。

提案はこうだ。コピーコンストラクター、operator =、比較関数、不等号関数に、クラスBから派生しているクラスDにたいして、const B &への暗黙の変換を禁止する。

その結果、上記のような合法なC++14コードはill-formedになる。

P0222R0: Allowing Anonymous Structs as Return Values

無名クラス型を関数の戻り値の型として返せる提案。

struct { int x, int y }
f()
{ return { 1, 2 } ; }

void g()
{
    auto [x, y] = f() ;
}

P0223R0: Class Namespace

クラスのメンバーをクラスの定義外で定義するときに、名前空間のようにクラススコープの中に入れることで、冗長な記述を省略できる文法の提案。

以下のようなクラスがあるとする

template < typename CharType, typename Traits, typename Allocator >
class MyString
{
    MyString( ) ;
} ;

クラスのメンバーをクラスの定義内で定義すると、クラス定義が極めて長くなってしまい。クラスの概要がつかみにくくなる。そのため、メンバーはクラスの定義外で定義したい。問題は、現状では極めて冗長な記述をしなければならない。

template < typename CharType, typename Traits, typename Allocator >
MyString< CharType, Traits, Allocator >::MyString( )
{
// ...
}

まずテンプレートを書いた上で、クラス名をテンプレートまで指定したスコープ解決演算子を書かなければならない。しかも、これをメンバー全てに対して記述する必要がある。

このような記述をするのは面倒だし、間違いのもとであるし、リファクタリングの妨げにもなる。そこで、以下のようにクラススコープを名前空間スコープのように使える文法を提案している。


template < typename CharType, typename Traits, typename Allocator >
namespace class MyString
{
    // 何も書かなくていい
    MyString()
    {
    // ...
    }
}

virtualやstaticはクラス名前空間では書けない。これは、現状でもクラス定義外では書けないからだ。

struct S
{
    virtual void f() ;
} ;

// virtualは書けない
void S::f() { }

P0224: Implicit Return Type

関数の戻り値の型を、先行する宣言から保管する提案。

int f() ;

// well-formed
// 戻り値の型はint
auto f() ;

C++14では、宣言が異なるためオーバーロード関数であるとみなされるが、引数が同じなのでill-formedになる。関数の戻り値の型でだけの違いではオーバーロード可能ではないので、不必要な制限である。

この制限緩和は、無名クラスを関数の戻り値の型として使えるようにするためである。


struct { int x, int y ; } f() ;

// OK
auto f()
{
    return { 1, 2 } ;
}

P0225R0: Why I want Concepts, and why I want them sooner rather than later

Ville Voutilainenによる、なぜConceptをC++17に入れるべきかという文書。

筆者の意見では、現状のConceptはC++17に入れるべきではない。

[PDF] P0226R1: Mathematical Special Functions for C++17, v5

数学関数をC++に追加する提案。

[PDF] P0227R0: Weakening the iterator categories of some standard algorithms

std::sort, std::inplace_merge, std::stable_sortのイテレーター要求をRandom Access IteratorからForward Iteratorに変更する提案。

アルゴリズムの研究の発展により、Forward Iteratorでもこれらのアルゴリズムをそれなりの効率で実装できるため。

[PDF] P0228R0: A Proposal to Add Safe Integer Types to the Standard Library Technical Report

数学的に正しく振る舞うかチェックできるライブラリ、std::safe<T>の提案

C++の組み込みの整数型は、数学の法則に正しく従わない。コンピューターの内部の挙動に従うように設計されている。これにより、以下のような数学的に正しいコードは、実際には正しく動作しない。

int f( int x )
{
    return x*x ;
}

なぜならば、x*xの結果は、int型で表現できる範囲を超えてしまうかもしれないからだ。

std::safe<T>は、整数型Tを置き換えて使うことができる。もし、結果が数学的に正しくない場合、例外が投げられる。

int f( std::safe<int> x )
{
    return x*x ;
}

ライブラリ実装により、実行時のオーバーヘッドを極力発生させないことができる。例えば以下のコード

int f( std::safe<std::int8_t> x )
{
    reutrn x*x ;
}

Integral promotionにより、x*xの結果の型はint型であり、それをint型として返す。int型はint8_t型同士の乗算結果を必ず数学的に正しく表現できるので、この場合に実行時チェックは必要ない。ライブラリの実装によって、そのような実行時チェックは省ける。

また、アーキテクチャによっては、演算結果が数学的に正しいかどうかをチェックできる機能を提供していることがあり、ライブラリがそのような機能を活用すれば、実行時のパフォーマンス向上に繋がる。

このライブラリは、整数型のDrop-in replacementとして使えることを目的としているため、追加のオプションなどはない。既存の似たようなライブラリには、オーバーフローやアンダーフローを検出するかどうかや、整数の範囲などを細かく指定できるものがある。このライブラリでは、単純性のため、そのような設計はしない。

なかなかいいライブラリだ。

[PDF] P0229R0: SG5 Transactional Memory Meeting minutes 2015/11/02-2016/02/08

トランザクショナルメモリの会議の議事録。短い。

ドワンゴ広告

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

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

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

2016-06-17

GNU/Linux版Steamがクソすぎる件

正確に言うと、Steam Runtimeがクソすぎる。

GNU/Linux版のSteamは、ユーザースペースをごっそり自前で用意している。これはSteam Runtimeと呼ばれている。steamランチャーとsteamのゲームは、Steam Runtimeでchrootされた環境で実行される。

GNU/Linuxにおいてプログラムをバイナリブロブで配布して実行する場合、ユーザースペースをすべて同梱した上でchrootして実行するのは手っ取り早く互換性を保てる方法だ。問題は、バイナリはそのまま時代に遅れていくということだ。

現在のSteam Runtimeは、Ubuntu 12.04のユーザースペースのものである。実に4年も前のバイナリを使っているわけだ。

事の発端は、GNU/Linuxで動かすfactorioのFPSが安定しないということだった。私が構築したゲーム用のGNU/Linux環境は、GeForce GTX670MXを使っている。数世代前のGPUとはいえ、factorioを動かすのに問題があるとは思えない。

STEAM_RUNTIME=0することで、steamランタイムを無効化できるのだが、依存するi386版のshared libraryを全部インストールしても、まだvgui2_s.soが見つからず起動できない。これはsteam側のshared libraryのようだ。

ところで、factorioの公式サイトのアカウントは、factorioを購入済みのSteamアカウントと紐付けることで、factorioのSteam DRMのかかっていないバイナリを落とすことができる。このバイナリで試してみたところ、なんとFPSが60で安定した。

結論、GNU/Linux版Steamはクソ

2016-06-16

C++標準化委員会の文書: P0210R0-P0219R0

P0210R0: A light-weight, compact dynamic array

軽量な動的配列ライブラリの提案。

vectorはamortized constant timeを実現するために、実際に必要なサイズよりも大きなサイズのメモリを確保している。これはメモリ消費量を必要以上に上げる。

そのため、そのような挙動を行わない軽量な動的配列ライブラリの追加をする提案。

このライブラリは、insert, push_back, emplace{,_back}, eraseといったメンバー関数は提供しない。これらのメンバー関数は、汎用的なコンテナーの要件には定められておらず、特定のコンテナーがそのような操作を効率的に実装できる場合にのみ提供されるものだからだ。

reserveやcapacityもない。

resize(n)は要素数をnにする再確保を常に行う(ただしn == size()の場合を除く)

このライブラリの名前をどうするかは自転車小屋の議論が続いている。

このライブラリはスタックからメモリを確保するdynarrayとは別の目的なので、別のライブラリとなる。

用途は、new T[n]の置き換えだ。

new T[n]にくらべて何が優れているのか。

  • サイズも一緒に管理される
  • クラスによってメモリが所有されるので、オブジェクトの破棄時にメモリも解放される
  • 要素に対して個々に破棄することができる

P0211R0: Allocator-aware library wrappers for dynamic allocation

アロケーターを使ってnew/make_unique/make_shared相当のことができるライブラリ。

new/make_unique/make_sharedは、operator newから生のストレージを確保して、オブジェクトを構築する。アロケーターでやるには、placement newでオブジェクトの構築を自前でやらなければならない。

auto p = new T( args ... ) ;
delete p ;

相当のことを、メモリ確保をアロケーターに変えて行いたい。このライブラリを使えば、以下のように書くことができる。

auto p = std::allocate_new<T>( alloc, args ... ) ;
std::allocate_delete( alloc, p ) ;

同様に、make_uniqueやmake_sharedも、以下のように書ける。

auto p = std::allocate_unique<T>( args ... ) ;
auto p = std::allocate_shared<T>( args ... ) ;

便利なライブラリだ。

P0212R0: Wording for [[maybe_unused]] attribute.

名前が使われないことをヒントとして示す[[maybe_unused]]の文面案。以前は[[unused]]だったが、改名された。

たとえば、以下の例でassertはプリプロセッサーマクロで、リリースビルドで消えてしまうので、コンパイラーからはresultが使用されていないように見え、変数が使われていないという警告を出すかもしれない。これをmaybe_unusedで解消できる。

int f(int x, int y)
{
    [[maybe_unused]] int result = error_check( x, y ) ;

    assert( result ) ;

    return do_something( x, y ) ;
}

P0213R0: Reexamining the Performance of Memory-Allocation Strategies

Fundamentals TSで追加される新しいメモリアロケーターのベンチマークテスト。

monotonicとは、メモリ解放をしないアロケーター。アロケーターオブジェクトの破棄時に、一括してメモリを解放する。メモリの中にリスト構造か何かをつくって開いている領域を探してといったメモリ管理が不要になるシンプルなデザイン

multipoolとは、確保する小さなサイズごとにテーブルを作りそこから確保する戦略のアロケーター

P0214R0: Data-Parallel Vector Types & Operations

1

ベクトル型を利用したSIMDプログラミングの説明

これ事態は提案ではなく、このような機能をC++に入れるにあたって委2員に基礎的な知識を説明するための文書とみえるが、それでも結構詳しく定義されている。

P0215R0: A Civil-Time Library

時間を、absolute time/civil time/time zoneに分類した上で、civil timeをサポートするライブラリの提案。

absolute timeとは、起点時間からのカウント数で、例えばtime_tなどが相当する。C++ではすでにchronoライブラリがある。

civil timeとは、グレゴリオ暦による年月日時分秒のことだ。これをサポートするライブラリを提案している。

タイムゾーンやサマータイムは含まれない。うるう秒も予測不可能なので含まれない。オブジェクトは時間は常に妥当な日付を指す。妥当でない日付を指定すると、正規化が行われる。例えば、1月32日を指定すると、2月1日になる。これにより、呼び出し側は範囲チェックを行わずに単純に日付同士の演算ができる。秒以下の時間は管理しない。

月同士の演算をする場合には、日を1日に合わせなければならない。

P0126R0:A Time-Zone Library

タイムゾーンライブラリの提案。

P0217R0: Wording for structured bindings

P0217R1: Wording for structured bindings

P0217R2: Wording for structured bindings

構造化束縛(structured bindings)の文面案。

多値を受け取る文法の提案。

publicな直接の非staticデータメンバーを持つクラス、配列、std::tuple, std::pairから、それぞれの要素の型と値で変数を初期化できる文法となっている。

int a[3] = { 1, 2, 3 } ;
// int, int, int
auto [ a1, a2, a3 ] = a ;

struct B { int x, double y, std::string z} ;
B b = { 1, 2.0, "3"} ;
// int, double, std::string
auto [ b1, b2, b3 ] = b ;

std::tuple< int, int, int > c{ 1, 2, 3 } ;
// int, int, int
auto [ c1, c2, c3 ] = c ;

これにより、多値を返す関数を使うのが楽になる。

std::tuple< int, double, std::string > f()
{
    return { 1, 2.0, "3" } ;
}

int g()
{
    auto [ x, y, z] = f() ;
}

文法は以下の通り。

auto [ identifier-list ] brace-or-equal-initializer

identifier-listのそれぞれの変数名は、リファレンス型になる。初期化子の要素数と同じだけの識別子がなければならない。

int a[3] = { 1, 2, 3 } ;
// ill-formed.
auto [ a1, a2 ] = a ;

変数は必ずリファレンス型になる。これについては注意が必要だ。構造化束縛にリファレンス修飾子がない場合、初期化子がコピーされて、変数名はそのコピーへのリファレンスになる。リファレンス就職しがある場合、コピーされず、初期化子へのリファレンスになる。

例えば、以下のコードは

int a[3] = { 1, 2, 3 } ;
auto [a1, a2, a3 ] = a ;

以下のコードと同じ意味になる。

int a[3] = { 1, 2, 3 } ;
auto _a[3] = { a[0], a[1], a[2] } ;

auto & a1 = _a[0] ;
auto & a2 = _a[1] ;
auto & a3 = _a[2] ;

初期化子のコピーが作られ、変数はそのコピーへのリファレンスとなる。

以下のように書いた場合は、

int a[3] = { 1, 2, 3 } ;
auto & [a1, a2, a3 ] = a ;

以下のようになる。

int a[3] = { 1, 2, 3 } ;

auto & a1 = a[0] ;
auto & a2 = a[1] ;
auto & a3 = a[2] ;

初期化子はコピーされず、変数は初期化子のオブジェクトへのリファレンスとなる。

P0218R1: C++17 Filesystem

filesystemライブラリ。

P0219R0: Relative Paths

Filesystemライブラリに相対パスを計算する機能を追加する提案。

基本的には、。あるパスに対して、別のパスからの相対パスを計算する方法で、例えば/a/b/cに対して/a/bからの相対パスを計算するとcになる。

これだけならば、現行のFilesystemにも存在するのだが、Symlinkを追ってほしい、追わないでほしい。冗長な.や..を除去してほしい。ほしくないと言った衝突するユーザーの要望があり、かつどのように振る舞うか意見が別れるコーナーケースも多数あるため、Boostでは10年にわたって紛糾している問題。これに対する解決案。

ドワンゴ広告

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

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

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

2016-06-13

rootが無効化された環境でsshfsで権限の必要なファイルを変更する方法

sshfsは便利だ。リモートのファイルシステムをローカルのファイルシステムにマウントして扱うことができる。これにより、ローカル側のGUIのファイルシステムビューワーやエディターを使った温かみののある手作業による管理ができる。

ところで、Ubuntu Serverではrootが無効化されている。そのため、sshfs root@host:remote_path local_path はできない。代わりに、root権限が必要な操作はsudoを使って行う。しかし、sshfsではsudoができない。一体どうすればいいのだろうか。

試していないが、調べたところ、sftp_serverを指定する方法で行けるようだ。

まず、sudoのcredentialをキャッシュさせるために、ssh軽油でsudo -vを実行しておく。

そして、以下のようにsshfsでsftp-serverを指定する。

sshfs user@host:remote_path local_path -o sftp_server="/usr/bin/sudo /path-to/sftp-server"

おそらく行けるはずである。

2016-06-11

プログラミング言語基礎勉強会で発表した

プログラミング言語基礎勉強会 sponsored by @wantedly UIターン - connpass

上記の勉強会で発表してきた。今回の発表は、C++17の標準ライブラリを軽く説明した。

発表に使用したスライド資料はGitHubに上げてある。

EzoeRyou/cpp17lib-slide: 勉強会のスライド資料

発表者が全員濃かったので、他人の発表はあまりよくついていけなかった。

最後に地方と都内で仕事をすることについてというテーマでパネルディスカッションがあったが、いかんせんテーマが技術的なものではないので、極めて一般なこと、例えば地方は地代が安いだの、都内は通勤ラッシュがひどいだのといった、誰でも言えるようなことしか話せず、せっかくあの人間を前に集めて議論する内容としては非常にもったいなく感じた。一体何を考えて、あの人選に対してあんな陳腐なテーマを設定したのか謎だ。

その後懇親会に参加していろいろと雑談をしていた。結局、勉強会の価値はこういう雑談にあると思うので、懇親会がメインで発表は1,2時間だけという勉強会を開いてもいい気がした。

さて、勉強会の会場の近くには、そらよんというクライミングジムがあるので、勉強会のあとに行こうと思っていたが、どうしたことか、休日は21時までしかやっていないそうだ。ジムに行った時には、すでに閉まっていた。

ドワンゴ広告

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

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

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

2016-06-09

GNU/Linuxでゲーム

数年前のゲーミングラップトップを手に入れたので、Ubuntuを入れてゲーム用のGNU/Linux環境を構築しようとした。

GNU/Linuxにおけるゲーミング環境は年々良くなってきている。ValveのSteamOSのおかげでGNU/Linuxに対応する理由があるからだ。今回の目的は、持ち運べるfactorioゲーム環境を作ることだ。

もらったゲーミングラップトップは、G-tunes Nextgear i770というものだ。Core i5 3230MにGeForce GTX670MX、メモリが12GB、ストレージが1TBのSSHDだ。メモリが12GBなのは、4GBメモリを4枚さして16GB積むと何故か起動しないので1枚抜いたとのことだ。メモリが悪いのかマザボが悪いのかはわからない。

もらった時点でWindows 10が入っていたが、容赦なくUbuntu 16.04をインストールする。Windows殺すべし。慈悲はない。

さて、インストールが終わったら。apt-get install steamしてsteamをインストールする。そしてfactorioをインストール。

いざ起動してみると、何事もなく動いた。しかしFPSが30程度しかでない。

GPUドライバーが自由なソフトウェア実装であるNouveauになっていた。涙をのんで不自由なnvidia-driverをインストールする。

nvidiaの不自由なドライバーをインストールするとまともなFPSが出るようになった。しかし、画面にティアリングが発生する。どうやっても直らない。また、xrandrを使ったディスプレイの回転ができない。また、外部ディスプレイをつなぐと、nvidia-settingsからは、一枚の巨大なディスプレイを使っているかのように扱われる。解像度が高すぎるため、一度外部ディスプレイをつなぐと、ゲームのFPSが落ちる。

調べたところ、これはnVidiaのドライバー上の問題で、今のところどうしようもないということ。nVidia Fuck You!

nVidia Optimusは、IntelのGPUとハードウェア的にもかなり密に結合していて、nVidiaのGPUがIntelのフレームバッファに直接書き込むような形になっているそうだ。

また、factorioのゲームサーバーを立てるために、適当な安い中古のラップトップを買ってきた。Ubuntu Serverをいれて、とりあえずfactorioサーバーを建てられるようにはした。中古だが、ある程度こだわって選んでしまったので、果たしてサーバーにしていいものかどうか迷う。ただし、メモリが2GBしかないため、デスクトップ用途に使うとしてもUnityはいれられないだろうから、XbuntuとかLubuntuなどを入れることになると思う。