Hatena::ブログ(Diary)

滴了庵日録 このページをアンテナに追加 RSSフィード

2013/06/08(Sat)

C++の例外の問題点

C++はJavaC#と同様に例外の機構を備えていますが、JavaやC#と比べてC++では例外の使用を避ける向きが多いようです。たとえば、Google C++スタイルガイドでは "We do not use C++ exceptions." とされています。これはどうしたわけでしょうか?


ひとつには、C++はコードサイズや実行速度の要求が厳しい用途に使われることが多いからでしょう。C++で例外を使用するとコードサイズが大きくなり、またオーバーヘッドが増えて処理速度が落ちます。非常に限られたリソースしか持たない組込み系や、大量のデータを高速に処理しなければならないエンジンでは、これは大きなデメリットです。


しかし、C++で例外が敬遠される理由はそれだけではありません。C++はJavaやC#などの現代語に多大な影響を与えた言語ですが、C言語という古代語をひきずっており、今となってはイケてない部分が多いいわば中世の言語です。例外に関してもJavaに比べて下記の点がイケてません。

  • catch忘れをコンパイル時に指摘してくれない。
  • スタックトレースが無いので、どこで発生した例外か分からない。
  • finally構文が無いので、後始末の処理がめんどう。

そして、最大の問題点は、メモリリークを起こしやすい」ことです。言うまでもありませんが、JavaやC#とちがってC++はGCを備えていないので、確保したメモリはプログラマーがきっちり漏れなくの解放しなければなりません。しかるに例外とは「抜け道」を作ることなのです。抜け道を作ったら漏れの危険性が高まります。GCを備えてないC++では例外の使用は慎重にしなければならないのです。


C++はそもそも何事もプログラマ自己責任に任せるアンマネージドな言語です。そして、ライブラリで対応できることはあえて言語でサポートしないという思想を持っています。上記の問題点も、ライブラリの使用で解決できる部分はあります。それらを駆使し、かつ自己責任でちゃんとメモリの管理ができるなら例外を積極的に利用して良いでしょう。逆にいうならそれが出来ないならC++では例外は使わないほうが賢明かもしれません。

JavaやC#でもメモリリーク

ふだんC/C++をメインに使っているので、JavaやC#みたいなGCを備えた言語は便利だなーと感じます。たとえば、関数内で生成したオブジェクトを関数外に返すなんてことは、C++だときちんと後始末されるか非常に神経質になります。C++はnewせずにスタック上にオブジェクトを生成することもできるので、そんなオブジェクトをうっかり関数外に返してしまうととんでもないバグになります。その点、オブジェクトをすべて動的に生成したうえで不要になったらGCが自動的に始末してくれるマネージドな言語は、安全かつ簡便です。


さりとて、JavaやC#だってメモリリークと無縁というわけではありません。ではなぜGCがあるにもかかわらずメモリリークが起こるのでしょうか? GCは不要になったオブジェクトの後始末をしてメモリを回収しますが、ここでいう「不要になったら」とは「誰からも参照されなくなったら」という意味です。たとえ実質的に使われなくなったオブジェクトであっても、1カ所からでも参照が残っていたらGCには回収されません。つまり、不要になったオブジェクトへの参照の解除忘れがメモリリークを招くのです。


もっともよくあるパターンはイベント購読の解除忘れでしょう。イベントリスナーは、JavaならaddListener、C#なら+=でイベント源に登録しますが、 イベントリスナーのオブジェクトが不要になったとき、イベント源に登録の解除(JavaならremoveListener、C#なら-= )をしなければいけません。これを忘れるとイベント源はいつまでもイベントリスナーへの参照を持つことになり、イベントリスナーはGCに回収されなくなります。つまりメモリリークです。


より一般的にいうと、よそのコンテナ(キュー、リスト、動的配列連想配列など)にオブジェクトをつっこんだまま、削除を忘れるとメモリリークになります。前述のイベントリスナーもその一例といえるでしょう。コンテナ自体が短命ならばまだしも軽傷で、コンテナが消滅すれば晴れて一切の参照を失ったオブジェクトたちもGCの回収対象になります。しかし実際には、この手のコンテナは往々にして長命ないし不滅なのです。


他のオブジェクトをコンテナに入れて管理するような、システム側寄りのオブジェクトが長命なのは当然といえるでしょう。長命の最たるものはクラスの静的メンバであるオブジェクトです。クラスは不滅なので、クラスに参照されているオブジェクトもまた不滅となります。


C#で、あるクラスのオブジェクトがリークしてるかどうかは、デストラクタ(ファイナライザ)を実装してメッセージを出力するようにして、GC.Collect()で強制的にガベージコレクションをさせてみると確認できますね。

