Rokiのチラ裏

学生による学習のログ

order of eval

Because my memory was a little vague, I learned about C++ order of eval again and wrote it in Japanese to my reference book.

github.com

The document of built version is here and for list of grounds and references of this document, consult this page. This beginning of thing is from these tweets so I am grateful for yohhoy’s polite response.

Why an access to a volatile glvalue is considered a side effect by [intro.execution] ?

side-effect についてドラフトで再確認していたところ、その定義として “Reading an object designated by a volatile glvalue” という文面があったのでタイトルの内容をメモ。執筆時現在、side-effect については [intro.execution]/14/sentence-1 にて以下のように述べられている。

Reading an object designated by a volatile glvalue, modifying an object, calling a library I/O function, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution environment.

タイトルに答える内容は、同セクションの3つ目のセンテンスで述べられている。

When a call to a library I/O function returns or an access through a volatile glvalue is evaluated the side effect is considered complete, even though some external actions implied by the call (such as the I/O itself) or by the volatile access may not have completed yet.

ライブラリ I/O 関数への呼び出しが返されるか、volatile glvalue によるアクセスが評価されると、呼び出しによって暗黙の外部操作(I / O自体など)や volatile なアクセスをまだ完了していない可能性がある。
stackoverflow にも分かりやすい回答が載っていたので一部引用。

