2019年04月14日

クラス・テンプレートのコピー・コンストラクタについて

今日は昼から雨が降り始めました。明日も雨のようです。桜もだいぶ散ってしまいそうです。

散歩の途中で撮影した桜です。毎年、同じような写真をアップロードしているような気がします。
桜

今回はプログラミングの話題です。以下のようなクラス・テンプレートを作成したとします。

template< class T >
class Test
{
public:

  Test( T t )
  { cout << "Test( T t ) called" << endl; }

  template< class U >
  Test( const Test< U >& t )
  { cout << "Test( const Test< U >& t ) called" << endl; }

  template< class U >
  Test& operator=( const Test< U >& t )
  { cout << "Test& operator=( const Test< U >& t ) called" << endl; return( *this ); }
};

次のように使ってみます。

Test< double > testDbl( 1 );
Test< int > testIntCopy( testDbl );
Test< double > testDblCopy( testDbl );
testIntCopy = testDbl;
testDblCopy = testDbl;

今まで、テンプレートを使った任意の型への変換用コンストラクタから、同じ型へのコピー・コンストラクタが作成されるものと思っていました。しかし、実行結果は

Test( T t ) called
Test( const Test< U >& t ) called
Test& operator=( const Test< U >& t ) called

となって、同じ型へのコピー・コンストラクタはコンパイラが自動的に作成したものが使われます。ちゃんと明示したければ、

Test( const Test& t )
{ cout << "Test( const Test& t ) called" << endl; }

Test& operator=( const Test& t )
{ cout << "Test& operator=( const Test& t ) called" << endl; return( *this ); }

という感じに定義してあげないとダメでした。これらを追加すると

Test( T t ) called
Test( const Test< U >& t ) called
Test( const Test& t ) called
Test& operator=( const Test< U >& t ) called
Test& operator=( const Test& t ) called

という風に出力されます。以前作ったプログラムで同じ誤りがあって、よく今まで問題が発生しなかったものだと少し焦りました。その後、「プログラム言語 C++」を見たらちゃんと書いてありました。  

2019年01月27日

大坂なおみ

全豪オープンで大坂なおみ選手が見事優勝を果たし、テレビや新聞で大々的に取り上げられてました。
そんな中で、男子の方はジョコビッチが優勝。これも何気にすごいと思います。

-----

C++ でおなじみのテンプレートですが、テンプレート引数にもテンプレートを使うことができます。例えばこんな感じです。

template< class T, template< class > class Op >
SomeClass
{
Op< T > op_;
:
};

テンプレート引数を付けないと SomeClass< double, SomeOp< double > > と書かなければならないのが、上記のようにすることで SomeClass< double, SomeOp > だけで済むようになります。誤って SomeOp< int > と書いてしまうようなミスも防げるというわけです。但し、利用できるのはクラステンプレートのみです。
意外と知られていないように思うので紹介しておきます。「プログラミング言語 C++」にも書かれていますが、自分は別の書籍で知りました。  

2016年10月30日

自己組織化写像(SOM)

めっきり寒くなってきました。今年も残り二ヶ月ですね。

クラスタリングの一つ、SOM ( Self-organizing maps ) のサンプル・プログラムを作成してみました。SOM を使うことで、高次元のデータを二次元マップ上に写像して視覚的に分類することが可能になります。
紹介しているサイトを見ると、理解しやすい色のクラスタリングが例としてよく挙がっていますが、巡回サラリーマン問題の近似解を求める応用例を見つけてさっそく試してみました。二次元データを二次元マップに写像することで目的を達成することができます。

巡回サラリーマン問題

初期値 "×" を与えて訓練すると、"△" のノードに次第に近づいていきます。真の解ではないですが、割りといい結果が得られているのではないかと思います。この使い方を見つけてから SOM を使うのがなかなか面白くなってきました。

クラスタリングには他にも凝集型クラスタリングや K-平均法、EM アルゴリズムなどがありますが、現在、順番にサンプル・プログラムを作成しようとしているところです。年内にできれば一度更新したいですね。  

2016年06月26日

演算子の多重定義

今日はそれほど暑くもなく、カラッとしたいい天気でした。しかし、明日からまた雨のようですね。

C++ には「演算子の多重定義」というものがあります。任意の型に対して、int 型や double 型などと同等に演算子が利用できるというものです。

SomeClass SomeClass::operator+( const SomeClass& s1, const SomeClass& s2 );

という関数を実装すれば、

SomeClass s1, s2;
:
// 何らかの方法で値を代入
:
SomeClass add = s1 + s2;

