ぷろみん

プログラミング的な内容を扱ってます

自分で作ったクラスをrange based forに対応させよう

Range based forは素敵

for(auto value: vector){
}

良いですね、スマートです。
しかし、これstd::vector等のスタンダードライブラリに入っているクラスしか使えないと思ってはいないでしょうか?
私が体験した職場で自身のクラスをrange based forに対応させているパターンを見たことがありません。
案外簡単にできるので是非やってみてください。

最低限必要なこと

struct Iterator{
    auto operator!=(Iterator){
        return false;
    }
    auto operator++(){
    }
    auto operator*(){
        return nullptr;
    }
};

struct Foo{
    auto begin(){
        return Iterator();
    }
    auto end(){
        return Iterator();
    }
};

int main()
{
    Foo foo;
    for(auto item: foo){
    }
}

はい、これだけです。
Iteratorに既存のものを利用する場合はなんとbeginとendの関数を定義するだけでrange based forに対応できてしまうのです。
ただ、注意点もあります。
Iterator != Iteratorでループの終了条件を判定するので同じコンテナのIteratorを返すようにしないといけません。
もっと自由度を高くしたい場合は専用のイテレータを作った方が良いでしょう。

後、const版のbeginとendを作っておいた方が後々幸せになれます。

カスタムイテレータ

イテレータの方もそこまで難しいことはなく、range based forの判定に必要な関数を定義するだけです。
operator!=で現在のイテレータとend()のイテレータを比較します。現在のイテレータとend()のイテレータが一致した場合にループ処理を終了させます。
operator++で現在のイテレータを次のイテレータに変換します。
operator*でイテレータからforで使う値を取り出します。

auto item = *iterator;

と同じですね。

展開されるコード

上記クラスがどう処理されるのかも記述しておきます。

for (auto __begin = foo.begin(), __end = foo.end(); __begin != __end; ++__begin ) {
  auto item = *__begin;
  // range based forの中身
}

begin(), end(), operator!=, operator++, operator*が何故必要なのか一目でわかりますね。

参考

Range-Based For Loop Wording (Without Concepts)

綺麗にコードを書く技術

概要

難しい処理をこなす処理を簡単に描けるプログラマではそのコードは汚いものです。
思うに綺麗にコードを書くことは技術のレベルとは関係ないので執着されないのでしょう。
しかし、私は綺麗にコードを書くことは無駄には思えません。
技術が技術と呼ばれる理由に人に共有し洗練していくことが可能という部分があると思います。
私の考える綺麗にコードを書く方法を共有することで技術として残せたらと思います。

リネーム程度のリファクタリングは挨拶かのようにする

汚いコードを書く人の特徴として、一度作成し終わった関数やクラスには追加しかしないというものがあります。
作成段階では引数、名前、メンバが正確じゃないのは当たり前です。
新しい処理を追加する度に与えられたコンテキストが変化していないか目を光らせて、変化していた場合はすぐさま変更しましょう。

スコープを常に最少にする

スコープ、変数のライフタイムを常に自身の考えられる中での最少にすると自然と綺麗なコードになります。
スコープが最少になることを意識してコードを書くと自然に仕切りが必要になり、それが関数として分離できるようになります。

大部分で使わないメンバは現在のクラスに相応しくないのではないかと考える

多数のメンバと多数の関数を眺めてみると良く見つかるのですが、特定の関数グループでしか利用しない変数が存在することがあります。
クラスを分割するチャンスです。

クラスをまたがるコンテキストに注意を払う

2つのクラスの関係を見て、あまりに密に入出力を交換している場合は2つのクラスのためのラッパーを書いた方が良いかもしれません。

継承は使わない

特にC++の話ですが、継承は使わない方が良いです。
継承を使ってコードを綺麗に書けている人を今まで見たことがありません。
多くの場合、仮想関数やらポリモーフィズムやらオーバライドしているしていない、基盤クラスの肥大化によりスパゲッティ化します。
自分は良くても後から来た人は大体読みにくいと思ってます。
Mix Inなら有効な手法なので、こちらを意識できる人は使っても良いと思います。
そういう人はダックタイピングも分かるでしょうし、節度を守れそうです。

cppファイルでのクラス宣言はしない

これはC++の話です。
cppファイルで宣言されたクラスは他のファイルでは使えないので、有用なクラスを作ってしまうと悲惨です。
特にリファクタリングが嫌いで既存のコードへの修正を嫌う人が保守した場合地獄が待っています。
その有用なクラスしか取得できない情報を取得するためのコードが山のように追加されてあらゆるコンテキスがそのファイルに依存するようになります。
そうなると手練れでもそのファイルを依存の海から救い出すのは難しいでしょう。

