カテゴリー「C/C++」の記事

日本国際賞授賞ニュースを見て

デニス・リッチー氏とケン・トンプソン氏が「日本国際賞」を受章したとのニュースを、今日0:00のNHKニュースで知りました。

ソフトウェア業界ではあまりにも有名すぎる、いわゆる「K&R」であり、彼らなくしては、今のIT産業の繁栄もインターネットの繁栄もなかったかもしれないと思うと、ノーベル賞受章にも値するのでは?と思うのですが。

おそらく、「K&R」の2人がいたから、「ソフトウェア開発で飯を食う」ほど、ITが「産業」と言うほど育つのに40年の時間を得ることが出来たのか?・・・もし「K&R」がいなかったら・・・?

少なくとも、2010年のウィキリークスとか2011年のチュニジアの政変はなかったかな。

| | コメント (0) | トラックバック (0)

Windowsのエラーダンプ解析

昨年の6月に設計を開始し、この2月で一応の完成を見たWindowsアプリケーションを、チームの人に試用してもらっていたところ、「特定の操作をするとアプリが落ちることがある」という報告を受けていました。しかも、一見したところ再現性がない、と。

最初は、ソースコードを読んでバグのありそうなところを一つづつ潰していき、怪しそうな部分にログ出力コードを埋め込み、非常にに怪しいと思われる部分をtry-catchで囲ってみたりしたものの、それでもアプリがクラッシュ(;_;)

で、最後の手段と思って、その名も「Windowsダンプの極意 エラーが発生したら、まずダンプ解析! 」なる本を買いました。

UNIX系OSであればcoreダンプはおなじみのはずで、10年ほど前に(私のキャリアの中で唯一)HP-UXのアプリを開発したとき、かなりお世話になったものですが、Windowsでも同じことができる、というわけです。

つまり、アプリがクラッシュしたときにダンプファイルを出力させ、そのファイルを後から(別のPCでもOk)解析してエラーの原因を突き止めるということが可能になるわけです。

解析に先立って、エラーダンプを解析するためのwindbgというデバッガをマイクロソフトからダウンロード&インストールしたり、リリースビルドでmapファイルを出力するようにVCプロジェクトを設定したり、ターゲットマシンでダンプファイルを出力する設定をしたり、といった準備をしておき、今朝、ダンプファイルをゲット。

昼過ぎまで別の作業があったので、午後3時ごろから解析開始。
windbgの使い方に慣れていないのと、スタックトレースを追いつつ逆アセンブルコードを眺めつつmapファイルを見て対応するソースコードを探したり、で2時間経過。

結果、WM_MOUSEMOVEイベントハンドラの中で落ちていることがわかり、後はソースコードレベルの解析に切り替え。さすがにソースコードレベルの解析は手馴れているので、コードを見た瞬間に「あちゃ~^^;」。
マウスカーソルが特定の場所にあるときに限ってちょっとしたデータ処理をする必要があり、データがゼロのときに処理してはいけないはずのところで処理を突っ走ってしまって不正なメモリ領域をアクセスしていた、というオチでした。

試用してもらっていた環境が「VistaにインストールしたVirtual PC上で動作するXP環境」だったので、その環境に特有の問題かと(根拠のない)推測をしていたのですが、何のことはない、(こう言うのもなんですが)ごく普通のバグでした(--;)

ともあれ、エラーダンプ解析はかなり有用だ、ということが身をもって分かりました。

| | コメント (0) | トラックバック (0)

UML CASEツール

前の派遣先を5月末で契約解除し、6月から新しい派遣先での勤務が始まりました。
今回の派遣先での業務は、化学系の分析装置を制御するソフトの作成です。

一応、大学は化学系だったので、分析化学の授業で今回扱う分析方法の概要を習った記憶だけはあったのですが、学生実験でその分析装置を操作することもなく、「思いっきり物理」(^^)の研究室に進んでしまい、修論の研究テーマは新しい分析手法の開発(ハード、ソフト込み。ただしソフトの比重が非常に高かった^^;;)で、新卒から7年ほど勤めた会社では音響計測のアプリケーション開発の仕事が多かったので、よくよく計測系の仕事に縁があるらしいです。