と書くことができます。四則演算だけではなく、等号・不等号演算子やポインタ・参照演算子なども多重定義できるので、うまく利用すれば非常に便利な反面、やみくもに使うと混乱の元とも言われています。例えば、ベクトルに対して積の演算子を多重定義した場合、それは内積なのか外積なのかというのは利用する場面によって変わります。こんなときは素直に innerproduct, outerproduct と通常の関数を用意したほうが混乱しなくて済みます。

演算子の多重定義の実体は関数やメンバ関数です。そうなると、演算子の優先順位はどうなるのかが気になるところです。簡単なサンプルを作ってテストしてみました。

/// 10 を法とした合同算
struct Mod10
{
  unsigned int r; // 10 を法としたときの値

  // コンストラクタ
  Mod10() : r( 0 ) {}

  // 積の代入演算子の多重定義
  Mod10& operator*=( const Mod10& i )
  {
    r = ( r * i.r ) % 10;
    return( *this );
  }

  // 和の代入演算子の多重定義
  Mod10& operator+=( const Mod10& i )
  {
    r = ( r + i.r ) % 10;
    return( *this );
  }
};

// 積の演算子の多重定義
Mod10 operator*( const Mod10& i1, const Mod10& i2 )
{
  Mod10 buff( i1 );
  buff *= i2;

  return( buff );
}

// 和の演算子の多重定義
Mod10 operator+( const Mod10& i1, const Mod10& i2 )
{
  Mod10 buff( i1 );
  buff += i2;

  return( buff );
}

int main( int argc, char* argv[] )
{
  if ( argc < 3 ) return( -1 );

  unsigned int i = atoi( argv[1] );
  unsigned int j = atoi( argv[2] );

  Mod10 m1; m1.r = i;
  Mod10 m2; m2.r = j;

  Mod10 ans1 = m2 * m1 + m1;
  Mod10 ans2 = m1 + m1 * m2;
  Mod10 ans3 = ( m1 + m1 ) * m2;

  std::cout << ans1.r << std::endl;
  std::cout << ans2.r << std::endl;
  std::cout << ans3.r << std::endl;
}

演算子の優先順位を考慮していなければ、関数に置き換えると

Mod10 ans1 = operator*( m2, operator+( m1, m1 ) );
Mod10 ans2 = operator+( m1, operator*( m1, m2 ) );

Mod10 ans1 = operator+( operator*( m2, m1 ), m1 );
Mod10 ans2 = operator*( operator+( m1, m1 ), m2 );

となって、ans1 と ans2 の値は異なるのではないかと予想していましたが、例えば m1 = 2, m2 = 3 とすると、

8
8
2

となってちゃんと優先順位が考慮されていました。で、「プログラミング言語 C++」を調べてみたら、最初の方に「優先順位を考慮している」ときちんと記述されていました。それにしても、今まで何回か使ったことがあるのに疑問に思わなかったというのが少々情けない。。。  

2015年11月01日

コンストラクタと例外

エラー処理の話の続きです。

C++ での例外処理でよく話題になるのが「コンストラクタで例外を使ってよいか」という件。今ではガンガン使うようにしています。
コンストラクタは戻り値が得られないので、インスタンス化に失敗したかどうかを知るためには、成否を判断するためのフラグをメンバ変数として設けておいて実際に利用する前に判断させるというのがよく使われる方法です。以前は例外の使用は極力避けるようにしていたのでこの方法を採用していましたが、コンストラクタ内で利用する関数が例外を投げた場合を想定してきちんと捕捉して処理しなければならずコーディングが面倒になるし、単にフラグだけでは内部で何が起こったのかわからないので呼び出し側から見ても使いづらいところはあります ( メッセージ出力したり、エラー・コードを管理する手もありますがますますコーディングが大変になってきます) 。なので、今では逆に例外を多用するようにしています。

コンストラクタでの例外を禁止する理由は一般に「メモリ・リーク」の原因になるというものですが、内部で例外が発生したのならそれを捕捉して後処理をしてから再スローすれば済みます。あるサイトで見かけたのがこういうタイプのもの。

SomeClass* p = 0;
try
{
  :
  p = new SomeClass();
  :
}
cacth (...)
{
  (後処理)
}
delete p;

コンストラクタ内で例外が発生したら、p にアドレスが代入される前に catch 節に写ってしまうので、最後の delete p でもオブジェクトは解体されず、メモリ・リークになってしまうように見えます。しかし、ここはちゃんと対策されていて、コンストラクタで例外が投げられたら自動的に delete がよばれ、SomeClass 用に確保されたメモリはきちんと開放されます。但し、SomeClass のデストラクタは呼び出されないので、コンストラクタ内で new を使っている場合は例外を捕捉して後処理を行うことは必須となります。

