2015年04月07日

テンプレート

以前はほどほどに利用していた C++ のテンプレートですが、いろんなことができることがわかってきて最近は頻繁に使うようになりました。

よくサンプルにあるのはこういうものです。

template<typename T> T max( T x, T y )
{
  return( ( x < y ) ? y : x );
}

型 T がテンプレート引数で、これを任意の型にすることができます。

double d = max<double>( 0.5, 0.7 ); // double用
int d = max<int>( 16, 10 ); // int用

関数の中では "<" を使って二つの値を比較しています。この演算ができなければコンパイル時にエラーになります。逆に言えば、関数内にある演算や他の関数が利用できる型ならちゃんとコンパイルできるので、普通の組み込み型ではなく任意のクラスなどもテンプレート引数にしてしまうことができます。派生クラスによるポリモーフィズムは動的に変化するのに対し、コンパイル時に型が変化するので静的なポリモーフィズムと言えます。STL はテンプレートを多用しているので、STL をうまく組み合わせるといろんなことが実現できます。これがなかなかおもしろくて、最近ハマっています。

残念なのが、コンパイル時のエラーがわかりづらいこと。慣れないと対処法がわからず、慣れてきても原因がわかりにくいことが多々あります。テンプレートを利用できるのは C++ と D 言語くらいだそうで、D 言語は少し興味があるんですよね。機会があれば勉強したいところですけどね。  

2015年03月08日

ビットシフトで苦戦

先ほど「アルゴリズムのコーナー」を更新しました。

今回のお題は「適応型ハフマン符号化」です。サンプル・プログラムの作成に結構苦戦しました。ほぼ完成したと思っていた夕方頃にバグを見つけたときは、今日の更新は無理かなと思っていました。

今回見つけたバグは次のようなものです。

~0 << l

で、0 のビット反転結果を l だけ左シフトできます。左辺が 32 ビットなら l が 32 のときはゼロになってしまうはずですが、なぜかゼロにならず、ビットシフトが全くされなくなります。しかし、

~0 << 32

と書けばちゃんとゼロになります。型のサイズより大きくビットシフトしている場合、コンパイラが勝手に剰余を求めてくれるのか、それとも他に理由があるのか、そこまではわからなかったのですが、最終的には l の大きさを見て計算方法を切り替えるようにしました。少々気持ち悪い感じはしますけどね。  

2015年02月01日

二月最初の日

今日は愛知県知事選。結果はある程度予想してましたがそれでも投票には行きました。今回は投票率もかなり低いんじゃないでしょうか。

今年になって最初のホームページ更新を行いました。「静的ハフマン符号化」の章で、以前作成したものをリニューアルしています。このまま「適応型ハフマン符号化」の方もリニューアルを始めようかとも考えていますが、他にも進めたいテーマがいくつかあって迷っています。しかし、迷っているだけで先に進めないというのが一番よくないですね。

C++ には STL ( Standard Template Library ) という標準のライブラリがあります。これを使いはじめると、データ列の表現のための malloc 等を使ったメモリ管理や、文字列を char* で表現するというようなことを全くする必要がなくなります。vector をはじめとするコンテナ・クラスや各種アルゴリズムは強力で、データの加工をするならこれ一つあれば十分と思えるくらいです。今回のサンプル・プログラムでも多くの場面で活用しています。
C 言語や C++ でプログラミングしているのに STL は使ったことがないという方は、ぜひ利用することをお勧めします。配列の代わりに利用できる vector や文字列の string を利用するだけでもかなりプログラミングが楽になります。

利用方法を知るのに便利なサイトを紹介します。

C/C++ リファレンス

cplusplus.com

どちらも、STL をある程度利用している今でも活用しているサイトです。下側は英語ですが、必要な物はほとんど全て網羅されていて非常に重宝しています。  

2015年01月18日

gtk+3 使ってみた

ようやく本腰を入れて gtk+3 への乗り換えを進めてみました。

ウィジェットへの描画方法が一新されてしまったので最初は手探り状態でしたが、ようやくある程度理解して動作するところまで完成しました。描画のところだけ変更すればあとはそのまま動作するようになったので一安心です。