今回は、詳細設計からコーディング、テストまで関わるのですが、設計から関わるのは、実は数年ぶり。今まで派遣要員として関わった仕事は、コーディングとテストしか担当しない場合が多かったのです。
設計するにあたってUML CASEツールを使うことを考えているのですが、昔使っていたツールは、Visual Studio 6.0(のEnterprise Edition)に付属していたVisual Modelerという、Rational Roseのサブセット版でした。

自宅のPCにインストールしてあるので数年ぶりにVisual Modelerを使ってみたのですが・・・。クラス図しか描けない。せめてシーケンス図も描けないと使用に耐えないです。まあ10年も前にリリースされたツール、しかも開発環境におまけでついてきたサブセット版なので仕方ないといえば仕方ないですが。

ここ1年ほど個人的に使用していたJUDE Communityというツールは純国産ですが、今回はC++とVBでの開発なので、Javaのコード生成しかできないJUDEは却下。

そこで海外製のフリーのUML CASEツールを探して、いくつか試用してみて気に入ったのが、SDE for Visual Studio .Netというツール。Visual Studio 2003/2005に組み込まれて動作し、UML2.1に準拠し、当然C++やVB.NETのコードを生成することができ、逆にソースコードからクラス図を生成する、リバースエンジニアリングにも対応していて、ドキュメントもWordのdocファイルで出力できるという優れもの。これでフリーというのが信じられない出来です。
ただ・・・Core2 Quad 2.4GHz、メモリ4Gのマシンでも動作が重く感じることがあるのが悩ましいところではあります。またインストーラが130Mバイトもあり、FTTH環境でないとダウンロードもままならないです。

できればVC++6.0とVB6.0で開発したい事情があるのですが、2005に移行したほうがいいのか、悩ましいところではありますが・・・。

---- 7月18日追記 ----

色々探した結果、C++とVB(.NET以前のバージョン)のソースコード出力/リバースエンジニアリングもできるツールとして、Enterprise Architectというものを見つけました。
とりあえず30日トライアル版で試用してみて、「これは使えそう」という感触を持ったので購入して頂きました。たしか1ライセンスあたり3万しなかったというのが大きな理由だったような。

ここに例示した、どのUMLツールも、VisualStudio6についてきたVisualModelerとは比べ物にならない使い勝手のよさ。UML自体が普及したことにより、こういったCASEツールが普及したのかなぁ、などと思い、今思えば非常に力不足なVisualModelerを使って設計していた昔と比べて、10年の時間を感じました。

| | コメント (0) | トラックバック (0)

ディスコグラフィ作成:同義語辞書の作成

ディスコグラフィ作成に取り組み始めて1ヶ月を経過。もはやちょっとしたプロジェクトと化してきた感があるが、ようやく同義語辞書の作成にかかることができるようになった。

辞書のファイル構造は、

ルードヴィヒ・ヴァン・ベートーヴェン  Ludwig van Beethoven
弦楽四重奏曲  String Quartet Streichquartett Quatuor à cordes
Martha Argerich マルタ・アルゲリッチ
....

といった具合に、1行に同じ意味の単語を羅列している。C++で記述するオブジェクト構造も基本的には、

CSynonymEntryクラス  ファイルの1行を表現し、単語の羅列を保持する。
CSynonymDicクラス  辞書全体を表現し、複数個のCSynonymEntryオブジェクトを保持する。

という構造であるが、CSynonymEntryクラスは、単語の保持以外にいくつか機能を追加したいので、
class CSynonymEntry : std::vector<CString>{...};
と実装した。

また、CSynonymDicクラスは、ある単語が辞書に登録されているか調べる必要があることと、ファイルに記述したイメージ通りデータを保持しておく必要があることから、

typedef boost::shared_ptr<CSynonymEntry> CSynonymEntryPtr;

class CSynonymDic
{
private:
    class CDictionaryOrder{
    public:
        bool operator()( const CString& lhs, const CString& rhs ){...}
    };
   typedef std::map<CString,CSynonymEntryPtr,CDictionaryOrder> CSynonymMap;