クラス内で使うメンバのサイズが大きくなければ、new を使って動的にメモリ確保しない方がソースはすっきりしますが、そういうわけにもいかないですよね。  

2015年10月25日

エラー処理

趣味であれ仕事であれ、プログラム作成で一番頭を悩ませるのはエラー処理だと思っています。

何か新しいアルゴリズムを考案したり理解したりしてプログラムを作成するというのは、少なくとも日曜プログラミングなんかをやっている方なら非常に楽しい作業なのではないかと思います。しかし、不測の事態が起きた場合や、意図しない入力があった場合に備えてきちんとしたエラー処理を組み込もうとした途端に楽しかったはずの作業が苦痛に変わります。個人的には、デバッグ同様できるだけ避けたい作業の一つで、退屈なだけにエラー処理を考える方が苦痛に感じます。
エラー処理は、プログラムも冗長にしがちです。それを避けるために「例外処理」という機構が C++ や Java、C# などでは用意されています。例外を上手に使えば、プログラムを非常にシンプルにすることができます。しかし、C 言語などで組んでいるために既存のプログラムが例外を全く使っていない場合、そのプログラムは全て見直しが必要になるので、google のスタイルガイドでは「C++ で例外は使ってはいけない」としています。

例外処理を利用しない場合、外部から利用するための関数を作成した時に外部にエラーを通知するためには

1) エラーコードを戻り値として渡す
2) エラーメッセージを画面に出力する
3) 何もせずに戻る

くらいの方法が考えられます。
戻り値にエラーコードを返す場合、戻り値は通常、他の用途に使えなくなるので、何らかの値を得たい場合は引数を通して得る必要があります。また、エラーコード自体もきちんと管理しなければなりません。それから、エラーメッセージを出力しても、それがプログラム側に伝わるわけではなく、これだけで充分ということはまずないでしょう。何もしないというのは、外部にエラーが発生したことが検知できないことを意味しますが、結果からそれを知ることができれば充分ということも場合によってはあります。たいていは、エラーコードとエラーメッセージの組み合わせを利用しますが、それを呼び出し元がきちんと見てくれるかという問題もあり、関数が出力するエラー全てに対して一つ一つきちんと対応するコードを書いていたらキリがなく、どこかで妥協することが多くなります。

例外処理を利用すると、例外は呼び出し元に次々と伝播していきます。通常のアプリなら、メイン・ルーチンのところだけで例外を捕捉して、メッセージを出力して終了としておくだけでも十分な場合が多く、それなら途中の関数などで例外の補足時に行わなければならないことを必要最低限な部分だけ書いておけば、エラー処理部分はかなり簡潔にまとめることができます。

ということで、特に Java や C# であれば、エラー処理は基本的に例外を使うことになるし、C++ だけでコーディングするのなら迷わず例外を使うべきでしょうね。  

2015年10月11日

ポインタの呪い

明日は体育の日で休みですね。

C言語を使う上で避けることのできない(使わなくてもなんとかなるかもしれませんが)概念に「ポインタ」があります。

int i = 3;
int* ip = &i;

と書くと、ip は i へのポインタ ( i を指し示すもの ) となって、

int j = *ip;

と書けば j に ip が指し示す i の値が代入され、

*ip = 6;

と書けば ip が指し示す i に 6 が代入されます。

理解すれば非常に便利なポインタですが、これのせいで C 言語のマスターをあきらめたという人も多いようです。たいていは、変数がメモリ上に保持される様子を使って説明されていたりして、特に初心者にとってはわかりづらいのではないのでしょうか。また、文法そのものもとっつきにくい原因となっていると思います。関数からポインタそのものを受け取りたいような場合は

void f( size_t sz, char** cpp )
{
  *cpp = malloc( sz );
}

という具合に '**' で「ポインタのポインタ(ハンドル)」を表します。さらに関数ポインタの配列などは型の書き方がややこしくてたいていは忘れてしまい、本を見て思い出すといった具合です。C++ では参照が使えるようになって、ポインタはあまり意識しなくてもいいようになりました。関数ポインタも、関数オブジェクトというさらに便利な機能によってほとんど利用せずに済みます。
さらに Java や C# は「オブジェクトは参照渡しで組み込み変数は値渡し」と決められているので、完全にポインタのようなややこしい部分は意識しなくてよくなったわけですが、「参照渡しだから」ということで関数内で新しいインスタンスを構築して渡そうとしてうまくいかないといった失敗例をよく見かけます。