C#で、アンマネージドリソースを使った場合は、当然ながらGCの対象外なので自己管理が必要ですが、それはまた別の話。

2013/06/07(Fri)

FTDIのUSBシリアルの不具合?

先日、シリアルポートのRTSをめぐる混乱について書いたとき、Windows が RTS_CONTROL_TOGGLE をサポートしてることを実験で確認しました。しかし、FTDIのUSBシリアルではじゃっかん問題があるようです。


先日の実験ではWindows標準のシリアルポートドライバ(serial.sys)で駆動されている物理COMポート(COM1)を使用しました。同じ条件でFTDIのUSBシリアル(FT232H)の仮想COMポート(独自のドライバを使用)を使って実験したところ、RTSがネゲートされるタイミングにバラツキがあるばかりか、まれにTxDの送信完了より前にネゲートされることが確認されました。


下図は9600ボーでFTDIの仮想COMポートに送信したときのTxD(上)とRTS(下)です。CMOSレベルのUARTなので負論理です。このようにTxDがRTSからはみ出してしまうケースがあります。


FTDIのRTSタイミング


物理COMポートではこんなことは起こらず、RTSのネゲートタイミングはTxD送信完了に対してほぼ一定であったので、FTDIのドライバが RTS_CONTROL_TOGGLE にきちんと対応しきれてないのだろうと思われます。

HEWのCall Walker

いまさらだけど、HEW付属の静的スタック解析ツールCall Walkerの使い方についてメモ。


HEWの [ビルド]>[***Toolchain]>[最適化リンカ]>[その他] にて、

「スタック情報ファイル(sni)出力」にチェックしてビルドする。

[ツール]>[Renesas Call Walker] でスタック解析結果がツリー表示される。

2013/06/05(Wed)

Windows7に.NET Gadgeteer開発環境を構築する

概要

.NET Micro Frameworkベースのプラットフォームである.NET Gadgeteerの開発環境を構築します。GHI Electronicsの解説ページを参考にしました。


 【メモ】

インストール

(1) Visual Studio Express 2012

Visual Studio 2012 Express for Windows Desktopダウンロードし、インストールします。インストールしたらさっさとオンライン登録してプロダクトキーを取得しておきましょう。こういうものはいつなくなるか分からんのでISOをダウンロードしておくが吉です。Windows7でISOイメージをマウントするにはVirtual CloneDriveとかを使いましょう。


(2) .NET Micro Framework SDK

.NET Micro Framework SDK 4.3 (RTM)をダウンロードし、インストールします。


(3) GHI Software Package

上記解説ページの「3.Install GHI Software Package v4.2」よりダウンロードし、インストールします。ただしダウンロードするには「Log In」からユーザー登録とログインが必要です。


(4) FEZ Config

上記解説ページの「4.Download FEZ Config (Beta)」よりダウンロードし、FEZ_Config_v011.exeを得ます。これはデバイスファームウェアアップデートなどをおこなうユーティリティーツールです。


使い方

Visual Studio Express 2012を起動します。「新しいプロジェクト」から Visuial C# > Gadgeteer > .NET Gadgeteer Application を選んで新規プロジェクトを作成します。ウィザードでメインボード(マイコン基板)の選択画面が出るので、たとえば「FEZCerberus」を選択します。


Program.gadgeteerにメインボード(マイコン基板)の絵が現れます。C#のGUIデザインと同じ要領でツールボックスから使用したいモジュール(サブ基板)をドラッグしてProgram.gadgeteerに貼り付け、コネクタをクリックしてメインボードと結線します。モジュールのインスタンス名もGUIデザインと同じ要領でプロパティウィンドウで変更できます。


プログラムはProgram.csに記述します。ここには、プロジェクト作成時に Gadgeteer.Programクラスの派生クラスとして、ユーザー定義のProgramクラスのひな形が生成されています。このクラスのProgramStarted()メソッドが、メインボードの電源投入orリセット時に呼び出されます。簡単なポーリング型のプログラムであれば、ここに無限ループを書いて回せばよいでしょう。


イベントドリブン型のプログラムであれば、イベントハンドラを記述し、ProgramStarted()メソッドにてイベントハンドラの登録をおこないます。イベントハンドラの登録はGUIアプリと同じ要領で次のような形式で記述できます。

インスタンス.イベント += new クラス.イベントハンドラ(ハンドラ関数)


実機との接続・電源供給は、USB Client SP Moduleなるモジュール(サブ基板)を介します。デバッグ接続時は、Debug.Print()メソッドを用いて、VisualStudioのデバッグコンソールにメッセージを表示できます。


とりあえず、今日はここまで。


参考サイト

APIリファレンス

  ※ GHIのライブラリにはPremium版とオープンソース版の2系統がある。

本家 GHI Electronics のお役立ちページ

その他