    CSynonymMap                            mxDictionary;
    std::vector<CSynonymEntryPtr>    mxEntries;
public:
    CSynonymDic();
    ~CSynonymDic();
    //その他メンバ関数
};

と記述した。つまり、単語が辞書に登録されているか調べる目的に、map<CString,CSynonymEntry*>を用意し、ファイルに記述したイメージ通りデータを保持しておく目的から、vector<CSynonymEntry*>を用意した。

が、実際にはCSynonymEntry*の替わりにboost::shared_ptr<CSynonymEntry>を使っている。これはBoostというクラスライブラリで提供されている、参照カウントつきポインタを定義するshared_ptrクラステンプレートを用いているためである。
また、mapのテンプレート引数の3番目にCDictionaryOrderを指定しているが、これは独自のソート順序を定義するためである。

このようにして辞書のデータ構造を実装し、GUIを作ってひたすらデータ入力。結果、登録単語数として1600、辞書ファイルの行数として500余りになった。Perlで検索処理を行うことを考えると、単語の平均文字数の大きい行ほど前に来るようにソートしておいたほうが何かと都合が良いと考えられるので、ファイルを保存する時にソートすることにした。

あとは、Perlで検索CGIを書くだけだが、Perlを個人的用途で触るのは4、5年ぶり、業務でも3年前に30行程度のCGIスクリプトを書いたことがあるだけなので、Perl独特のクセはかなり忘れてそう・・・。

| | コメント (0) | トラックバック (0)

ディスコグラフィ作成:CDジャケット画像の縮小とWebサーバへのデータ転送

700枚余りのCDのデータ入力とジャケット写真をスキャナでJPEG画像として取り込む作業は終わり、その後買い足したCDについても、買った当日にデータ入力とジャケット取り込みをするように習慣付けるようになったので、データ入力に関しては問題はクリアできた。

しかし、CDデータを収めたUTF8形式のタブ区切りテキストファイル3つと、CD枚数と同じだけのJPEGファイル700個をFTPクライアントソフトを使ってWebサーバへ転送するのがかなり面倒・・・。
FTPクライアントソフトを使って、転送したいファイルをいちいち選択して転送するという作業自体が面倒だし、転送漏れも起こりうる。これは、データ入力ツールにFTP転送の機能も追加したほうがよさそう。これは、MFCのインターネットクラスなどを使うことで1日でサクッと作れてしまうのだった。

それともう一つ。携帯からも検索できるようにしようと考えているのでジャケットの画像ファイルのサイズを小さくしたものも用意しておきたい、という要求も出てきた。データ入力の際、スキャナからは100dpiで取り込んでいるため、大体480×480ピクセル、80~100kバイト程度のJPEGファイルが700個ある。携帯の画面サイズは(少なくとも私の持っている東芝のau向け機種では)240×320ピクセルなので最大でも200×200ピクセル程度まで画像を縮小する必要があるし、ファイルサイズも小さくないとパケット通信料も馬鹿にならない。とにかくJPEG画像を縮小したい。

最初は、オリジナルの480×480ピクセル、100kバイトのファイルをCGIで動的に縮小することを考えていたので、PerlでJPEGファイルのリサイズできるものはないかと検索してみたが、なかなか見つからない。まあ、JPEGファイルの仕様自体が、離散コサイン変換とか圧縮と言った数学的内容を含んでいて難解なので、簡単にPerlベースのソースコードを見つけようというのも無理があるわけだが・・・。

そんなわけで、CGIで動的に画像を縮小することはあきらめ、PC上で画像を縮小するためのツールやライブラリをVectorで検索してみたが、どうも見つからない。
で、いろいろ検索するうちに、ImageMagickというオープンソースのライブラリがあるらしい、ということを知り、ImageMagickの公式サイトからWindows用のインストールパッケージ(WindowsInstaller形式ファイル)をダウンロードして、インストールし、公式サイトのチュートリアルページを参考に、
「JPEGファイルを読み込み、元の1/4に縮小し、JPEGファイルとして書き出す」
というコードを書いてみたが、ファイルを読み込むところで落ちてしまう。インストールされていたサンプルコードと見比べても何が違うのか良く分からない。