一番苦しんだのが、GdkPixbuf を使って Drawable へ描画するところ。昔参考にしていた本にあった方法で、GdkPixmap 経由で描画する形にプログラムを作成していたので、GdkPixbuf を GdkPixmap へ描画するときの原点と、GdkPixmap を Drawable に描画するときの原点の二つを管理していたのですが、gtk+3 からは Cairo を使った描画スタイルになったので原点は一つだけ管理すればよくなりました。さらにイベントも expose_event が廃止されて draw イベントとなり、イベントハンドラの内容も変える必要がありました。このあたりが整理できたらあとはそれほど苦労したところはありません。しかし、まだ完全に理解したわけではなく、ドキュメントを参考にいろいろと試行錯誤しています。

Cairo が gtk+ 専用というわけではなく、gtk+ が Cairo を利用するようになったということのようなので、Cairo そのものも、もっとドキュメントを確認する必要があるようですが、パッと見たところいろんなことができそうです。もう少し内容を理解していきたいですね。いずれにしても、gtk+ を使ったサンプル・プログラムなんかももう少しすれば完成しそうなので、またいずれ公開しようかと思っています。  

2014年12月07日

スタイルはいいに越したことは ...

寒い日が続きます。エアコンなしでは過ごせないくらいになってきました。

プログラムを作成するときの書き方は、そのコードを読みやすくする上で重要です。その規定は「スタイル」と呼ばれます。名前空間の使い方で悩んでいるところがあって検索してみたら、Google の「C++ スタイルガイド」というのを見つけました。「Google C++ Style Guide」日本語訳で、他のいくつかの部分でも非常に参考となるところがありました。もちろん、全てに無条件で賛成できるわけでもないので、7 割から 8 割程度くらいを取り入れてみました。
特に重要だと強調しているのが「読みやすさ」と「一貫性」で、これはその通りだと思います。日曜プログラミングのような場合はあまり意識せずに作り始めて後で後悔するというのが非常に多くて、まさに今の状況がそんな感じです。今日もいろいろと手直しをしていました。いつになったら終わるんでしょうか。  

2014年11月29日

ドキュメント作成も自動で

明日で 11 月も終了。今年もついに残り一ヶ月です。

年末になると急にあせりだすというのも不思議なものですが、今年は割とのんびりと過ごしています。個人消費の落ち込みの例にもれず、特に大きな買い物をする予定もなく、何枚かの CD と何冊かの本くらいしか考えていません。ただ、ちょっとしたプログラムを公開しようかと考えていて、今はその準備をしています。

公開するにあたってドキュメントの整備が必要になるのですが、手作業ではかなりの労力が必要になります。ソースのコメントからドキュメントを自動的に作成するような機能は統合開発環境なんかでよくありますが、未だにエディタでソースを組んでいる身としては利用したことがなく、今頃になっていいモノがないか探していました。
Doxygen というものが割と有名なようで、早速使ってみたらこれが非常に便利でした。変換できるように書式を見なおす必要がありましたが、ドキュメントを手作業で作成する手間に比べれば大したことはありません。作成されたドキュメントも非常に見やすく、自分が一番参考にできそうなくらいです ( 自分で作っておいて何をやっていたのかわからなくなるというのはよくある話です )。

こういった便利な道具を使いこなせばもっと効率もよくなるんでしょうけど、なかなかその気になれないというのは来年こそ何とかしたいものです。  

2014年11月24日

C++ メモランダム (reference)

今日、11 月 24 日はフレディ・マーキュリーの命日でしたね。もう 20 年以上も経過しています。
そういえば、少し前にブライアン・メイを見ました。テレビだったか動画サイトだったか忘れましたが何かのインタビューだったと思います。内容も覚えてないですが、ただ「だいぶ老けたな」と思いました。フレディ・マーキュリーが生きていたら、どんな感じになっていたでしょうかね。

C++ で利用できる参照型 (reference) X& は非常に便利で、特にヌル参照が発生しにくいという利点があるのですが、発生がゼロになるというわけでもなくて、例えば

void test( const int& p )
{
  std::cout << p << std::endl;
}

int main( int argc, char* argv[] )
{
  int* i = 0;
  test( *i );
}

と書いてもコンパイルはできてしまい、実行結果は segmentation fault になります。一時期、参照ならヌル参照は絶対発生しないと思い込んでいた時期がありました。それから、ポインタの参照渡しは

