くれなゐの雑記

例を上げて 自分で手を動かして学習できる入門記事を多めに書いています

C++の.at()で配列外参照(Out Of Range)を検出して配列外参照した時に0などを返すマクロ

結論

(競技用なのでマクロ小文字ですごめんなさい)
あとこれ本当にバグなく動くのかよくわかってないです

#define oorret 0
#define oor(x) [&](){try{x;} catch(const std::out_of_range& oor){return oorret;} return x;}()

使用例

.at(10000)という範囲外の部分を参照してますが、0を返しています。


gist4e9a649b1c983d0d7d755698c3f7b78c

以下解説及び製作記

動機

競技プログラミングとかで

vector v;
...hoge...
if(0 <= index and index <= 100) hoge v[index] hoge;

と書くのが非常に辛い。

他の手法の候補

どうにかならないものかと考えてみたところ処理速度と, 微妙なめんどくささを犠牲にすると

v.at()

を使えばout of range(以降"oor"と略記)を検出することができる。これを使う
どうやって使うか考え、以下の2つの候補を考えた。

1. 自前でout of rangeを参照すれば0を返すようなコンテナを(vectorを継承するなどして)作る方法
2. マクロを使う方法(今回の記事)
( 3. 関数を作るってどうにかする方法(簡単) )

コンテナを自作する方法について

試しに作ってみたものの2次元以上の配列になるとちょっとめんどくさかったのでやめた

関数を作ってどうにかする方法について

過去に作ったことがあって試しに使ってみたこともあったけど

at(v, index);

みたいにコンテナとindexをコンマでわけないといけないのがどうも直感的で好きではなかった。
(TMPとif()分で.at()使わなくても簡単に作ることができる。)
やっぱりC++っぽい.at()か[]を残した書き方にしたい

マクロでどうやって実現するか

マクロだとv.at()という文字列を直接渡すことができるのでワンチャンあった。
.at()でoorすると例外が発生し, catch(const std::out_of_range&)することができる.
それを受け取り、catchしたら0, しなかったらそのままの値を返すという処理をすれば良い。三項演算子でこれを実現するのは難しそうだったので、[&]なラムダ式にした。
これで当初の目的のv.at()を残しつつ配列外参照を検出できた。(やったね)
ただしまだ欠点があって、oor()中身はdouble型やint型などの一つの数字の型でしかダメで、内部が構造体とかになったら動かない。decltype()とかうまいこと使って値を返さないと汎用的にならない(oorretをいじれば動きます。)

感想

またゴミを作ってしまったかも知れない

おまけ

世の中にはSEGVそのものを消し去る人もいるみたいです
rkx1209.hatenablog.com