仕方ないのでライブラリの全ソースを圧縮したzipファイルを公式サイトからダウンロードして展開し、ライブラリをビルド・・・してみたが、コンパイルエラーが出まくってどうしようもない。結局、インクルードファイルの中のマクロ定義を2つコメントアウトしたらビルド成功。コメントアウトしたマクロ定義は、XWindowを使うかどうかを決定するマクロと、GNOMEのxmlライブラリを使うかどうかを決定するマクロだった。ソースをダウンロードしてからビルドに成功するまで丸1日・・・。今使っているノートPCはいろんなソフトをインストールしまくっているせいかビルドがやたら遅いので、セーフモードで起動した状態でビルドする、という有様(笑)。それでもデバッグモードでのビルドに要した時間は1時間弱。今のPC、今度の3月で4年になるし、そろそろ新しいPCに乗り換える時期にきたかも。
ライブラリをデバッグモードでビルドするのに1日かかったあと、ようやくデバッグ再開。ライブラリの奥底までステップインしてようやく分かったのは、環境変数MAGICK_CODER_FILTER_PATHとMAGICK_CODER_MODULE_PATHを定義しておかなければいけない、ということだけだった。

まあ、そんなドタバタはあったものの、ImageMagickライブラリ(より厳密には、ImageMagickにアクセスするためのC++インタフェースクラスライブラリ)を使うと、非常に簡単にJPEGファイルの縮小ができた。以下にサンプルコードを示す。

#include <Magick++.h>      //このファイル1つをインクルードすればライブラリの全機能が使用可能
using namespace Magick;

int main( int argc, char **argv )
{
   InitializeMagick(*argv);   //exeファイル名を引数としてライブラリを初期化

   try{
    Image xImage;   //画像データを保持するオブジェクト

    //jpeg画像を読み込む
    xImage.read( "sample.jpg" );

    //元画像の25%に縮小
    unsigned int uWidth = xImage.columns(), uHeight = xImage.rows();  //画像の幅と高さを取得
    Geometry xGeometry( uWidth/4, uHeight/4 );  //サイズを定義するクラス
    xImage.resize( xGeometry );

    //jpeg画像を保存する
    xImage.write( "sample_small.jpg" );
   }
   catch ( Error& rxError ) {
    std::cout << rxError.what() <<std::endl; //エラーの内容を表示
   }
}

このImageMagickライブラリ、画像ファイルの読み書きとサイズ変更だけではなく、さまざまな機能を持っているらしいので、画像を扱うアプリを書く機会があれば勉強してみたいと思う。そんなことはさておき、同義語辞書を作成するためのGUIをまだ全然考えていない。うーん・・・。

| | コメント (2) | トラックバック (0)

ディスコグラフィ作成:とりあえずデータ入力は終わったが・・・

ディスコグラフィ作成に取り掛かり始めてはや20日。どうにかこうにか年内にデータ入力だけは終わりました。入力したCDの枚数700枚ちょっと(DVD10枚、VHS1本も含む)。

データを入力するためにCDを棚から取り出すたびに、
「このCD、いらんわ」
と選別もしたのですが、選別されて「売却」のカテゴリーに入ったCDは約200枚。うち25枚はアマゾン・マーケットプレイスに出品できなかったのですが、残りの約175枚を延々とアマゾンに出品するのにトータルすると丸1日分くらいの労力は使ったかも(^^;)
プラケースがホコリをかぶってたり、CDが汚れてたり(汚れを栄養にしたのか、カビが生えてるものも・・・)していて、ウェットティッシュでプラケースを拭いたり、CDを食器洗い用洗剤で洗ったり、といった作業が大変でした。