int& *p

ではなく

int* &p

と書きます。前者は「参照へのポインタ」で定義不可能、後者は「ポインタへの参照」を意味します。これ、いつも逆に思えて混乱します。

参照の使い方としては、

int a = 5;

int& ref_of_a = a; // a への参照

int* pnt_of_a = &a; // a へのポインタ

int* &ref_of_p = pnt_of_a; // pnt_of_a への参照 ( = a へのポインタへの参照 )

int* pnt_of_r = &ref_of_a; // ref_of_a へのポインタ ( = a へのポインタ )

くらいを整理しておけば大丈夫でしょうか。ちなみに参照はエイリアス(別名)という意味なので実体がありません。なので、参照へのポインタというのは定義不可能ということになります。また、初期化も必ず必要です (別名にするものを渡さないと初期化できないためです)。
  

2014年11月16日

C++ メモランダム (binder/adapter)

今日も寒い一日でした。11 月も半分が終わり、師走が近づいてくるとどうしても慌てだすんですよね。別に慌てる必要もないのに。

少し前に、STL のバインダ・アダプタを利用していて意味不明なエラーに遭遇し、ハマったことがあったので、ここで紹介しておきます。

次のようなクラスを作ったとします。

template<class T> struct OverloadTest
{
  void operator()( T& t1, T& t2 )
  { std::cout << t1 + t2 << std::endl; }

  void operator()( const T& t1, const T& t2 )
  { std::cout << t1 + t2 << std::endl; }
};

使うときは

OverloadTest<int> test;
int i = 5;
int j = 6;
test( i, j );

などと書けば、i + j = 11 を出力するという簡単なものです。ところが、

OverloadTest<int&> test;

と型を参照型にすると、

error: ‘void OverloadTest<T>::operator()(const T&, const T&) [with T = int&]’ cannot be overloaded
error: with ‘void OverloadTest<T>::operator()(T&, T&) [with T = int&]’

などとエラー表示されます。この場合は、メンバ関数をどちらか一方にすれば解決するし、そもそも二つのメンバ関数を定義しておく必要はありません( const T& の方を採用すればいいです )。

これと同じ現象が、STL の中の bind2nd と ptr_fun を組み合わせて使った時に発生しました。元々、構造体 RGB 用に用意した任意の演算子多重定義関数を、第二引数は固定値にして呼び出したいために作ったクラスです。

template<class T> class RGB_BindFunc : public RGB_Op
{
  typedef RGB& (*Op)( RGB&, T );

  std::binder2nd< std::pointer_to_binary_function<RGB&,T,RGB&> >
    ptrBinFunc_;

public:

  // PtrFunc コンストラクタ
  RGB_BindFunc( Op f, T t )
    : ptrBinFunc_( std::bind2nd( std::ptr_fun( f ), t ) ) {}

  // operator() : 任意の演算処理(override)
  virtual bool operator()( RGB& rgb )
  {
    ptrBinFunc_( rgb );
    return( true );
  }
};

これを次のように利用しようとすると、

RGB_BindFunc<unsigned char> func( (RGB& (*)( RGB&, unsigned char ))operator&=, 0x0F );

以下の様なエラーが出力されます。

.../binders.h:152: error: ‘typename _Operation::result_type std::binder2nd<_Operation>::operator()(typename _Operation::first_argument_type&) const [with _Operation = std::pointer_to_binary_function<GraphicLibrary::RGB&, unsigned char, GraphicLibrary::RGB&>]’ cannot be overloaded
.../binders.h:146: error: with ‘typename _Operation::result_type std::binder2nd<_Operation>::operator()(const typename _Operation::first_argument_type&) const [with _Operation = std::pointer_to_binary_function<GraphicLibrary::RGB&, unsigned char, GraphicLibrary::RGB&>]’

しばらくこのエラーに悩みましたが、binders.h の中でまさにさっき示したような定義がされていて、参照付きの変数が関数内にあるとうまくいかないということがわかったので、最終的に bind2nd と ptr_fun は使わずに組むことにしました。

