27 Oct 2013, 02:40

自由自在にコード内を飛び回る!Eclipseのコードリーディング機能が便利

Eclipseには、コードを読みやすくするための様々な機能が備わっています。

GUIだからマウスをたくさんつかうんだろうという迷信がありますが、実際はキーポードから手を離さなぽことがおおいです。

キーバインドを巧みに利用して、コード内を自由自在に飛び回るためのテクニックを紹介します。

[toc]

[//www.youtube.com/embed/db4vCH7oqG8?rel=0]

宣言を開く(F2,F3)

関数や宣言の場所へジャンプすることができます。いわゆる、タグジャンプ、EmacsのGNU Grobalのようなもの。もっともよく利用する機能です。

F3で飛んで、Alt + 左で元に戻る。これでソースコード内をマウスを利用せずにピョンピョンと移動することができます。

F2を押すと、飛ばずにポップアップで宣言の場所を表示することができます。

呼び出し階層を開く(Ctrl+Alt+h)

関数がメソッドがどこの関数から呼ばれているかを順々に表示してくれる機能。

リファクタリングの時の修正による影響範囲を調査する時、重宝する機能。

型階層を開く(F4)

クラスの型を階層的に表示てくれる機能。継承関係が分かる。

C++だと機能するけれども、Cだとよくわからない機能。あまり利用しない。

エディタバッファを開く(Ctrl + x + b)

ファイル間の移動は、ショートカットで新規にファイルを開くこともあるけれども、もともと開いているファイルに飛ぶこともデキる。

このキーバインドは、Emacsキーバインドを設定しているので、Emacsに慣れている人はそのキーバインドをそのままEclipseでも利用できる。すなわち、Ctrl + x + b。

Alt + 左で前へ戻り、Alt + 右で次へ進むことができる。もはやブラウザでWebページを見るような感じ。

検索で飛ぶ

デフォルトのEclipse CDT検索機能は利用しません。代わりに、プラグインを利用します。

検索結果をクリックするだけで検索箇所へジャンプできるので、コンソールからgrepして検索するよりも効率がよいです。

26 Oct 2013, 17:01

モダンディでイケイケなTDDの最新動向が集結!『Modern C++ Programming with Test-Driven Development』

『Modern C++ Programming with Test-Driven Development』をだいたい読み終わりましたので、感想を書きます。

各章の概要

各章の概要と、学習メモは以下のエントリを参照。

初めは懇切丁寧にTDDの基礎が述べられているので、モダンだからと言ってもTDD実践入門に最適。 使われているフレームワークは、

  • GoogleMock
  • CppUTest

Mockに関する説明は少なかったように思う。一部のテクニックを書くのではなくて、まんべんなく書かれているイメージ。

また、コンパイラはC++11を使っていて、見慣れないC++の文法がたくさん出てきたのが少しつらかったところ。このへんも、モダン。

写経について

この本は、ところどころ写経しながら読みました。写経環境の導入手順は以下にまとめました。あとから思うと、Ubuntuが一番楽じゃないかと思う。

電子書籍なので、本からウェブ上のソースコードにリンクが張ってあります。すべてをそのまま写したところもありますが、ちょっと手を抜いて、サンプルコードをコピペしながら読んだところもあります。 サンプルコードを実際に動かしたり、diffを取ったりしながら読んだほうが、理解が深まりますね。

感想

モダン!( ー`дー´)

この名に恥じない内容だった。新しいテクニックや考え方にワクワクして、明治時代の人がザンギリ頭に触れる喜びを感じた。

西洋のTDD界ではkataだとかdojoだとかMikadoだとかがブームだというおかしなジャポニズムを初めて知った。そんなニッポンはShakyoがブーム?この本を通してKataを知ったのは収穫だった。今後の勉強の進む方向はコッチだ。

個人的には、『test driven development for embedded c』に続く良書。ただし、かつての感動はなかった。もともと、前半の内容は知っていることが多かったので。C++というのも、Cに比べると疎遠なので。

サブタイトルが『Code Better, Sleep Better』となっている。

Code Betterは、リファクタリングについての話題が多め。実際のサードパーティから依存関係を取り除いたり、実際のレガシーコードを利用したリファクタリングしたりする。理想的なTDDなどありえなくて、現実はとてつもないレガシーコードと戦わなければいけない。現実に則した本だ。我らの味方だ。

Sleep Betterがなにを暗示しているのかワカラなかったので、そろそろ寝ます。おやすみなさい(´-ω-`)

26 Oct 2013, 16:02

『Modern C++ with TDD』学習メモ(Chapter6-11)

『Modern C++ with TDD』後半分の学習メモです。前半部分は、ココ。

『Modern C++ with TDD』学習メモ(Chapter2-5) | Futurismo

各章の概略

  • Chapter6: Incremental Design 主にリファクタリングの話題。どうやってシンプルなコードを作っていくかを実践する。1行でも関数として抽出する。インクリメンタルに、デザインを作り上げていくその開発スタイルが『モダン』といっている。
  • Chapter7: Quality Tests テストの書き方について。FIRSTの原則の紹介。一つのテストでひとつのことを検証する、テストの名前付に慎重になる。そして、テストコードのリファクタリングの実践、ほかTips。
  • Chapter8: Legacy Challenges 実際のレガシーコードを使ったリファクタリング体験。いきなり200Stepsのうんこメゾッドが出される。ここでは、CppUTestを使う。テクニックは『レガシーコード改善ガイド』に載っているもの。この章は,必読。涙がでるほど素晴らしかった。
  • Chapter9: TDD and Threading マルチスレッドのプログラムに対するTDDのアプローチの紹介。正直、ここは理解できなかった。
  • Chapter10: パフォーマンスや受け入れテスト、TPPなどの、TDDを発展させた話題を取り扱う。
  • Chapter11: Growing and Sustaining TDD おまけのような、未分類の小さな話など。Code Jodo、マネージャーへのTDDの説得方法、参考リンクなどなど。

StudyMemo

Small Methods

一行の論理でもメソッドとして抽出する。その理由は、

  • Methods名が機能を現してコメント代わりになる。
  • 重複を排除するためのスタートになる。
  • パフォーマンスは瑣末なものだ。

Chapter6は 1990年代では、奇妙なことがよいこととされてきた、と結論づけられる。いまは、このような過剰なリファクタリングは奇妙に見えるけれども、20年後はこれ普通になるのだよ、と。

Chapter10では、実際に簡単な数値実験が行われる。inlineメソッド、Smallメソッド、inline無効にした場合で実験。inlineメソッドとSmallメソッドのパフォーマンスはほぼ同じ、だけどinline無効メソッドは時間がかかる。

最近のコンパイラは頭がいいから、小さな関数は勝手に最適化してくれる。なので、Smallメソッドはパフォーマンスを阻害しないと。他人のいうことに耳を傾けてはいけない、彼らは同じことをいうだけなので。

ここにも、数値実験があった。このリンクの記事はリファクタリングについてよく語っていて興味深い。数値をみると、なるほどと思う。

自分でも実験してみた。だいたい、変わんない。

パフォーマンスが懸念だが、そもそもパフォーマンスがネックというのは迷信なのか?オーバーヘッドは無視していいのか?未来は、パフォーマンスは瑣末な問題になるのだろうか?

コードにコメントを書くことについても、『コード自身が語っているのに、なんで冗長なコメントを書くの』と。

FIRSTの原則

良いテストは以下の5つに従う。

  • F First
  • I Isolated
  • R Repeatable
  • S Self-Verifiying
  • T Timely

Mikado Method

ミカドメソッドとは、大規模なコードをリファクタリングするための方法論。 別記事にまとめました。

Code Kata

これは別記事にまとめました。

Transformation Priority Premise

ボブおじさんが考案した、TDDをより簡単に実施するための方法。

“Uncle Bob” Martin – The Transformation Priority Premise from 8th Light on Vimeo.

26 Oct 2013, 15:29

コーディングをもっともっと加速する!Eclipseのコード補間機能まとめ

Eclipse CDTの強力な(Javaに比べると見劣りする)単語補間、コンテンツ・アシスト機能を紹介します。

これで、超高速なコーディングが可能??

単語補間

途中まで単語を入力したあとに、単語補間を実行すると、エディタ内の似ている単語で補間してくれる。

ちなにみ、単語補間は Alt+/に割り当てて、コンテンツ・アシストは Ctrl+Spaceに割り当てている。キーバインドが競合していると、補間が発動しないので、注意すること。競合していたときは、検索窓から Alt+/で競合コマンドを検索して、アンバインドする。

コンテンツ・アシスト

設定のカスタマイズは、以下を選択。

  • 設定 -> C/C++ -> エディタ -> コンテンツ・アシスト

自動有効化でトリガを設定できるので、とりあえずすべてチェックを入れる。トリガが発動するまでの時間も100ms以下にすると、高速でアシストが発動する。

構造のメンバだったり、関数名だったりを、これでガンガン置換できます。

ちなみに、コンテンツ・アシストで出てくるコードテンプレートは自分でも作成できます。以下の記事参照。メタプログラミングが可能です。

Javaならば、Code Recomennderという強力なプラグインがあるものの、CDTにはない。

クイック・フィックス

赤バツが表示されている時に、どう修正すればいいかを教えてくれる。

Ctrl+,でエラー箇所に飛んで、Ctrl+1を次々に実施してエラー箇所を修正。

Javaに比べると、CDTのクイックフィックスは貧弱。定義がないメソッドがあった場合、Javaならコード生成までしてくれるが、CDTはそこまではしてくれない。

パラメータのヒント

これはおまけのような機能。

引数の型をわすれたときにはこれ。ポップアップで教えてくれる。

26 Oct 2013, 12:54

組込み開発の二大迷信に挑む!リファクタリングにおけるパフォーマンスとスタックオーバーフローについての数値実験

組込み開発において、リファクタリングしようとすると、自分は怒られる。

  • パフォーマンスが低下する
  • スタックオーバーフローする

怒られるのが嫌で、リファクタリングできない。この2大迷信について、簡単な実験してみた。

Normalコード(Test1)

これは普通のコード。ここを参考にした。clock

[tsu-nera]% cat timer.c
#include <time.h>
#include <stdio.h>

int main(void)
{
  clock_t start, end;
  long l;
  long i=0;
  int  n=0;
  clock_t total_start, total_end;

  total_start = clock();

  while(n < 5){

    start = clock();
    i = 0;

    for (l=0; l<100000000; l++) {
      i++;
    }

    end = clock();
    printf("ループ1億回の時間: %f秒\n", (double)(end - start) / CLOCKS_PER_SEC);
    n++;
  }

  total_end = clock();

  printf("平均の時間: %f秒\n", (double)(total_end - total_start) / CLOCKS_PER_SEC / n);

  return 0;

}

Functionコード(Test2)

つづいて、関数でインクリメントを抽出したコード。

[tsu-nera]% cat timer2.c
#include <time.h>
#include <stdio.h>


void incriment(long* i) {
  (*i)++;
}

int main(void)
{
  clock_t start, end;
  long l;
  long i=0;
  int  n=0;
  clock_t total_start, total_end;

  total_start = clock();

  while(n < 5){

    start = clock();
    i = 0;

    for (l=0; l<100000000; l++) {
      incriment(&i);
    }

    end = clock();
    printf("ループ1億回の時間: %f秒\n", (double)(end - start) / CLOCKS_PER_SEC);
    n++;
  }

  total_end = clock();

  printf("平均の時間: %f秒\n", (double)(total_end - total_start) / CLOCKS_PER_SEC / n);

  return 0;

}

実験結果

気持ち、Test1のほうが早い気がする。もっと実験すれば、大数の法則で正確な値がでそうだけど、まあいいや。とりあえず、気持ちの問題で小さな関数はパフォーマンスをあまり低下させないという自身がついた。

Test1

[tsu-nera]% ./a.out
ループ1億回の時間: 1.050000秒
ループ1億回の時間: 0.720000秒
ループ1億回の時間: 0.670000秒
ループ1億回の時間: 1.050000秒
ループ1億回の時間: 1.170000秒
平均の時間: 0.932000秒
[tsu-nera]% ./a.out
ループ1億回の時間: 1.030000秒
ループ1億回の時間: 0.960000秒
ループ1億回の時間: 0.960000秒
ループ1億回の時間: 0.770000秒
ループ1億回の時間: 0.680000秒
平均の時間: 0.882000秒
[tsu-nera]% ./a.out
ループ1億回の時間: 1.250000秒
ループ1億回の時間: 0.840000秒
ループ1億回の時間: 0.670000秒
ループ1億回の時間: 0.570000秒
ループ1億回の時間: 0.630000秒
平均の時間: 0.792000秒
[tsu-nera]% ./a.out
ループ1億回の時間: 1.030000秒
ループ1億回の時間: 0.730000秒
ループ1億回の時間: 0.630000秒
ループ1億回の時間: 0.520000秒
ループ1億回の時間: 0.460000秒
平均の時間: 0.674000秒
[tsu-nera]% ./a.out
ループ1億回の時間: 0.940000秒
ループ1億回の時間: 0.720000秒
ループ1億回の時間: 0.630000秒
ループ1億回の時間: 0.540000秒
ループ1億回の時間: 0.500000秒
平均の時間: 0.666000秒

Test2

[tsu-nera]% ./a.out
ループ1億回の時間: 1.010000秒
ループ1億回の時間: 0.810000秒
ループ1億回の時間: 0.650000秒
ループ1億回の時間: 0.560000秒
ループ1億回の時間: 0.480000秒
平均の時間: 0.702000秒
[tsu-nera]% ./a.out
ループ1億回の時間: 1.470000秒
ループ1億回の時間: 1.160000秒
ループ1億回の時間: 0.930000秒
ループ1億回の時間: 0.700000秒
ループ1億回の時間: 0.590000秒
平均の時間: 0.970000秒
[tsu-nera]% ./a.out
ループ1億回の時間: 1.050000秒
ループ1億回の時間: 0.870000秒
ループ1億回の時間: 0.710000秒
ループ1億回の時間: 0.770000秒
ループ1億回の時間: 1.270000秒
平均の時間: 0.934000秒
[tsu-nera]% ./a.out
ループ1億回の時間: 1.200000秒
ループ1億回の時間: 0.890000秒
ループ1億回の時間: 0.710000秒
ループ1億回の時間: 0.650000秒
ループ1億回の時間: 0.540000秒
平均の時間: 0.798000秒
[tsu-nera]% ./a.out
ループ1億回の時間: 1.020000秒
ループ1億回の時間: 0.760000秒
ループ1億回の時間: 0.620000秒
ループ1億回の時間: 0.550000秒
ループ1億回の時間: 0.560000秒
平均の時間: 0.704000秒

スタックオーバーフローのテスト(Test3)

関数を呼びすぎるとスタックオーバーフローするよと脅されたが、脅した人はどのくらいの確信をもって発言したのかをテストした。

こんなサンプル。

#include <stdio.h>

void incriment(long* i) {
  (*i)++;
  printf("i=%d\n",*i);
  incriment(i);
}

int main(void)
{
  long  i=0;
  incriment(&i);
  return 0;

}

テスト結果

大体、自分のCygwin環境だと400000くらいでクラッシュする。

i=392880
i=392881
i=392882
[1]    18958 segmentation fault  ./a.out

ただ、スタックオーバーフローは、タスクサイズと環境依存なので、一概に安心はできないな。3階層くらいならば、へのようなものか(・・?

結論

関数抽出しても、コンパイラが最適化してくれるため、パフォーマンスは気にしない。小さな関数はコンパイラがinlineしてくれる。20年前の常識は、現代の非常識。

ただし、スタックオーバーフローは注意を払う。

26 Oct 2013, 10:27

大規模コードをリファクタリングする方法『ミカドメソッド(Mikado Methood)』について

TDDの技法で、mikado method(ミカドメソッド)という、なにやら怪しい名前の方法が流行ってるらしいので、調べてみた。

ミカドメソッドとは

ミカドメソッドとは、大規模なコードをリファクタリングするための方法論。

本もある。

動画もある。

Large-scale refactorings using the Mikado Method – Ola Ellnestam & Daniel Brolund from Øredev Conference on Vimeo.

[//www.youtube.com/embed/OTccRWjriOM?rel=0]

 ミカドメソッドの由来

Mikado(ミカド)メゾッドの名前は、ヨーロッパでポピュラーなテープルゲームが由来らしい。ミカドは、天皇(帝」である。

ちなみに、日本をバカにした『ミカド』というオペレッタもあるというのはどうでもいい雑学。

ミカドメソッドの方法

以下のステップに従う。

  1. ミカドゴール(最終到着目標)を決める
  2. おバカな方法でまずはゴールを実装する。
  3. エラーをたくさんみつける。
  4. 速やかにエラーを除去する。
  5. 新しい解決方法を必要条件として導き出す。
  6. エラーが取れなかったら、初めのスタート地点に戻る。
  7. 2から6を繰り返していく。
  8. エラーがなくなったか確認。

数学用語の『必要条件』を利用して目標を展開していく。PはMikadoの必要条件。

Mikado => P

MikadoのためにはA、B、Cが必要。Bのためには、E、Fが必要など。必要条件の展開は、ミカドグラフ?に描いていく。

Micado
======
| | |
A B C
  |
 E F

いくらでも失敗してよい、失敗したら初めに戻ればよい。はじめに戻る方法は以下。

git reset --hard && git clean -f

成功した道をミカドグラフに残していく。この失敗を恐れぬ大胆な挑戦と、確実な道筋を描いていくことが、大規模リファクタリングに適しているのだとか。あまり、書籍で読んだだけでは実感がわかないな。

ということで、練習用のリポジトリがgithubにある。練習方法は以下の記事が参考。この記事を書いているときは、やっていないけれども、あとで挑戦してみる。

26 Oct 2013, 01:22

Eclipseが立ち上がらないときは.logを見て調査する

Eclipseが立ち上がらなくなったので、調査方法と解決方法をメモ

とりあえず eclipse.exe -cleanで

運が良ければ、cleanオプションをつけてEclipse再起動すればなおる。

> eclipse.exe -clean

eclipse.exeが置いてあるフォルダのeclipse.exe -clean.cmdを実行すれば良い。

Eclipse Errorログを見る

Eclipseのエラーログは以下にある。

(YourWorkspace)/.metadata/.log

問題が起こったら、とりあえずログを見て調査する。該当時間のメッセージをググる。

Eclipse コンソールモードで立ちあげ。

eclipse.exeが置いてあるフォルダのeclipsec.exeを実行すると、コンソールアプリケーションとして起動することができる。

そこに現れる標準出力をググる。

ビューの設定をリセット

今回の原因。

!MESSAGE Job found still running after platform shutdown.  Jobs should be canceled by the plugin that scheduled them during shutdown:

以下のファイルを削除すればよいようだ。

(YourWorkspace)\.metadata\.plugins\org.eclipse.e4.workbench\workbench.xmi

これは、Eclipseの起動状態を管理しているファイルのよう。ビューが開けなくなったときも、このこの workbench.xmiを削除(orリネーム)して、-clean コマンドをつけて再起動すれば良い。

25 Oct 2013, 04:14

TopCoder用テンプレート(C++)

TopCoder用テンプレート

自分のテンプレートを公開。随時追加中。

補助関数

よくつかう補助関数をまとめます。補助関数を利用するには、 以下をインクルード。

#include<algorithm>
using namespace std;

最大・最小

int = 1, int j = 2;

// 最大値
rslt = max(i,j); // rslt = 2

// 最小値
rslt = min(i,j); // rslt = 1

スワップ

ポインタを指定する必要はありません。sortの実装の時などに。

// 交換
swap(i,j) // i=2,j=1

25 Oct 2013, 04:06

C++での動的配列(set,map)の使い方まとめ

C++での動的配列(set,map)の使い方についてまとめます。

setの使い方

setとは、自動でソートされる動的配列を扱うためのSTLです。

数学でいうところの”集合”に対応するものですが、setは要素数が有限です。

宣言

#include <set>
using namespace std;

set <string> s;

関数

要素数を調べる
s.size();
要素を追加する
s.insert("hoge");

同じ要素を追加しても、要素数は1つのまま。

s.insert("hoge");
s.insert("hoge");
s.size(); // = 1
要素を検索する
s.find("探すもの")

if( s.find( "hoge" ) == s.end() )
{
    cout << "Not Found" << endl;
}
else
{
    cout << "Find" << endl;
}
要素をすべてクリアする
s.clear();

よくある使い方

文字列Sのなかの文字の要素数を調べる。

int getNumber(string S) {

  set <string> s;
  for(int i=0; i<S.size(); i++) {
    s.insert(S[i]);
  }
  return s.size();
}

pairの使い方

pairは2つの値をペアで扱うためのSTLです。

宣言
#include <utility>
using namespace std;

pair <string, int> p;
参照
p.first();  // 一つ目の要素
p.second(); // 2つ目の要素
要素を追加する
p.first  = "hoge"
p.second = 3

pair<int, string>( "hoge", 3 );
p = make_pair("hoge",3);

mapの使い方

map とは、連想配列を扱うための STLです。

(キー, 値)のペアでデータが保持されます。「任意の型のキー」から「値」を引く辞書を作成できます。

setは(キー)のみを要素にしていますが、mapはpairを要素とします。

宣言
#include <map>
using namespace std;

map <string, int> m;
参照
m.first();  // キーへのアクセス
m.second(); // 値へのアクセス
要素を追加する
s["hoge"] = 3;
s.insert(pair<string, int>("hoge", 3));
s.insert(make_pair("hoge", 3));
要素を検索する

setと同じなので、省略。

要素をクリアする
m.clear();

よくある使い方

mapの要素にアクセスするには、イテレータを利用するのが便利です。

map<string, int>::iterater it;

また、要素の先頭アクセスするには、begin(),最後にアクセスするにはend()を利用します。

m.begin(); // 先頭要素にアクセス
m.end();   // 最後の要素にアクセス

文字列Sのなかの文字で一番数が多いものの最大値を求めます。

int getNumber(string S) {
    map <string, int> m;
    for(int i=0; i < S.size(); i++) {
      m[S[i]]++;
    }

    int max = 0;
    map<string, int>::iterator it;
    for(it = m.begin(); it!=m.end(); it++ ) {
      if( max < it->second ) max = it->second;
    }
    return max;
}

22 Oct 2013, 12:26

C言語でのローカル変数宣言は、ブロックの途中でも宣言可能らしい(C99)

C++の勉強をしていると、こんなことをよく言われる。

C++では、ローカル変数はメソッド内のどこでも宣言できる。C言語はブロックの先頭で宣言しなければいけない。そこがCとC++の差だ。

実は、そんなことはなかった!C言語でも、ローカル変数を関数内のどこでも宣言できる。

むかしは、ローカル変数は関数の頭で宣言しないといけなかった。しかし、C99で仕様拡張があった。

C99以降のC言語ではローカル変数を関数内のどこでも宣言できるようになったらしい。スコープは、その宣言された直後から開始される。ただし、VisualC++2008/2010/2012 のいずれも、この機能には対応してい無いらしい(これは未検証」

たとえば、こんなのもエラーしない・・・?

#include <stdio.h>
int main(void)
{
  printf("hogehoge\n");
  for(int i=0; i<10; i++)   printf("%d",i);
}

エラーした。

$ gcc local_val.c
local_val.c: 関数 ‘main’ 内:
local_val.c:4:3: エラー: ‘for’ ループ初期化宣言は C99 モード内でのみ許可されています
   for(int i=0; i<10; i++)   prinntf("%d",i);

コンパイラは、 gcc 4.8を利用。

$ gcc --version
gcc (GCC) 4.8.1

こんどは、C99をサポートするコンパイルオプションをつけると通った。

$ gcc -std=c99 local_val.c

リーダブルコードでも、スコープを狭めたほうが可読性が上がるし、こっちの仕様のほうがいいな。