全てに対して入出力を意識する

関数が入力を受け取って出力を生成するように、全てのプログラミング上の事象の入出力を常に意識すべきです。
生成したいものがあったとしたら、その生成に最低限必要なものだけを与えて生成したいものを生成することに集中すべきです。
このあたりが混ざったコードは簡単にスパゲッティ化します。意識しましょう。
また、具体的なインスタンス等が無かったとしても、何らかの副作用が発生する場合はそれも出力として意識すべきです。

ファイル、関数を巨大にしない

これは当たり前のことなんですが、何故か守れない人が多いです。
スコープ、依存を最少にすることを意識していたら、そのまま全部小さくできるはずです。

変数名やクラス名にはこだわる

適当な名前を付けるくらいなら当然説明的で変な省略語のない名前が良いです。
しかし、変数名は生ものです。コンテキストが変われば名前も変わってきます。
そういう時は素直にエイリアスを貼って別名を付けましょう。
また、スコープに合わせて変数名の長さを調節することも大事です。
長いスコープで存在するものには長い名前を、短いスコープで存在するものには短い名前を与えましょう。

許容されるのなら既存の汚いコードには従わない

無意味なプリフィックスやネームスペースを使っていなかったり、謎のフォルダ分けがあったりします。
そんな場合、コード管理者に問い合わせてそのルールに従わなくても良いか聞くと案外気にしていなかったりします。
まぁ、そんなコードになってしまっている時点で管理できていないのは当たり前なのですが。

なので、容赦なく無視して汚いコード文化を綺麗なコードで上書きしていきましょう。
大丈夫、綺麗なコードを書こうとしている貴方の方が絶対に良いコード文化を築けるはずです。

また、自身に権限があるのなら綺麗に書くことの助けとなるようなコーディング規約を策定すべきでしょう。
私のオススメは

https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md

に従うことですが、膨大過ぎるので少しづつ増やしていくのが現実的かと思います。

毎月開催、自分頑張れよ会議

概要

8月の振り返りと9月の目標について

2017年8月の目標 - ぷろみん

8月の振り返り

とりあえず、無事フリーランスになれた。

Rustの勉強はちょっとずつ進めている。

英語は相変わらず単語アプリで遊ぶ程度。

結局ブログも書こう書こうと思ってたけどこんな時期に。もう9月半分過ぎてるよ。
新しい環境と新しい仕事で忙しかったことにしておこう。

9月にできること

色々列挙してもTODOリストからすべきことに変化しないので、できると思うことだけに厳選

英語の単語アプリを完走する。
既にいくつかのアプリは完走しているので、そこまで苦労することなく完走できるんじゃないかと思う。

やっぱり大好きなC++関連じゃないとモチベーションが続かない気がするのでRustからのC++呼び出しをやってみようかと思う。
何度か調べた限りでは記事もそれなりにあるし多分そのまま実装できるのではないかと思う。

余裕があればUnityからのC++呼び出しについても何か書けたらと思う。
ここは前の仕事でやってた所なのでまとめる感じになるので時間さえ作れればいける。
ただ、直接の関数呼び出しに関しては上手くいかなかったのでそこを何とかしたい。

2017年8月の目標

概要

マイルストーンを置かないと人間はどこまでも怠惰になるので。
月初めに立てて、月終わりに見直そう。

したいこと

大き過ぎで小目標に向かないのもとりあえず書きだす。

C++

大好きC++
私がC++のことをどうやって知っていったかという記事をいつか書きたい。
最近知った面白ネタとしては

d.hatena.ne.jp

がある。プライベート侵害である。合法とはいったい。
アクセスが許されないからってテンプレートから侵入してコンストラクタでポインタを無理矢理奪取してるの面白過ぎるでしょ

後はC++17についてもうちょい詳しくなりたい。
最近@pink_bangbiさんに variant を教わった。便利。

英語

最近話題になったシャーロックホームズが対訳で読めるサイト がカジュアルに英語に触れられるので重宝している。

ブログの更新

有意義な情報の発信を目指すと研究が進まない場合、長期間ブログが更新できない。
目標を立てることで進捗を確認するといういつでも気軽に書ける概念が手に入る。
少しも進まないくらいなら少しでも進んでいる感触を得た方がモチベーションを保てるかもしれない。
強制的なやるべき事があるとモチベーションが下がったりもするので用観察。

