C++

C/C++の単項演算子 + について

C++ で char を std::cout とかで出したいとき、 char hoge = 'a'; std::cout << +hoge; みたいに書くと a じゃなくて 97 が出る。これはなぜなのか。 C11 ではこのように述べられている。 The result of the unary + operator is the value of its (promoted) operand. The integer promotions are performed on the operand, and the result has the promoted type. C++17 ではこのように述べられている。 The operand of the unary + operator shall …(略) Integral promotion is performed on integral or enumeration operands. The type of the result is the type of the promoted operand. というわけで C、C++ 両方で単項演算子 + の結果は汎整数拡張がなされた結果になる。 hoge は char で +hoge は int の型を持つということになり、 operator<< の int オーバーロードが呼ばれ、97 が出力された。 参考にした資料 手元にあった C11 と C++17 の最終ドラフトで確認した。 より新しい規格では仕様が変わっている可能性もある。

EmscriptenでJSとCで相互にデータをやり取りする

Emscripten で JavaScript の世界と C の世界でデータをやり取りする方法をメモ (with ccall/cwrap)。 C や C++ 側で必要なこと Emscripten でコンパイルすると、main から到達できないコードは dead code elimination でサクサク消されちゃうので 関数に EMSCRIPTEN_KEEPALIVE を付けて消されないようにしておく必要がある (EMSCRIPTEN_KEEPALIVE 自体は __attribute__((used)) に展開されるっぽい。環境によるとは思うけど)。 コンパイルは emcc -s WASM=1 -s NO_EXIT_RUNTIME=1 -s EXPORTED_RUNTIME_METHODS="['ccall']" -o index.html main.cc とかで。NO_EXIT_RUNTIME にしておくことで、 main を実行したあとランタイムを止めるというデフォルトの挙動を変更できる。 ExPORTED_RUNTIME_METHODS に cwrap とかを入れておくと cwrap から使えるようになる。 ccall や cwrap で関数を呼び出すときの基本的な方法 Module.cwrap('hoge', 'string', 'number')(5); みたいな感じで C の関数を呼び出せるが、問題は引数と戻り値のところに何を入れるかということになる。

型ごとの上限・下限

一応型ごとの大きさは規格では決まってない(最小限のビット数はあるけどこれを書いても意味がない) けど x86_64 だったら普通同じになるはず。 Type Bits Min Max signed char 8 -128 127 unsigned char 8 0 255 signed short 16 -32768 32767 unsigned short 16 0 65535 signed int 32 -2147483648 2147483647 unsigned int 32 0 4294967295 signed long int 64 -9223372036854775808 9223372036854775807 unsigned long int 64 0 18446744073709551615 signed long long int 64 -9223372036854775808 9223372036854775807 unsigned long long int 64 0 18446744073709551615 コード #include <iostream> #include <limits> using namespace std; #define LIMIT(type) cout << "`" #type "` | " \ << sizeof(type) * numeric_limits<unsigned char>::digits << " | " \ << +numeric_limits<type>::min() << " | " \ << +numeric_limits<type>::max() << endl; int main() { cout << "Type | Bits | Min | Max" << endl; cout << "-----|------|-----|----" << endl; LIMIT(signed char); LIMIT(unsigned char); LIMIT(signed short); LIMIT(unsigned short); LIMIT(signed int); LIMIT(unsigned int); LIMIT(signed long int); LIMIT(unsigned long int); LIMIT(signed long long int); LIMIT(unsigned long long int); } 浮動小数点数 10進での桁数 type num float 6 double 15 long double 18

new と malloc(3) を混在させていいのか

という議論が ここ にあったけど(2007 年……古い……!),malloc(3) で取ってきたメモリを delete(もしくは delete[])に渡してはいけないとか,new で取ってきた メモリを free(3) に渡してはいけないのに malloc(3) を読んだり new したり しても問題ないってのはなんか奇妙な話な気がしてきた。 (普通に C++ 書いていれば malloc には用がないけど)。 なぜかというと,どちらのメモリも malloc(3) が管理していないと, 同じ領域が別の用途で使われてしまうということになるので。 で,new で取ってきたのも malloc が管理しているんだったら, delete しても free(3) に渡されるだけだから, デストラクタとかがなければ問題にならない気がする。 と思って gdb でアタッチして試したら実際に malloc が呼ばれてるっぽい。 まあ仕様上未定義ってだけだよね。

チャンクアップデートの最小幅を使ってパフォーマンスチューニングした

Minecraft で読み込まれるチャンクの半径は 3–16 の間で選べるのだそうだ。 ということは,5 チャンク飛ばして更新されているか確認していけば 3 チャンクだったとしても どこかのチャンクにはぶつかるということだ。 というわけで,1 チャンク見て,間の 5 チャンクは更新されているかの確認もせずに 飛ばして,更新されていたら間のチャンクがアップデートされているか確認する ということにすると,何も更新がない場合の生成がかなり速くなった。 やっぱりやることを減らすと簡単にかなり速くなる。

Minecraft のマップを生成するやつを C++ で書き直し始めた

一月は行く,二月は逃げる,三月は去るみたいな,この時期にありがちな,でもちょっと早いような話をしつつ……。 でももう二月なんで早いですよね。まあそんなことはどうでもいい。 こないだ作った Minecraft のマップ画像を生成するやつを C++ で書き直し始めてみたりした。 でもまあ今日はそんなに作業できなかったのでほとんどヘッダファイル書いたら終わったみたいな感じだけど。 もともと Python で書いていたやつは anvil-parser というライブラリを使っていて, そのライブラリは NBT なる Minecraft の独自バイナリ形式のパーズには nbt という別のライブラリに 任せているっぽかった。nbt というライブラリのリポジトリを見に行ってみたら,チャンク読むところとかも実装されていて, なんかマップを生成する example も存在したんですね。うーん。まあいいやろ。 で,anvil-parser ってのはそのライブラリのチャンクの実装とかは使ってなくて, その部分は独自でやってるみたいでした。まあ古い形式にしか対応してなさそうだったのでそのせいかな, という感じがします。

C++標準ライブラリのマニュアルページをインストール

Ubuntu (をはじめ多くの GNU/Linux ディストリビューション) では C 言語のマニュアルページは最初から標準で入っていますが、 C++ のマニュアルページは入っていません (C++ にはマニュアルページを見る文化がないのだろうか…)。 という訳で、自分でインストールする必要があります。 $ sudo apt install libstdc++-8-doc で、見るときは、 $ man std::vector みたいな感じで std:: までつけないといけないみたいです。