2015年7月22日水曜日

C/C++ のプログラムの書き方入門 コーディングルールはどこから学ぶ?(2016/3/15 追記)

読みやすいコードを書くことは重要。個人で書いていてもデバックのやり易さが違うし、チームだといわずもがな。下手なコードを書いていると、解りやすくかけ!!と言いたくなる。過去にそれですべて書き直しをしたことがなんどかありますね。っと、いっても私も長いこと書いてなくて忘れて下手なコード屋になっていますが。
ちょっと復習がてらに、私の知っているコーディングルールは、本のやつと自動車業界とNASA。各々紹介していきます。ってか引用のみです。
追記:
流石はGoogleさん。自分のコーディングルールを公開しているようでした。結構細かくて面白いので追記します。

目次
1.本
2.MISRA-C(自動車業界の標準です)
3.NASA
4.Google

本文
1.本から
1.1

プログラマと名乗っている人で知らないと、潜りだと思います。まぁ、俺は現在潜りなので持ってないけど。きれいに書くなら必読書。原書が英語なので、どこかで手に入るでしょう。

1.2.
最近知った本。ちょっと読んでみたらかなり良かったです。
英語版であるが、原初のサンプルがここでみることができる(The Art of Readable Code)

2.自動車業界:
MISRA-C
MISRA-C:2004(ちょっと古い) R:必要,A:推奨
環境
1.1 Rすべてのコードは、ISO/IEC 9899:1990,Program language-Cを満たしていなければならない。
1.2 R未定義または未規定の動作に依存してはならない。
1.3 R言語/コンパイラ/アセンブラが準拠するオブジェクトコードの共通インターフェース規格が定義されている場合に限り、複数のコンパイラや言語を用いることができる。
1.4 Rコンパイラやリンカが、外部識別子に対する31文字特徴及び大文字・小文字の区別をサポートしているかを確認しなければいけない。
1.5 A浮動小数点の実装は、定義された浮動小数点規格に従うべきである。
言語拡張
2.1 Rアセンブル言語が、カプセル化して分離しなければならない。
2.2 Rソースコードでは、/* ・・・ */ スタイルのコメントだけを用いなければならない。
2.3 R文字列 /* をコメント内で用いてはならない。
2.4 Aコードの一部を”コメントアウト”すべきではない。
文書化
3.1 R用いる処理系定義の動作は、すべて文書化しなければならない。
3.2 R文字集合及びそれに対応するエンコーディングは、文書化しなければならない。
3.3 A選定したコンパイラの整数除算の実装について確認し、文書化し、十分に配慮すべきである。
3.4 R用いる #pragma 指令は、すべて文書化し、説明しなければならない。
3.5 Rビットフィールドの処理系定義の動作とパッキングに(プログラムが)依存している場合、それは文書化しなければならない。
3.6 R製品コードで用いられるすべてのライブラリは、この文書の規定にしたがって記述されていなければならず、適切な妥当性確認を受けていなければならない.
文字集合
4.1 RISO/IEC 9899:1990で定義されている拡張表記だけを用いなければならない。
4.2 R3文字表記は、用いてはならない。
識別子
5.1 R(内部及び外部)識別子は、31文字を超える特徴に依存してはならない。
5.2 R外部スコープの識別子が隠蔽されることになるため、内部スコープの識別子には外部スコープの識別子と同じ名前を用いてはならない。
5.3 Rtypedef 名は、固有の識別子でなければならない。
5.4 Rタグ名は、固有の識別子でなければならない。
5.5 A静的記憶域期間をもつオブジェクトや関数の識別子は、再使用すべきではない。
5.6 A構造体及び共用体のメンバ名を除いて、あるネームスペースの識別子を、他のネームスペースの識別子と同じ綴りにしてはいけない。
5.7 A識別子は、再使用すべきではない。
型
6.1 R単なるchar型は、文字データの格納及び使用に限って用いなければならない。
6.2 Rsigned char型及びunsigned char型は、通知データの格納及び使用に限って用いなければならない。
6.3 A基本型の代わりに、サイズ及び符号属性(signedness)を示す typedef を用いなければならない。
6.4 Rビットフィールドは、unsigned int型またはsigned int型だけで定義しなければならない。
6.5 Rsigned int型のビットフィールドの幅は、2ビット長以上でなければならない。
定数
7.1 R(0以外の)8進定数及び8進拡張表記は、用いてはならない。
宣言及び定義
8.1 R関数は、常にプロトタイプ宣言をもち、プロトタイプ宣言は、関数定義及び関数呼び出しの両方から参照されなくてはならない
8.2 Rオブジェクト又は関数を宣言又は定義するときは、常にその型を明記しなければならない。
8.3 Rそれぞれの関数宣言及び定義で与えられる仮引数の型は、一致しなければならず。、戻り値の型も一致しなければならない。
8.4 Rオブジェクト又は関数を複数回宣言する場合、それらの型に互換性がなければならない。
8.5 Rヘッダファイル内で、オブジェクト又は関数を定義してはならない。
8.6 R関数は、ファイルスコープで宣言しなければならない。
8.7 Rオブジェクトが単一の関数内だけでアクセスされている場合は、そのオブジェクトをブロックスコープで定義しなければならない。
8.8 R外部オブジェクト又は外部関数は、単一のファイル内だけで宣言しなければならない。
8.9 R外部結合をもつ識別子は、唯一の外部定義をもたなければならない。
8.10 R外部結合が必要な場合を除き、ファイルスコープに存在するオブジェクト又は関数すべての宣言及び定義は、内部結合にしなければならない。
8.11 Rstatic記憶クラス指定子は、内部結合をもつオブジェクト並びに関数の定義及び宣言に対して用いなければならない。
8.12 R外部結合をもつ配列を宣言するときは、明示的にサイズを記述するか、初期化によって暗黙的にサイズを定義しなければならない。
初期化
9.1 Rすべての自動変数は、用いる前に値を代入しなければならない。
9.2 R配列及び構造体を0以外で初期化する場合は、構造を示し、それに合わせるために波括弧 "{ }" を用いなければならない。
9.3 Rすべての要素を明示的に初期化する場合を除き、列挙子リストでは、先頭以外のメンバを明示的に初期化するために、"=" 構文を用いてはならない。
算術型変換
10.1 R次の条件に該当する場合、整数型の値を異なる潜在型に暗黙的に変換してはならない。
a)同じ符号属性(signedness)をもつより大きな整数型への変換でない場合
b)式が複合式である場合
c)式が定数でなく、関数の実引数である場合
d)式が定数でなく、return式である場合
10.2 R次の条件に該当する場合、浮動小数点型の式の値を異なる型に暗黙的に変換してはならない。
a)より大きな浮動小数点型への変換でない場合
b)式が複合式である場合
c)式が関数の実引数である場合 
d)式がrturn式である場合
10.3 R整数型の複合式の値は、式の潜在型と同じ符号属性(signedness)をもつ、より小さな型へのキャストだけが許される。
10.4 R浮動小数点型複合式は、より小さな型へのキャストだけが許される。
10.5 Rビット単位の演算子 ~ 及び << が、潜在型の unsigned char 又は unsigned shortであるオペランドに適用される場合、その結果は、そのオペランドの潜在型へ直ちにキャストさせる。
10.6 Rすべての符号なし型の定数には、接尾語 "U" を付けなければならない。
ポインタの変換
11.1 R関数ポインタは、汎整数型以外の任意の型との間で変換してはならない。
11.2 Rオブジェクトポインタは、汎整数型、オブジェクト型を指す他のポインタ、voidポインタを除く任意の型との間で変換してはならない。
11.3 Aポインタ型と汎整数型との間でキャストすべきではない。
11.4 Aオブジェクト型を指すポインタとオブジェクト型を指す異なるポインタとの間でキャストすべきではない。
11.5 Rポインタで指し示された型からconst修飾やvolatiles修飾を取り除くキャストを行ってはならない。
式
12.1 A式中におけるC言語の演算子優先順位規則への依存は、極力制限すべきである。
12.2 R式の値は、規格が認められるどのような順序で評価されようとも、同じでなければならない。
12.3 Rsizeof演算子は、副作用がある式に用いてはならない。
12.4 R論理演算子 && 又は||の右側のオペランドには、副作用があってはならない。
12.5 R論理演算子 && 又は||のオペランドには、一次式なければならない。
12.6 A論理演算子(&&,||,!) のオペランドは、実質的なブール型になるべきである。実質的なブール型である式は、(&&,||,!) 以外の演算子のオペランドとして用いるべきではない。
12.7 Rビット単位の演算子は、潜在型が符号付きのオペランドに対して適用してはならない。
12.8 Rシフト演算子の右側のオペランドの値は、0以上、かつ、左側のオペランドの潜在型のビット幅未満でなければならない。
12.9 R単項マイナス(ー)演算子を、潜在型が符号なしの式に用いてはならない。
12.10 Rカンマ演算子は、用いてはならない。
12.11 A符号なし整数定数式の評価では、ラップアラウンドが発生しないようにすべきである。
12.12 R浮動小数点お潜在的なビット表現は、用いてはならない。
12.13 Aインクリメント(++)演算子及びデクリメント(ーー)演算子は、式中で他の演算子と混在させるべきではない。
制御文の式
13.1 Rブール値が生成される式の中で代入演算を用いてはならない。
13.2 Aオペランドが実質的なブール型である場合を除き、0との比較テストは明示的に行うべきである。
13.3 R浮動小数点式は、等価又は非等価のテストをしてはならない。
13.4 Rfor文の制御式は、浮動小数点型のオブジェクトを含んではならない。
13.5 Rfor文の3つの式には、ループ制御に関わるものだけを記述しなければならない。
13.6 Rforループの中で繰返しカウンタとして用いる数値変数は、ループの本体内で変更してはならない。
13.7 R結果が不変になるブール演算は、許されない。
制御フロー
14.1 R到達しないコードがあってはならない。
14.2 R空文でない場合には、次のいずれかの条件を満たさなければならない。
a)実行方法にかかわらず、1つ以上の副作用があること
b)制御フローを変更すること
14.3 R前処理の前の状態で、空文は、それ自身(;)を1行におかなければならない。ただし、空文のあとの最初の文字が、空白類文字であるという条件が満たされれば、空文の後にコメントを入れることができる。
14.4 Rgoto文を用いてはならない。
14.5 Rcontinue文を用いてはならない。
14.6 R繰返し文では、ループを終了させるためのbreak文の使用を最大でも1つだけに留めなければならない。
14.7 R関数では、関数の最後に唯一の出口がなくてはならない。
14.8 Rswitch, while, do ... while, for 文の本体を構成する文は、複合文でなければならない。
14.9 R"if(式)"構造の後には、複合文を続けなければならない。elseキーワードの後には、複合文又は他のif文を続けなければならない。
14.10 Rすべての if ... else if 構造は、else節で終了しなければならない。
switch文
15.1 Rswitchラベルは、それを直接う内包している複合分が、switch文の本体である場合にだけ用いなければならない。
15.2 R空でないswitch節は、無条件break文で終了しなければならない。
15.3 Rswitch文の最後の節は、default節でなければならない。
15.4 Rswitch式では、実質的なブール型になる値を用いてはならない。
15.5 Rすべてのswitch文には、最低でも1つのcase節を記述しなければならない。
関数
16.1 R可変個引数をもつ関数を定義してはならない。
16.2 R関数は、直接的か間接的かにかかわらず、その関数自身を呼び出してはならない。
16.3 R関数プロトタイプ宣言では、すべての仮引数に対して識別子を指定しなければならない。
16.4 R関数の宣言とその定義で用いられる識別子とは一致しなければならない。
16.5 R仮引数をもたない関数は、仮引数をvoid型で宣言しなければならない。
16.6 R関数に渡される実引数の数は、仮引数の数と一致しなければならない。
16.7 A関数プロトタイプ内のポインタ仮引数は、指し示しているオブジェクトを変更する目的でポインタが用いられていない場合、constへのポインタとして宣言すべきである。
16.8 R戻り値の方が非voidの関数の場合、すべての出口で、式をもつ明示的なreturn文を記述しなければならない。
16.9 R関数識別子には、前に&を付けるか、括弧付きの仮引数リスト(空でも可)を指定して用いなければならない。
16.10 R関数がエラー情報を戻す場合、エラー情報をテストしなければならない。
ポインタ及び配列
17.1 Rポインタ算術は、配列又は配列要素を扱うポインタにだけに適用しなければならない。
17.2 Rポインタ減算は、同じ配列の要素を扱うポインタにだけに適用しなければならない。
17.3 R>,>=, <, <= は、同じ配列を扱う場合を除き、ポインタ型に適用してはならない。
17.4 Rポインタ算術で許される形式は、配列添字付けだけでなければならない。
17.5 Aオブジェクトの宣言では、3段階以上のポインタ間接参照を記述すべきではない。
17.6 R自動記憶域にあるオブジェクトのアドレスを、そのオブジェクトの存在が終了した後にまで持続期間をもつ他のオブジェクトに代入してはならない。
構造体及び共用体
18.1 Rすべての構造体及び共用体の型は、コンパイル単位の最後に完全でなくてはならない。
18.2 Rオブジェクトを、重複するオブジェクトに代入してはならない。
18.3 Rメモリ領域は、無関係な目的で再使用してはならない。
18.4 R共用体は、用いてはならない。
前処理指令
19.1 Aファイル内で、#include 文の前には、他の前処理指令又はコメントだけを記述すべきである。
19.2 A#include指令のヘッダファイル名には、非標準文字を記述すべきではない。
19.3 R#include指令の後には、<filename>又は"filename"が続かなければならない。
19.4 RCマクロは、波括弧で囲まれた初期化子、定数、括弧で囲まれた式、型修飾子、記憶域クラス指定子、do-while-zero構造だけに展開されなければならない。
19.5 Rマクロは、ブロック内で#define又は#undefしてはならない。
19.6 R#undefを用いてはならない。
19.7 A関数形式マクロよりも関数を用いるべきである。
19.8 R実引数のすべてを指定せずに、関数形式マクロを”呼出し”してはならない。
19.9 R関数形式マクロの引数には、前処理指令のような字句を記述してはならない。
19.10 R#又は##のオペランドとして用いられている場合を除き、関数形式マクロの定義では、仮引数のそれぞれのインスタンスを括弧で囲まなければならない。
19.11 R前処理指令のすべてのマクロ識別子は、用いる前に定義しなければならない。ただし、#ifdef及び#ifndef前処理指令やdefined()演算子の場合には、その限りでない。
19.12 R1つのマクロ定義内で、#又は##前処理演算子を複数回用いてはならない。
19.13 A前処理演算子#及び##を用いてはならない。
19.14 Rdefined前処理演算子は、2つの標準形式のうち、いずれかの形式を用いなければならない。
19.15 R同じヘッダファイルの内容が2回取り込まれないように、予防措置を取らなければならない。
19.16 R前処理指令は、前処理で除外されたとしても、構文的に意味をもつようにしなければならない。
19.17 Rすべての#else, #elif, #endif前処理指令は、それに関連する#if指令又は#ifdef指令と同じファイルに存在しなくてはならない。
標準ライブラリ
20.1 R予約済み識別子及び標準ライブラリのマクロや関数は、定義、再定義又は定義の解消をしてはならない。
20.2 R標準ライブラリのマクロ、オブジェクト、関数の名前は、再使用してはならない。
20.3 Rライブラリ関数に渡される値については、その妥当性をチェックしなければならない。
20.4 R動的ヒープメモリ割り当ては、用いてはならない。
20.5 Rエラー指示子 errno は、用いてはならない。
20.6 R<stddef.h>ライブラリのoffsetofマクロは、用いてはならない。
20.7 Rsetjmpマクロ及びlongjmp関数は、用いてはならない。
20.8 R<signal.h>のシグナル処理機能は、用いてはならない。
20.9 R入出ライブラリ<stdio.h>は、製品コードで用いてはならない。
20.10 R<stdlib.h>ライブラリのライブラリ関数 atof, atoi, atolは、用いてはならない。
20.11 R<stdlib.h>ライブラリのライブラリ関数 abort, exit, getenv, systemは、用いてはならない。
20.12 R<time.h>ライブラリの時間処理関数は、用いてはならない。
実行時誤り
21.1 R実行誤りが最小であることを、次の内少なくとも1つ用いて保証しなければならない。
(a)静的解析ツール/技法
(b)動的解析ツール/技法
(c)実行時誤りをチェックするための明示的なコーディング

3.NASA(原文コピー)
10個でいいらしいです。

概要Wiki

  • Avoid complex flow constructs, such as goto and recursion.
  • All loops must have fixed bounds. This prevents runaway code.
  • Avoid heap memory allocation.
  • Restrict functions to a single printed page.
  • Use a minimum of two runtime assertions per function.
  • Restrict the scope of data to the smallest possible.
  • Check the return value of all non-void functions, or cast to void to indicate the return value is useless.
  • Use the preprocessor sparingly.
  • Limit pointer use to a single dereference, and do not use function pointers.
  • Compile with all possible warnings active; all warnings should then be addressed before release of the software.

  • 詳細な原文

    4.Google C++ Style Guide
    長いので、下のURLを参照ください。
    https://google.github.io/styleguide/cppguide.html

    2015年7月21日火曜日

    STL vector の基本操作・サイズ・イテレーター・操作・交換・判定

    http://program.station.ez-net.jp/special/handbook/cpp/stl/vector.asp
    より転載

    可変長配列 std::vector<> を使用する

    C++ では、可変長の配列を簡単に利用できる std::vector<> というテンプレートクラスが用意されています。
    これを使用することで、要素を追加したり削除したり、挿入したりといったことが簡単にできます。同じようなものに std::list がありますけど、こちらと違ってインデックス番号を使って任意の要素を自由に取り出せるのも魅力です。
    テンプレートクラスなので、任意のデータ型を扱えるのも便利です。

    使用準備

    C++ で std::vector<> を使用するには、次のようにヘッダーファイルをインクルードする必要があります。
    #include <vector>
    このようにすることで、std::vector<> テンプレートクラスが利用できるようになります。

    また、この std::vector<> を操作するのに使う一部の関数は、次のヘッダーに用意されています。
    #include <algorithm>
    これをインクルードすることで、並び替えなどの外部関数を使うことができます。

    初期化

    std::vector<> インスタンスは、次のようにして生成します。
    std::vector<int> vector;
    左側で指定している "std::vector<int>" がデータ型になります。
    ここで "<int>" としているのは、この std::vector<> が int 型を扱うということを意味しています。ここを使いたい型に変えることで、任意の型を std::vector<> で扱うことが可能です。
    その後の "vector" は、自由につけられる変数名です。

    利用できるメソッド

    std::vector<> には、動的配列を手軽に操作するためのメソッドがたくさん用意されています。
    その中の、良く使いそうなメソッドをいくつか紹介します。

    基本操作

    TYPE at(sizet index)指定したインデックス番号の要素を取得します。[] 演算子と違って、無効なインデックスを指定した場合は out_of_range 例外が発生するので、間違いを検出しやすくなっています。
    void clear()格納されている要素を全て削除します。
    void push_back(const TYPE& value)指定した値を最後尾に追加します。新しい値は std::vector<> 内の要素を格納するメモリに代入文で設定されます。このとき、追加済みの全ての要素が再構築されます。
    void pop_back()最後尾の要素を削除します。
    TYPE front()先頭要素を取得します。
    TYPE back()最終要素を取得します。
    std::vector<> は、スタック的な動作をするメソッドが用意されています。
    push_back で最後尾に値を追加し、pop_back で最後尾の要素を削除します。
    このとき、値の追加の際には、既存の全ての要素がコピーコンストラクタを使って再構築され、それまで使っていた値は全て破棄されます。最後尾の削除については、最後の要素が破棄されるだけで、その他の要素はそのままのようです。

    最後尾の要素の値を取得したい場合には back を使用します。
    また、std::vector<> では任意の場所の値を at メソッドまたは [] 演算子を使って取得できます。
    どちらも 0 から始まるインデックス番号で要素を指定することができますが、存在しない要素を指定した場合、at メソッドでは out_of_range 例外が発生します。[] 演算子の場合は、不正のまま続行されます。

    状態の取得

    size_t size()格納されている要素数を取得します。
    void empty()要素が格納されていない場合に true を返します。
    std::vector<> では、要素の数を知るための size メソッドの他にも、要素があるかどうかを調べるための empty メソッドも用意されています。

    イテレータ

    iterator begin()要素の先頭を示すイテレータを取得します。
    iterator end()要素の末尾 (EOF) を示すイテレータを取得します。最終要素はこのひとつ前にあります。
    reverse_iterator rbegin()要素の末尾を示すリバースイテレータを取得します。
    reverse_iterator rend()要素の先頭 (EOF) を示すリバースイテレータを取得します。先頭要素はこのひとつ前にあります。
    イテレータというのは、繰り返し処理を支援する仕組みです。
    std::vector<>::iterator 型のイテレータでは、アロー演算子 (->) を使うことで、そのイテレータが示す位置にある値のポインタを取り出すことができるようになっています。このイテレータの値を足したり引いたりすることで、位置を前後に移動することもできます。
    std::vector<>::reverse_iterator 型のリバースイテレータは、通常のイテレータの逆順に動作するイテレータです。

    また、std::vector<> を const 指定で使用している場合、これらのイテレータは読み取り専用のものが取得されます。
    その時に begin メソッドや end メソッドなどで取得できるイテレータの型は普段とは異なり、それぞれ std::vector<>::const_iterator と std::vector<>::const_reverse_iterator になります。

    特定の要素の操作

    iterator erase(iterator pos)
    iterator erase(iterator start, iterator end)
    イテレータで指定した pos 位置の、または start から end までの要素を削除します。戻り値は削除された要素の次を示します。
    iterator insert(iterator pos, const TYPE& value)
    void insert(iterator pos, size_t num, const TYPE& value)
    イテレータで指定した pos 位置に値 value を挿入します。このとき num が指定されていれば、
    イテレータで要素の位置を指定することで、それが示す要素を削除したり、そこに新しい要素を追加したりすることもできます。

    要素の交換

    void swap(std::vector<>& source)指定された std::vector<> の要素と、自身が保持する要素とを入れ替えます。
    2 つの std::vector<> の要素を交換するメソッドも用意されていました。

    使用できる演算子

    一致の判定

    ==ふたつの std::vector<> が一致するかを調べます。要素は == 演算子を使って評価されます。
    !=ふたつの std::vector<> が一致しないかを調べます。要素は == 演算子を使って評価されます。
    std::vector<> での一致判定は、2 つの std::vector<> の .size() が一致するかどうかと、同じインデックス番号の要素がそれぞれ全て一致するかで判定されます。
    全てを満たす時に true、どれかが満たされない場合に false となります。
    このとき、== でも != でも、要素ごとの判定は "==" が使われるようでしたが、処理系に依存するかもしれません。また、判定は条件が満たされなくなったところで打ち切られるようでした。

    順位の判定

    <=左の std::vector<> が右の std::vector<> 以下かを調べます。
    >=左の std::vector<> が右の std::vector<> 以上かを調べます。
    <左の std::vector<> が右の std::vector<> 未満かを調べます。
    >左の std::vector<> が右の std::vector<> より大きいかを調べます。
    std::vector<> での順位判定は、std::lexicographically_compare 関数のような自書式順序で比較が行われるそうです。
    辞書式順序というのは Wikipedia: 辞書式順序 にも詳しく記載されていますが、C 文字列の比較 strcmp と同じと思うと判りやすいと思います。つまり > や < などで途中まで一致していても、最後の要素で大小関係が正しく成り立っている場合は true になります。
    これを判定するとき、内部ではどうやら要素を比較するとき、与えられた std::vector<> の順序とそれを逆順にしたのとのそれぞれで < 演算を行うことで、小さいのか、大きいのか、どちらでもない(等しい)のかを判定しているようでしたが、処理系に依存するかもしれません。また、判定は条件が満たされなくなったところで打ち切られるようでした。

    要素の操作

    []std::vector<> の指定されたインデックス番号の要素を取得します。
    =左の std::vector<> に、右の std::vector<> を代入します。このとき、要素は新しいものが作られてコピーされます。
    std::vector<> では [] 演算子を使って指定した要素を取り出せるようになっています。
    ただし、存在する要素の範囲を超えたインデックスを指定すると、[] の場合は不正な値を取得して処理を続けてしまいます。
    確実に範囲内にある場合はいいでしょうけど、確実でない場合は at() メソッドを使って取得する方が、範囲外にアクセスしたときに out_of_range 例外が発生するので安全です。

    代入演算子では、左に指定した std::vector<> に右の要素を代入します。
    このとき、もともとあった要素は全て破棄されて、新しく代入される要素はコピーコンストラクタを使って新しい別の値として代入されます。

    □ 要素をインデックスを指定して取得するとき

    std::vector<> では、インデックス番号を使って要素を取得することができます。
    このとき、std::vector<> では [] 演算子を使って要素を取得することもできますが、at() メソッドを使用した方が安全です。
    int value = vector.at(5);
    指定したインデックスが、std::vector<> が持っている要素を超えた場合、[] 演算子では不正な値を取得して処理が続行されますが、at() メソッドを使用した場合は out_of_range 例外が発生するようになります。
    指定するインデックスが、既存の要素を超えないことがあまりにも確実な場合は、もしかすると [] 演算子の方が高速なのかもしれないですけど、処理速度の効率化が余程必要なところでなければ、at() メソッドを使っておいた方が無難です。

    □ std::vector<> でクラスインスタンスを扱う場合

    クラスの値を扱う場合

    std::vector<> ではクラスインスタンスも扱うことができます。
    std::vector<CMyClass> vector;
    たとえばこのようにすることで、CMyClass 型を扱う std::vector<> を使えます。
    この時、push_back メソッドで挿入されるインスタンスは、通常はコピーコンストラクタを使って生成された新しいインスタンスになります。poo_back メソッドで削除されたインスタンスは、デストラクタが呼び出されます。

    ここで気になるのが、push_back メソッドを実行した時です。
    このとき vector に既に値が格納されていた場合、それらの値が全てコピーコンストラクタを使って生成し直されます。
    コピーにそれほどコストがかからないクラスならいいのですけど、メモリを大きく使用するクラスなどの場合は、要素が追加されるたびに全て生成し直されるのは都合が悪い場合があります。

    これについては C++11 で実装されたムーブコンストラクタを実装することで、コピーにかかる負荷を最小限に抑えることができます。
    それについては 右辺値参照とムーブコンストラクタの使い方 に詳しくまとめておきます。

    クラスのポインターを扱う場合

    クラスのポインターを持つ std::vector<> を作成した場合は、そこに格納したポインタが指すインスタンスの管理は自分で行う必要があります。
    std::vector<CMyClass*> vector;
    この場合は、push_back メソッドでの代入時のインスタンス確保と、pop_back メソッドでの破棄前に、必要なインスタンスの構築や破棄を適切に行う必要があります。
    その辺りについては 可変長配列 std::vector でインスタンスのポインタを扱っている場合の削除方法 の方に整理しておきました。

    □ イテレータを使って繰り返し処理を行う

    std::vector<> ではイテレータを使って繰り返し処理を行えます。
    このとき、イテレータの型は std::vector<>::iterator になり、これに整数を足したり引いたりすることで任意の場所へ簡単に移動することができるようになっています。
    const 指定の std::vector<> の場合には、イテレータは std::vector<>::const_iterator を使います。
    // イテレータを格納する変数を準備します。
    std::vector<CMyClass>::iterator iterator;

    // 最初のイテレータから、最後のイテレータになるまで、イテレータを一つずつ先に進めます。
    for (iterator = vector.begin(); iterator != vector.end(); ++iterator)
    {
    // ここで、要素毎の操作を行います。
    CMyClass* element = &(*iterator);

    }
    このように、イテレータを for 構文の中で使用して、最初の要素の先頭を使って初期化してから、最後を示すイテレータが現れるまでの間、イテレータをひとつひとつ進めることで、全ての要素を繰り返し処理しています。
    このとき、end メソッドで取得できる最後のイテレータは、最後の要素の次の位置を示しているので、end メソッドによって得られたイテレータと一致したタイミングでループを抜けられます。

    □ std::vector<> を並び替える

    std::vector<> の配列を並び替えたい場合には <algorithm> に定義されているソート関数を使用します。
    std::sort(vector.begin(), vector.end());
    このようにすることで、引数に渡した std::vector<> の要素が昇順に並べ替えられます。
    並び替えの判定には、要素間で < 演算子を使って判断されるようでした。また、この関数を使った場合は、値が一致した場合の並び順がどうなるかは決まっていないそうです。
    単なる数値のように値が決まるような場合は、値が一致した場合の並び順は考えなくていいですけど、クラスのインスタンスのようにその他の情報も入ったオブジェクトとしてみた場合には、不都合になる場合があります。

    値が一致したときに、それらの順番は元のものを維持したい場合は std::stable_sort 関数を使います。
    std::stable_sort(vector.begin(), vector.end());
    これにより、値が一致した場合の並び順が、元の順番と同じになります。
    こちらも、並び替えの際には要素間で < 演算子を使って判断されます。

    ちなみに、std::sort でも std::stable_sort でも、並び替えの処理中に、要素のコピーコンストラクタが呼ばれたり、代入演算子が実行されたりするようでした。
    コピーの負荷が気になる場合は、push_back のときと同じように、扱うクラスにムーブコンストラクタを実装することで、負荷を最低限に抑えることができます。ムーブコンストラクタについては 右辺値参照とムーブコンストラクタの使い方 に詳しくまとめてあります。

    扱う要素がクラスのポインタの場合には、要素の順番は変わっても破棄されることはないので、ポインタが指すインスタンスの確保や管理の心配は必要ありません。

    □ std::vector<> を右辺値参照で移譲する

    C++11 では、一時変数から保存用の変数に std::vector<> を移すようなときに、不必要なコピー処理を行わなくて済むようにするための右辺値参照という仕組みが規定されました。
    それについては 右辺値参照とムーブコンストラクタの使い方 で詳しく紹介してありますが、std::vector<> も右辺値参照を引数に取る代入演算子とムーブコンストラクタが用意されているので、簡単に委譲することができます。
    std::vecotr<CMyClass> newVector = std::move(oldVector);
    このように、右辺を std::move 関数を使って明示的に右辺値参照とすることで、今回の例では oldVector に格納されている要素をそのまま newVector に移動させることができます。
    このとき、右辺に指定した oldVector の内容は破壊されるので、これ以降は使えなくなります。

    実際にこれを行ってみると、oldVector が持っている要素をひとつも破棄することなく、newVector に持たせる要素をひとつも新規生成することなく、std::vector<> の値を oldValue から newValue へ移行することができました。
    特に std::vector<> でクラスを扱っている場合はコピーのコストが大きくなりがちなので、可能なところは積極的に使って行くのが良さそうです。
    ちなみに、関数の戻り値で受け取ったクラスを変数に受けるときは、戻り値は自動的に右辺値参照として扱われるため、明示的に std::move 関数を呼び出さなくて大丈夫です。




    2015年7月20日月曜日

    OpenCV Mat と Eigen の相互変換

    覚書として。参考サイトからの引用です。



    cv::MatとEigen::Matrixの相互変換

    cv::MatからEigen::Matrixへの変換
    #include <iostream>
    #include <Eigen/Core>
    #include <opencv2/core/core.hpp>
    #include <opencv2/core/eigen.hpp>
    
    int
    main(int argc, char *argv[])
    {
      // 3x3, double の行列
      Eigen::Matrix3d eigen_mat;
      eigen_mat << 1.1, 1.2, 1.3,
                   2.1, 2.2, 2.3,
                   3.1, 3.2, 3.3;
      cv::Mat cv_mat;
      // convert from Eigen::Matrix to cv::Mat
      cv::eigen2cv(eigen_mat, cv_mat);
    
      std::cout << "cv_mat:\n" << cv_mat << std::endl << std::endl;
      std::cout << "eigen_mat:\n" << eigen_mat << std::endl << std::endl;
    }
    
    実行結果:
    cv_mat:
    [1.1, 1.2, 1.3;
      2.1, 2.2, 2.3;
      3.1, 3.2, 3.3]
    
    eigen_mat:
    1.1 1.2 1.3
    2.1 2.2 2.3
    3.1 3.2 3.3
    
    Eigen::Matrixからcv::Matへの変換
    #include <iostream>
    #include <Eigen/Core>
    #include <opencv2/core/core.hpp>
    #include <opencv2/core/eigen.hpp>
    
    int
    main(int argc, char *argv[])
    {
      // 3x3 の行列
      cv::Mat cv_mat = (cv::Mat_<double>(3,3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);
      Eigen::Matrix<double, 3, 3> eigen_mat1; // コンパイル時に行列サイズが既知の場合
      // convert from cv::Mat to Eigen::Matrix
      cv::cv2eigen(cv_mat, eigen_mat1);
    
      std::cout << "cv_mat:\n" << cv_mat << std::endl << std::endl;
      std::cout << "eigen_mat1:\n" << eigen_mat1 << std::endl << std::endl;
    
      Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> eigen_mat2; // 動的な行列
      // convert from cv::Mat to Eigen::Matrix
      cv::cv2eigen(cv_mat, eigen_mat2);
      std::cout << "eigen_mat2:\n" << eigen_mat2 << std::endl << std::endl;
    }
    
    実行結果:
    cv_mat:
    [1, 2, 3;
      4, 5, 6;
      7, 8, 9]
    
    eigen_mat1:
    1 2 3
    4 5 6
    7 8 9
    
    eigen_mat2:
    1 2 3
    4 5 6
    7 8 9
    

    参考
    cv::Matの基本処理
    http://opencv.jp/cookbook/opencv_mat.html#cv-mateigen-matrix

    2015年7月19日日曜日

    米の山火事上空に撮影試みる無人機、衝突恐れヘリ消火中断

    あかんですね。アメリカ・日本ともに法律が厳しくなりそう。

    http://news.livedoor.com/article/detail/10367568/



    米カリフォルニア州フェラン(CNN) 米カリフォルニア州南部のサンバーナディーノ郡で17日発生し、高速道路にも火勢が達し多数の乗用車などが燃える被害が出た山林火災で、地元の消防当局者は18日、至近距離からのビデオ撮影を試みる無人航空機(ドローン)5機が現場上空に出現したため、ヘリコプターによる消火活動が一時中断されたと報告した。
    煙が立ちこめる視界不良の中で空中衝突などの不測の事態を回避するため、放水するヘリの出動は最大20分妨げられたとしている。
    同郡の消防当局者はドローンの邪魔がなければ最大20分間の消火活動ができ、住民らへの危険が減じていたかもしれないと述べた。ドローン5機はその後、立ち去り、ヘリの消火作業は再開されていた。
    カリフォルニア州では山林火災が発生した場合、現場上空にドローンが出現する例が最近目立ち始めている。消防当局は18日、山林火災の現場へ趣味でドローンを飛ばす行為を非難。米森林局の報道担当者もこの種の行為をやめるよう訴えた。
    当局が今回のドローン騒ぎへの調査を開始するのかは不明。米連邦航空局(FAA)の報道担当者によると、FAAはサンバーナディーノ郡での山林火災を受け、現場周辺を一時的な飛行規制下に置いた。許可なければ無人機も飛ばせないことを意味していた。
    危険な方法で無人機を飛ばしたことなどが判明した場合、1000~2万5000ドル(約12万4000~310万円)の民事上の罰金を科す可能性もあるとしている。
    今回の山林火災は、ラスベカスとロサンゼルスを結ぶ州間道15号線にも達し、車両多数が燃え、運転手らが逃げ惑う混乱も呈していた。消防当局によると、火勢は18日になっても衰えず、焼失面積は約14平方キロに達し、鎮火したのはこのうちの5%のみとなっている。火災で死傷者発生の報告はない。

    2015年7月18日土曜日

    OpenCV 特徴点検出+マッチング

    OpenCVにおけるマッチングは次のプロセスを経て実現されます。

    特徴点抽出
      ↓
    特徴量記述の計算の実行
      ↓
    マッチング

    少し突っ込めば、さらに双方向マッチングの実施+マッチング距離の閾値以下の除去があります。それを入れると、

    特徴点抽出
      ↓
    特徴量記述の計算の実行
      ↓
    双方向マッチング
      ↓
    マッチング距離の閾値以下の除去

    それでは、一気にコードを見てみましょう。(ORBを使用)

    // FeatureDetectorオブジェクトの生成
    Ptr<FeatureDetector> detector = new ORB(80, 1.25f, 4, 7, 0, 2, 0, 7);

    // DescriptionExtractorオブジェクトの生成
    Ptr<DescriptorExtractor> extractor = DescriptorExtractor::create("ORB");

    // DescriptorMatcherオブジェクトの生成
    Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("BruteForce-Hamming");

    // 特徴点情報を格納するための変数
    vector<KeyPoint> keypoints1;
    vector<KeyPoint> keypoints2;

    // Extract feature points
    detector->detect(img1, keypoints1);
    detector->detect(img2, keypoints2);

    // 画像の特徴情報を格納するための変数
    Mat descriptor1;
    Mat descriptor2;

    // compute the feature discriptor
    extractor->compute(img1, keypoints1, descriptor1);
    extractor->compute(img2, keypoints2, descriptor2);

    // 特徴点のマッチング情報を格納する変数
    vector<DMatch> dmatch;

    // compute matching
    matcher->match(descriptor1, descriptor2, dmatch);

    // Extract matching which has high matching rate
    int count = dmatch.size();
    int matchCount = 0;

    double min_dist = DBL_MAX;
    for (int i = 0; i < (int)dmatch.size(); i++) { // Acquire the minimum distance
    double dist = dmatch[i].distance;

    if (dist < min_dist) {
    min_dist = dist;
    }
    }
    if (min_dist < 1.0)
    {
    min_dist = 1.0;
    }

    double threshold = 30; // Fixed value is better than value times min_dist

    for (int i = 0; i < (int)dmatch.size(); i++) {
    if (dmatch[i].distance < threshold) {
    matchCount++;

    dmatchClose.push_back(dmatch[i]);
    }
    //std::cout << dmatch[i].distance << "\t" << min_dist << "\t" << threshold << std::endl;
    }




    参考:
    OpenCVで画像の特徴抽出・マッチングを行う
    http://whoopsidaisies.hatenablog.com/entry/2013/12/07/135810
    OpenCVでのORBによる特徴点抽出とマッチング(その1)基本的な使い方
    http://independence-sys.net/main/?p=2632

    OpenGLでGLUTを使って2つのウィンドウを制御する①
    http://blog.goo.ne.jp/omty/e/c8a5aee50e3918a1ad669472ec25e462
    サブウィンドウ
    http://seesaawiki.jp/w/mikk_ni3_92/d/%c8%af%c5%b8%ca%d403

    PixelBufferObject
    http://seesaawiki.jp/w/mikk_ni3_92/d/PixelBufferObject

    Visual Studioでデバッグ時にOpenCVのMat等の画像を表示できるプラグインが便利
    http://whoopsidaisies.hatenablog.com/entry/2014/11/28/175637



    OpenCV イメージデータ(Mat)のコピー(クローン)

    OpenCVのイメージデータをコピーしようとしたら、できない?なんでと思って結構悩んでしまいました。よく考えてみると当たり前なのですが。

    そもそも、OpenCV のMat型というのものは、次のようになっている(結構略)。
    サイズとかデータの型の情報:実体
    画像のデータ:ポインタ

    はぅ!!画像のデータも実体で入っているものとばかり思い込んでいた。

    ということで本題。

    void func(cv::Mat getImage){
        static cv::Mat imageInFunc;
        imageInFunc=getImage;
        return;
    }

    ってな感じで書いても、ポインタを代入しているだけなのでデータは保存されていない。元のデータが書き変われば、imageInFuncを表示させてみても、データは書き換わり続ける。

    で、どうすればいいかというと。cloneってものがあるのね。

    void func(cv::Mat getImage){
        static cv::Mat imageInFunc;
        imageInFunc=getImage.clone;
        return;
    }

    で解決。あほなことに2時間ほど費やしてしまった。

    参考:
    OpenCVでまずやること『Mat』
    知っておきたいMatのゴリ知識
    cv::Matの基本処理