void f( SomeClass sc )
{
  sc = new sc( ... );
}

この場合、C 言語でいうところの "普通の" ポインタ渡しを意味するので、オブジェクトのある位置が変数として渡されるだけです。その変数に新たなインスタンスの位置を代入しても、元のインスタンスは影響を受けないので意味がないわけです。ちなみに C# の場合はハンドルを渡すために ref キーワードがありますね。

void f( ref SomeClass sc )
{
  sc = new sc( ... );
}

とすれば、意図したとおり新たなインスタンスで書き換えてくれます。

もし、このあたりで悩んでいる方がいれば、参考になれば幸いです。  

2015年09月12日

プログラミングの勧め

大雨が過ぎたら今度は地震。どうやら明日も雨になりそうですね。

仕事で作業をもっと短時間でできるようにシステム化できないかと相談があり、打ち合わせした時の話です。処理条件変更後のパラメータの変化を確認するためグラフで一枚一枚出力しながら目視確認していて、パラメータも処理のステップも大量にあるので全て見切れないということで、グラフを一度に全て出力できないかというのが要望だったわけですが、大量のグラフをまとめて書かせようとすると結構大変なのでどうやって処理させようかと話していたとき、ふと、グラフのプロットが二点しかないのを見て「どの程度のプロット数になるのですか」と質問すると、「1, 2 点です」との回答。じゃあ、表形式にしてスペックアウトしたら色付けすればということであっさりと解決しました。
なぜわざわざグラフを使っていたかというと、それしか手段を知らなかったからということで、本当はデータを取得するツールはあるので工夫すればいくらでも効率よく作業できるようになるわけですが、そういうところは苦手なのかなと感じました。今はそれぞれの分野が専業的になって、データ加工などの仕組みはいわゆる IT 分野の仕事となっています。でも、簡単なものなら自前で作成できたほうが何かといいんじゃないかなと思ってます。IT 担当の数も限られていますしね。

プログラムの世界も敷居が低くなったと言われますが、本当の初心者にとっては昔より逆に高くなっているのかもしれません。オブジェクト指向にシフトして大きなプログラムは組みやすくなりました。でも、ちょっとしたものなら C 言語のような手続き型言語でも十分というよりかえって効率はいいような気がします。自分が最初に作ったプログラムは、PC-88 上の BASIC で書いた「ブロックくずし」でした。本に書いてあったものをまずは読みながら組み上げて、後でいろいろと変更して遊んだりしてました。こういう学習の仕方は本当に効果的だと感じました。ちょっと使い方がわかれば、プログラミングは仕事でもプライベートでも非常に強力な味方になってくれるはずなので、参考にしてもらえればと思います。  

2015年08月30日

暑さが思考を鈍らせる

大雨の日が続きますね。しかし、おかげで涼しくなってきました。

最近になって思うのは、温度より湿度のほうが「暑苦しい」という感覚に影響しているということです。30 度を超えていても湿度の低い日は案外エアコンなしで大丈夫だったりしました。逆に 30 度以下でも湿度が高いときは耐えられないですね。だいたい 60% くらいがリミットでしょうか。

暑いと思考回路も鈍くなるということで、今日はどうでもいいようなところで悩んでいました。

例えば、下のようなクラスを作ります。


template< class T >
class Test
{
  T* t_;

public:

  Test( T& t ) : t_( &t ) {}

  void print( size_t i )
  { std::cout << (*t_)[i] << std::endl; }
};


使うときはこんな感じになります。


int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, };
std::vector<int> vec( &data[0], &data[sizeof(data) / sizeof(data[0])] );

Test< std::vector<int> > test1( vec );
test1.print( 1 );


意図した通り、これで 1 が出力されます。しかし、


Test< int* > test2( &data[0] );
test2.print( 1 );


とすると下のようなエラーが出力されます。

error: no matching function for call to ‘Test::Test(int*)’

いろいろ試行錯誤してみると


Test< int*const > test2( &data[0] );


のようにポインタを定数とすればちゃんとコンパイルできました。実際はもう少し複雑なクラスの中で発生した問題だったので、最初はなぜこのような動作になるのか全くわからず、コンパイラのバグじゃないのと疑う始末。で、上に書いたようなクラスを使って検証した結果、


int* p = &data[0];
Test< int* > test2( p );


とすれば正常にコンパイルすることができて、&data[0] として渡した場合、定数ではないポインタへの参照としては扱われないということを理解しました。先週はこれにずっと頭を悩ませていたわけで、理由がわかって何かスッキリしました。というより、もっと早く気付けと過去の自分に言いたい  