Hardware registers, which is the normal use-case for volatile (typically in conjunction with pointers, but doesn't have to be), the read of a register will potentially have actual side-effects on the hardware - for example reading a fifo-register on a serial port [or network card, hard disk, or whatever], will affect the state of the hardware, since the fifo has now "moved on" one step. Skipping over, duplicating, caching the result of, or some other such optimisation would definitely cause a piece of driver code and hardware to behave in a different way than the programmer wanted - which would be the case if volatile wasn't considered as having a side-effect.

普段使いのコンパイル環境の整備

私は普段ちょっとしたコードに対してコンパイルを行う時、GCC と Clang のどちらかを使う。使うたびにコンパイルオプションを付与するのは面倒なので、これまでは単にオプションを含めた文字列をそのまま .zshrc のエイリアスとして設定していたのだが、あんまり管理の仕方としてはよろしくないなと思ったので、make で管理したいと思った。しかし、毎度 Makefile を作業ディレクトリごとに移動させたりパスを通したり何なりするのは元も子もないので、それに加えて zsh を使ってちょっとした環境整備をした。

で、これを呼び出す zsh スクリプト--version-Sは割と使うのでそれだけ用意した。

そして .zshrc には以下のように指定。これで、gcc, g++, clang, clang++ がこの Makefile の設定で実行される。

alias g++="$HOME/build_setting/make.zsh GXX"
alias clang++="$HOME/build_setting/make.zsh CLANGXX"
alias gcc="$HOME/build_setting/make.zsh GCC"
alias clang="$HOME/build_setting/make.zsh CLANG"

もっと良い管理法などはあるだろうか。個人的にはこれで満足しているので特別問題はないのだが。

Template non-type arguments によるコンパイル時四則演算パーサー

書いて見た。

テスト。

テストの通り、無駄に C++11 に対応している。勿論の事だが C++14 では variable template が使えるし、C++1z(17) では型推論が効くのでそれによって使い勝手も良くなっている。尚、clang 4.0.0 でテストを行ったが、少し前の GCC とかだと

というようなバグに引っかかるので注意。

javascript parseInt 関数の挙動

javascript (ECMAScript 2015) の parseInt 関数の挙動についてメモ。parseInt 関数は、第1引数の文字列をパースし、第2引数に与えられた基数(数学的記数法の底)に基づく整数を返す関数である。基数には、2 ~ 36 までの整数を指定する事ができ、0 を除いたそれ以外の値を指定した場合は NaN を返す*1。以下、ソースから一部改変して引用。

isNaN(parseInt('null',10)) === true // #1
isNaN(parseInt('null',23)) === true // #2
parseInt('null',24) === 23 // #3
parseInt('null',31) === 714695 // #4

♯1 と ♯2の挙動は、分かりやすい。'null'は文字列であり数ではないので、NaNが返ってくる。しかし、♯3 と ♯4 は NaN ではなくそれぞれ両者とも値が返ってくる。これは、第1引数文字列の頭から、第2引数で与えられた基数による単位解釈の基、数として認識できる値が有る限りパースが続き、そうでない部分を切り捨てて返すという parseInt 関数の仕様によるものである。
♯3 では基数として 24 を指定しているが、24進法で n は 23 という値を持つ。しかしその後に続く u は、24進法では値を持たないため無視されて 23 が返ってくる。♯4 は 31進法で全てを有効な数として捉える事ができるため、714695 が返ってくる。
この事実確認のため、MDN web docs の parseInt を見たら、日本語化されたドキュメントが参照している翻訳元が以前のリビジョンでは説明として間違っており、その後更新されて修正されたものの、日本語化されたドキュメントはその間違った説明がされているリビジョンから更新されていなかったため更新を加えた。その時に、parseInt 関数の関係で少し興味深い挙動を示す質問を見つけたので記載する。

stackoverflow.com

parseInt(123123123123123123123123);というように引数には数値が指定されているため、.toStringによって文字列に変換されて以下のようになる。

parseInt("1.2312312312312312e+29").

基数を指定しない場合 parseInt はそれを10進法と解釈するので、"1.2312312312312312e + 29"から + に達してその時点で中断し、"1.2312312312312312"を解析して、1が返される。

Template non-type arguments (Variadic templates) によるコンパイル時乱数生成

Variadic templates でコンパイル時に乱数を生成してみた。現段階ではエンジンとして linear congruential, Xorshift, mersenne twister を、Discrete Uniform Distribution として uniform_int_distribution を実装してみた。ただし Template non-type arguments に対して実数を扱わせる事はできないので*1、そのようなものに対する Discrete Uniform Distribution は linear congruential で用いられる方法を流用している*2また、discard 的なものは未実装であるがこれも追々実装予定。実装した。

github.com

一応こんな感じに書ける。

make_random_sequenceという命名がなんだか微妙だが、make_index_sequenceに則って取り敢えずそうしている。それぞれ乱数エンジンの型は、resultnext_type型とdiscart_type型を持っており、resultが現在の擬似乱数の生成された値、next_typeが次の擬似乱数を生成する型、discart_typeが指定された回数分内部状態が進んだ擬似乱数を生成する型となっている。
尚、パラメータパックの保持する値に対してランダムアクセスを行おうとすると、再帰によって値を1つ1つ捨てる他がないので、これが線形となってしまってとても時間がかかる。これを極力避けた。また、C++1z からテンプレート引数型にautoを記述する事ができるようになったが、これを再帰中に多様すると型推論の負荷が強く、手元のコンパイラ(GCC 7.1.0)では有限時間内にコンパイルが完了せず abort trap で死んだので、 mersenne twister の実装などでは特に型を明示するようにしている。

以下は、srook::anypack<>::random::mersenne_twister のテストコード。実装はこれをパスする。

*1:N4660 [temp.arg.nontype]. ただし参照を除く

*2:std::ratio を使って追々実装するかもしれない

P0707R1 Metaclasses の Design principles

P0707R1 は メタクラスという新しい概念、言語機能についての提案論文である。論文の中身そのものとは少し離れるのだが、1.1 の Design principles が中々良いデザイン原則で他のものにも流用できるな〜と感じたので引用してメモ。

The primary design goal is conceptual integrity [Brooks 1975], which means that the design is coherent and reliably does what the user expects it to do. Conceptual integrity’s major supporting principles are:
  • Be consistent: Don’t make similar things different, including in spelling, behavior, or capability. Don’t make different things appear similar when they have different behavior or capability. – For example, in metaclasses we use normal class declaration syntax instead of inventing novel syntax.
  • Be orthogonal: Avoid arbitrary coupling. Let features be used freely in combination. – For example, in these papers for can be used to process a reflected collection of items (e.g., all the member functions of a class), without having a distinct special-purpose for_each<> on a reflected collection.
  • Be general: Don’t restrict what is inherent. Don’t arbitrarily restrict a complete set of uses. Avoid special cases and partial features. – For example, this paper prefers to avoid creating a special-purpose syntax to declare metaclasses, and instead lets programmers write metaclasses using normal class scope declaration syntax plus the general features of reflection and compile-time programming. Also, metaclasses are just code, that can appear wherever code can appear – written inside namespaces to avoid name collisions (including putting common ones in std::), and shared via #include headers or via modules.
  • These also help satisfy the principles of least surprise and of including only what is essential, and result in features that are additive and so directly minimize concept count (and therefore also redundancy and clutter).

主な設計目標は概念的な完全性である[Brooks 1975]。これは、設計に一貫性があり、ユーザーが期待することを確実に実行することを意味する。概念的完全性の主要な支援原則は

  • 一貫性を保つ:綴り、動作、能力など、似たようなもの同士のそれを変えない。異なる行動や能力を持っているときに、異なるものが似ているように見せない。例えば、メタクラスでは、新しい構文を発明するのではなく、通常のクラス宣言構文を使用する。
  • 直交させる:任意の結合を避ける。機能を自由に組み合わせて使用​​できるようにする。 - 例えば、これらの論文では、リフレクションされたコレクションに対して別個の特別な for_each<>を持たずに、コレクション(例えば、クラスのすべてのメンバー機能)を処理するために使用する事ができる。
  • 一般的にする:固有のものに制限しない。特殊なケースや部分的な機能を避ける。例えば、この論文では、メタクラスを宣言するための特別な構文を作成することを避け、通常のクラススコープ宣言構文とリフレクションとコンパイル時のプログラミングの一般的な機能を使用してプログラマーメタクラスを書き込ませるようにしている。また、メタクラスは名前の衝突を避けるためにstd::名前空間に共通のものを入れたり、#includeヘッダやモジュールを介して共有するなどをしている。