Googleなんかで検索すると他にも悩んでいた方はいるようで、その中のアドバイスをいくつか参考にしました。しかし、ズバリこれで解決というようなものはなかったです。そもそもバグの類なのか、想定外の使い方をしてしまったのかも判別が付いてないです。  

2014年10月19日

PIC フォーマット

本日、アルゴリズムのコーナーを更新しました。

過去のページの見直しのみですが、画像圧縮法のひとつ「ランレングス法」がテーマです。非常に単純なアルゴリズムなのにもかかわらず、おもしろい用途も見つかったりして、今回は楽しく作業できました。
今回のテーマには「PIC フォーマット」も含まれています。昔、x68000 専用の画像圧縮アルゴリズムとして誕生したものです。x68000 ユーザだった頃は、当然いろいろとお世話になりました。画像フォーマットとしては他にも MAG や Pi といったものもありましたねえ。今では JPEG や GIF、PNG といった世界標準のフォーマットが主流となって、純国産の画像フォーマットはめったに見かけなくなりました。そう思うと少々さみしい気分になるのは年をとってしまった証拠でしょうか。

しばらくの間、画像関係のプログラムから離れていたのですが、最近またいくつかのネタを見つけて勉強中です。画像関連は結果がビジュアルで表されるので、作っていて楽しいんですよね。統計関連のネタも少し落ち着きつつあるので、両者を並行で進められればと思ってます ( 実は、統計関連の勉強を始めたのは画像認識に興味があったからなんですよね。最近、そのテーマから少々離れてしまっている気がします )。  

2014年10月05日

GTK+ と Qt

台風 18 号接近中です。

GUIツールを作成するときは、Linux 上なら GTK+、Windows 上なら .Net Framework を使っています。どちらも無償で扱えるというのは非常にありがたいことです。しかし、進化のスピードが速すぎて、過去に作成したソースが新しい環境ではビルドできなかったり、ビルドできても動作しないことがよくあります。メンテナンスするのは結構大変ですね。
昔、GTK+ 用の GUI ビルダ Glade を少しだけ使ったことがあって、あまり使い勝手がよくなかったので結局利用はやめてしまったのですが、未だに GUI のデザインをソース上で行っていてこれがかなり大変なので、最近の Glade がかなり進化したらしいということもあって、一度使って見ようかと思ってます。

もう一つ、少しだけ利用したことのあるツールキットに Qt があります。こちらは C++ で構築されているので各オブジェクトもクラスでまとめられていて、実はこちらの方が使いやすいのではないかと考えているものの、なかなか利用できないでいます。今まで作成してきた画像描画・加工ルーチンはツールキットと独立させているので、GTK+ 用と Qt 用にそれぞれ対応することもそれほど大変ではないんですが、あとはやる気の問題でしょうか。

The GTK+ Project

Qt Project

Glade - A User Interface Designer  

2014年06月07日

C++ メモランダム (ifstream)

相変わらず急に雨が降りだしたりして不安定な天気です。体の方もいまいち調子悪いです。

プログラムに渡すデータを標準入力とファイルの両方からに対応したい場合、C 言語で fopen 等を使う場合は

FILE* fp;
char* fileName;
  :
if ( fileName != NULL ) {
  if ( ( fp = fopen( fileName, "r" ) ) == NULL ) {
    fprintf( stderr, "can't open file %s.\n", fileName );
  }
} else {
  fp = stdin;
}

とすれば可能ですが、C++ で入力ストリームを使う場合

std::ifstream ifs;
string fileName;
  :
if ( ! fileName.empty() ) {
  ifs.open( fileName.c_str() );
  if ( ! ifs ) {
    std::cerr << "can't open file << fileName << std::endl;
  }
} else {
  ifs = std::cin;
}

とはできないんですよね。std::cin は std::ifstream の基底クラス std::istream なのでそのままでは代入ができないわけです。このときは、

std::ifstream* ifs;
string fileName;
  :
if ( ! fileName.empty() ) {
  ifs = new std::ifstream( fileName.c_str() );
  if ( ! *ifs ) {
    std::cerr << "can't open file << fileName << std::endl;
  }
} else {
  ifs = &( std::cin );
}

とポインタ (または参照) を使えば解決します。以前にも同じことで悩んで調べるハメになってしまったので、忘れないようにメモしておきます。