入力したCDの枚数にして500枚に達した頃から、データファイルの読み込みが遅くなる、という事態が発生。
データはUTF8エンコーディングのタブ区切りテキストファイル3つに保存していたのですが、読み込み処理が終わるのに50秒もかかる有様。
ファイル読み込み部分のC++のソースを読み返してみると、UTF8からワイド文字列に変換した後のファイルの内容を1行1行分割してvector<CString>に格納している(厳密に言えばLPCTSTR→CStringの変換コンストラクタ)のが遅い原因と推測し、行ごとに分割したテキストデータ(正確に言えば、バッファ上のデータの各行の先頭を指すLPTSTR)をvector<LPTSTR>に格納することと、その他細々した改良で、50秒を15秒まで短縮。それにしても、3つのテキストファイルは合計しても1万行にも満たないので、15秒はまだ時間がかかりすぎ。
そこで、コードの呼び出し回数と処理時間を計測する、プロファイリング用の小さなライブラリ(ソースファイルとヘッダファイル1つづつ。本当に小さい)を1時間ほどでチャチャっと作り、テキストの各行を解析するコードの1行1行の処理時間を計測。すると、1行分のテキストをタブ文字ごとに区切ってvector<LPTSTR>に格納していた部分で、vectorをresizeしたり、データをpush_backするのに最も時間がかかっていることが判明。で、結局、vector<LPTSTR>ではなく、LPCTSTRの配列に、タブ区切りされた文字列の先頭アドレスを格納することで、15秒を5秒に短縮。
もともとのコードでは50秒かかっていたので5秒まで短縮したので、処理速度10倍!1万行弱のテキストデータを処理するのに、C++で書いたネイティブコードで5秒なら妥当なところかな。

そんなこんなで700枚強のCDを入力し終えた今、次に考えないといけないのは、以前の記事でも触れた「同義語」の辞書を作成する問題。1つ前の記事のコメントでDさんに提案して頂いた、googleのサイト内検索、というのもいいかな、と思ったけれど、googleで「ベートーヴェン」で検索をかけたところ、「ベートーベン」はヒットしたが、「Beethoven」はヒットしない。
「ベートーヴェン」で検索をかけたら「Beethoven」もヒットして欲しいし、「弦楽四重奏」で検索したら「String Quartet」(英語)も「Streichquartett」(ドイツ語)も「Quatuor à cordes」(フランス語)もヒットして欲しい。

そうすると、やっぱり自前で同義語辞書を作る必要があるなぁ・・・。
同義語辞書を作るアプリを作らないといけないが、データ構造を考えるのはさほど難しくなさそうではあるが、どんなユーザーインタフェースにしようか、というところで悩んでしまう。
これは来年(といっても2日後^^;)の課題です。

明日は、東京文化会館でベートーヴェンの中期と後期の弦楽四重奏曲を聴き倒すコンサートに出かける予定。しかし、寒波が南下しているというニュースがあるし、彦根や岐阜の明日の天気予報は雪だから、新幹線、事前に予約していたのより1時間早いのぞみに変えようかなぁ・・・(明日の朝の時点でその列車の指定席に空きがあれば)。

| | コメント (0) | トラックバック (0)

C/C++セキュアコーディング

Amazonで「C/C++セキュアコーディング」という本を見つけたので買ってみました。

扱っているテーマは、ソフトウェアの欠陥に対してどのような攻撃がなされうるか、そして攻撃を避けるにはどうすればよいかというもので、大学の研究室でCを独学で学んで以来15年になりますが初めて読む類の本です。

届いたのは一昨日で、まだ最初の5分の1くらいしか読んでませんが、かなり刺激的な内容です。
バッファオーバーランと呼ばれる欠陥を用いて如何に予期しないコードが実行されるか、という解説を読んで、まだ解決策については目を通していないのですが、
「絶対にバッファオーバーランを起こさないようにしないといかんなぁ」
と決意を新たにした次第です。
実は1年半ほど前、ソース管理の不手際からバッファーオーバーランを起こしてしまってプロセスがお亡くなりになってしまう、という不具合がリリース後に発覚してしまったという恥ずかしい経験があったりします・・・。

他にもいろいろとソフトウェア欠陥についてのトピックがあるようで大変興味深いです。C/C++でコーディングする人は一読する価値はありそうです。

| | コメント (0) | トラックバック (0)