独自ドメイン運用上の問題

私はレンタルしているクラウドサーバをカジュアルLinux機として運用しているので
興味もったパッケージやアプリケーションを入れたりするので環境を初期化することを定期的にやってしまう。
こないだもGhostからWordPressに変更して独自ドメインのアドレスパスを無効にしてしまった。

この状態だと独自ドメインを安定運用することもできない。
安定したサーバを一つ用意しておき、そこから不安定なサーバに飛ばすようにするべきか。
今の格安サーバだとVMコンテナで管理するのは難しいのだろうか。
アクセスが集中するようなことは無いと思うので問題ない気はする。

Rust

最近勉強を始めた。まだまだ右も左も分からないよちよちプログラマー
Rustのドキュメントは読み物としても面白い。エラーハンドリングだけでこの分量とか初心者を殺しにきている。

OSS

世の中のOSSはコードが汚いものが多く、リファクタリングをしたいと前々から思っている。
しかし、綺麗なコードは主観的な表現で絶対的な改善ではないため受け入れられないんじゃないかなと二の足を踏んでいる。
個人的には変数名を意味不明な省略表現から脱却するだけでもかなり読みやすくなると思うのだけれども。

今気になっているのはredisで有名な割にコード量が少ない。
Unity等にも持っていけるポータブルメモリインデータベースにならないかなとコードを眺めている。
モバイルまで見るのならrealmだろうが。

ゲーム制作

やってみてスケール感大き過ぎて諦めるが多過ぎるので、もっと適当に小さく作る必要がある。
完璧主義はゲームにおいては最悪な考え方だ。

クォータニオン解説

torini.hateblo.jp

上記でもちょっと書いたが、複数環境でクォータニオンを運用するために詳しい知識を学習して共有したい。

文章書き

どんな駄文でも良いので20万文字かけたら、それには価値があると思っているのでそれを目指している。
1000字くらいなら簡単に書けても、それを200回繰り返すことができない。
とりあえず、頭を空っぽにして貪欲に文章を紡ぐ。内容や構成は最低限のステージに上がってからしようかと思う。

お絵かき

最低限アイコンくらい描けたら重宝しそうなので頑張っていきたい。
英語と同じで時間かけている割に上達しない。

VRお絵かき

tips.hecomi.com

esprog.hatenablog.com

凹さんとEsさんのアセットを使えば3D空間に落書きできるのは分かっているのでペンタブのトラッキングさえできれば簡単に実装できる。

VIVE™ | Vive Tracker

TrackerもVive関連商品として存在しているが、ペンのような小さなものをトラッキングする方法はあるのだろうか。
最初は普通に3Dペインターをサクッと作っちゃった方が良いかもしれない。
上記2つのアセットが優秀なので多分1日もかからずに作れる。

就職

仕事しないと生きられないから仕方ないね。
前の仕事でVRをメインでやっていたので、VR関連が良い。
更に前の仕事でゲームを作っていたので、ゲームだとなお良い。
ただ、ソシャゲ以外のゲーム業界は給料安過ぎる上に古代の音が聞こえるので避けたいな。

オイラー角と空間座標系

概要

複数の環境間での回転のインポート、エクスポートについて調べていたのですが、全然進まないので最小限のアウトプットを目指したメモです。

オイラー

説明はWikipediaに投げます。

重要なのは単にオイラー角といっても12通りの表現方法があることです。

今回の対象環境の1つであるUnityはz-x-y系を採用しています。

Z 座標を軸に z 度、X 座標を軸に x 度、Y 座標を軸に y 度 (順番はこのとおり) 回転する回転

オイラー角については上記座標系とCW(clockwise, 時計回り)とCCW(counterclockwise, 反時計回り)を合わせる事で他空間に同じ回転を適用することができます。
90度回転させると時計回りに回転するので、UnityではCWを使っているようです。

Tait–Bryan angles

航空力学で採用されている形式、 正面にX軸、右側にY軸、下方向にZ軸にそれぞれの正の値が割り当てられます。回転方向はCW。

オイラー角と違って軸の定義まで含まれています。
そして、ここまで厳密ではないと現実世界を定義できないのでジャイロセンサ等の出力はこの形式であることが多いです。

Quaternion

Quaternion Conventions: Hamilton and JPL

QuaternionにはHamiltonとJPL形式があるけど混同したり意識せずに混ぜて使ってしまっていることが多いよって話です。