2015年07月12日

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

大相撲の名古屋場所が始まりました。いよいよ本格的な夏のスタートという気分です。

今日は全国的に真夏日ということで、名古屋も非常に暑い一日でした。熱射病や水の事故のニュースも流れていて、これから要注意ですね。台風の影響で大雨なんかにも気をつけたいところです。

-----

C++ の持つ機能の一つであるテンプレートの最も簡単な使い方として、任意の型に対して同じ処理を行うような場合にまとめて書いておくというものがあります。

template<class T> T sum( T t1, T t2 )
{ return( t1 + t2 ); }

型 T が和の演算子 ( + ) を使えるならどんな型でもこの関数が適用できます。特別バージョンを作りたければ

template<> SomeClass sum( SomeClass c1, SomeClass c2 )
{
SomeClass c = c1;
c.add( c2 );
return( c );
}

としておけば、引数が SomeClass の場合だけ下側が使われるようになります。

テンプレート関数は宣言だけしておくこともできます。例えば、sin の逆関数である asin は、float, double, long double 用に asinf, asin, asinl と関数名が分かれていますが、

template<class T> T Asin( T t );
template<> inline double Asin<double>( double t )
{ return( asin( t ) ); }
template<> inline long double Asin<long double>( long double t )
{ return( asinl( t ) ); }
template<> inline float Asin<float>( float t )
{ return( asinf( t ) ); }

としておけば、呼び出し側で型を変えても自動的に追随してくれます。特別バージョンだけ実装しておいても、他の型を利用しない限りはコンパイルできます。

ようやくテンプレートを上手に使いこなすための知識が身についてきて、様々なところで利用するようになってきました。やはり、問題になるのはエラーが出た時に内容が理解しづらいというところでしょうか。これも慣れるに従ってそれほど苦にはならなくなるものの、初めて見るようなエラーメッセージに対しては今でもしばらく悩むことがあります。  続きを読む

2015年06月28日

「間抜け」でソース管理

仕事でソース管理ソフトの git を利用することになり、個人のソース管理にも使ってみることにしました。

昔、CVS や Subversion で個人で作成したソースの管理をしてみましたが、途中ですぐに利用しなくなり、結局は「リネームしてバックアップ」というやり方で済ませていました。仕事では Visual Studio に付属している Visual Source Safe を使い、すでにサポート切れなのにもかかわらず、過去のプロジェクトでは未だに動いています。
最近、職場で git を使ってソース管理をするということになり、使い方を勉強中です。実は、前に一度だけ使ったことがあるものの、やはりすぐに利用しなくなりました。しかし、最近はソースの数も増えてゴチャゴチャとしてきたので、そろそろ整理することも含めてちゃんと使ってみようかと考えています。さて、今度はきちんと使いこなせるのでしょうか。

git は、Linux の生みの親である Linus Torvalds さんによって開発されました。Linus さん曰く "git" は自分にちなんだ名前として付けたそうです。その意味は「ばか」とか「間抜け」。なんとも自虐的なギャグです。  

2015年06月21日

父の日

今日は「父の日」です。

ちなみに台湾では八月八日が「パパ ( 88 ) の日」という語呂合わせで父の日になっているそうです。日付はバラバラですが、世界中にあるんですね。

コンピュータに数値演算をさせる場合、どうしても誤差が避けられないわけですが、少し前に標本分散の計算で結果が負数になる現象が発生したことがあります。標本分散は通常、平均と各データの差の二乗和から得ますが、これだと複数の集団で個々に計算しておいて後でまとめるというようなことができません。そこで、よく使われるのが

標本分散 = 二乗和 / データ数 - 平均の二乗 = Σx2 / N - m2

という公式で、データ数・総和・二乗和を計算しておけば、後で複数の集団をまとめて計算することもこの公式からできます。ところが、全てのデータが等しいような場合に、誤差の関係で計算結果が負数になることがあります(分散は負数になることはあり得ません。データが全て等しければ分散はゼロです)。
そこで、精度を上げるために以下のように式を変形します。

標本分散 = Σx2 / N - ( Σx / N )2 = [ NΣx2 - (Σx)2 ] / N2

これできちんとした値が得られるようになりました。除算で桁落ちが発生すると有効桁数が少なくなるので、加減算をしてから除算するようにしたわけです。なお、念のために負数になったらゼロになるようにチェックも入れておきました。

プログラムの中で、数値演算はやはり一番難しい部分の一つですね。  

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