Convert from JPL to Hamilton

Quaternionからオイラー角への変換

Sensor Fusion

訳語が何になるかは分かりませんが、Sensor Fusionで検索すると参考になるものが色々出てきます。
Sensor Fusion · jherico/OculusRiftHacking Wiki · GitHub

参考

Conversion between quaternions and Euler angles - Wikipedia

中途半端なbit幅の符号付整数を扱い易くするSign extending trick

charじゃ大きすぎる

charは何気に256もの数字を記録できます。
しかし、組み込みのような制限の厳しい環境では1bitでも送受信のデータは節約したいものです。
そんな時はbit演算を駆使してデータをやりとりすることになるのですが、困った状況というものがあります。

例えば、あるセンサーが-10~+10の値を返す場合どうやって値を管理すれば良いのでしょうか?
負の値を表現するには通常2の補数表現が使われますが、それを自分でやるしかないのでしょうか?
実はbit fieldを使えばそんな問題も簡単に解決できるようになります。

bit field

知らない人も多そうですが、データのやり取りの際にUnion等と一緒に使われることが多い機能です。 bit fieldでは指定した変数に割り当てるbit数が指定できます。
極々ローレベルな問題を扱うことが多いCならではの機能といえるでしょう。
書き方としては単純で、クラスや構造体のメンバ変数の最後にコロンと割り当てるbit数を書くだけです。

struct BitField
{
    int a: 3;
};

int main()
{
    BitField bitField = {};
    bitField.a = 0b0000; // 0
    bitField.a = 0b0001; // 1
    bitField.a = 0b0010; // 2
    bitField.a = 0b0011; // 3
    bitField.a = 0b0100; // -4
    bitField.a = 0b0101; // -3
    bitField.a = 0b0110; // -2
    bitField.a = 0b0111; // -1
    bitField.a = 0b1000; // 0
    bitField.a = 0b1001; // 1
    bitField.a = 0b1010; // 2
    bitField.a = 0b1011; // 3
    bitField.a = 0b1100; // -4
    bitField.a = 0b1101; // -3
    bitField.a = 0b1110; // -2
    bitField.a = 0b1111; // -1

これでaには3bit以上の値が入らないようになります。
4bit目以降の数値は無視されていることが分かると思います。
そして、3bit目が補数表現のための負値の役割を果たしていることも分かります。

Sign extending trick

template <typename T, unsigned B>
inline T signextend(const T x)
{
  struct {T x:B;} s;
  return s.x = x;
}

int r = signextend<signed int,5>(x);  // 5bitの符号付整数をintに変換する

まとめ

これで1byteに符号付整数を2つ入れてくるようなセンサーを相手にしても頭を抱えなくて済みますね。
template引数をbit fieldにも使う事ができるって面白かったので紹介してみました。
頭の片隅に置いておくといつか役に立つ日がくるかもしれません。

参考

http://graphics.stanford.edu/~seander/bithacks.html#FixedSignExtend

C++では典型的なif文は使わない方が良い

よくある間違い

あなたはコンテナから指定のデータを探す時に以下のようなコードを書いていませんか?

for(int i = 0; i < 10; i++)
{
    if(list[i] == 5)
    {
        // do something
        break;
    }
}

正しい書き方

// #include <algorithm>が必要
auto result = std::find(list.cbegin(), list.cend(), 5);
if(result != list.cend())
{
    // do something
}

理由

一見するとC++の知識の必要のない最初のコードの方が読みやすく感じるかもしれません。
しかし、熟練のプログラマでも最初のコードを見ると一瞬これは何のコードだろうか?となります。
これは基本的な構文のみで書かれたコードにありがちな分かり辛さです。
一方、正しい書き方の方はすぐに何をしているか分かります。std::findって書いてあるのですから。

間違った書き方の方では更に悪い事にイテレータ破壊の危険性が高いです。
イテレータ破壊についての詳細は以前書いた記事を参照してください。

std::vectorの正しい使い方 - ぷろみん

しかし、algorithmを使おうとするとまずfindでは機能が足らなくなります。
find_ifになると大体の用途では問題なくなるのですが、今度はラムダを理解する必要が出てきます。
最近のコンパイラならラムダにautoを使えるので、

[](const auto& item){ return false; }

みたいに書いたら大体そのままいけるので大分楽にはなっているんで頑張ってください。スコープだけは意識して欲しいところですが。

キャプチャ内での変数宣言はもう入ったんでしたっけ。アレも出来たら便利ですね。