こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

C言語のコールバック

Java/C#/PHPといった言語はすでに使えるのですが、現在C言語およびWin32 APIを勉強中です。

C言語の関数にはJavaなどのインスタンスメソッドのthisにあたる引数が渡されませんが、Win32 APIのウィンドウプロシージャに代表されるコールバック関数において不都合があります。Javaの場合は、

interface Callback{
void onCallback();
}

といったインターフェイスが定義されているものとして、

hoge(new Callback(){
int data = 10;
public void onCallback(){
System.out.println(data);
}
});

のようなコードで引数以外のデータも参照できますが、C言語ではこのように引数で渡せないデータを参照するにはどうしたらよいでしょうか。グローバル変数として参照するのは、Javaのstaticなフィールドと同様に拡張性を損なうので避けたいと思います。

投稿日時 - 2007-12-22 15:30:11

QNo.3618838

困ってます

質問者が選んだベストアンサー

C言語のコールバックは、明示的にthis相当のデータを引数に
渡すよう定義するのが一般的かと思います。

Win32などは、ほぼ例外なく、Handleや、その他のvoid*を取れます。
# HWNDの場合などは、GetWindowLongPtr等のAPIで、
# HWMDに紐づいた情報をとることができるので引数にはありませんが。

引数にvoid*等があると、そこにthis(実態はポインタ)を
入れたり、必要な情報をまとめた構造体のポインタを入れたりが
可能です。

まぁC言語でも多くの場合はそういうものを渡して使いますし、
Javaは、そのあたりを暗黙に渡せるようになっているだけかと。
逆に、Javaを使うと不要な場合でも余分なthisの情報が
内部的に渡されるので、C言語開発当時の思想ではそういうものを
サポートしていないのが割りと普通です。

# C言語の場合には、必須でないものはプログラマに選択権を
# 与える(言語が勝手に新設の押し売りをしない)のが基本姿勢
# なので、勝手にそういった情報を渡してくれない。
# 必要な場合のみ、プログラマがその情報を渡す。
# (OSを書いたりするためのものですし、性能優先です)
# で、時代は下りマシンパワーもあがり、アプリを書くための
# 言語Javaでは、そのあたりもお手軽に使いやすいようになってる。

投稿日時 - 2007-12-22 23:43:38

補足

ご回答ありがとうございます。
教えていただいたGetWindowLongPtrやcbWndExtraのキーワードで
検索したら情報が見つかりました。ウィンドウプロシージャに
void*な引数がないので困っていましたが、cbWndExtraのような
領域を確保してウィンドウごとのデータを持つわけですね。
Javaでいえばjava.awt.Frameにユーザデータを格納する
java.lang.Object型のフィールドがあるようなもので、
普段Javaなどを使っている身からすればいささか気持ちが
わるいですが、そういうものなのでしょうね……。
だいたい、this(に相当するもの)の型がvoid*というのも
ダサいと思います。このようなケースでC言語では型安全なAPIの
設計は出来ないのでしょうか?

>プログラマに選択権を与える
というのもわかりますが、ライブラリの設計者が
コールバック関数にthisが確実に必要ない、と言い切れる場面が
自分には思いつきません。
>Win32などは、ほぼ例外なく、Handleや、その他のvoid*を取れます。
ということは、ほぼすべてのコールバック関数でthisのようなものが
必要になる可能性があるということでしょうから、選択肢は
複数あっても実際に有効な選択肢は常にひとつに見えます。
thisが無ければ、まさかグローバル変数をそのまま使って
拡張性を犠牲にしたくはありませんし、ウィンドウをキーにした
ハッシュテーブルでデータを格納してもオーバーヘッドや
コーディングの手間が気になります。
所詮は設計の古い言語であるということでしょうか?

投稿日時 - 2007-12-23 04:19:30

ANo.1

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(4)

ANo.4

OOP+αに対応したC++ならthisがありますが、
C言語はそもそもOOP用じゃないですから。

C++とからなら、class Objectなりを定義することもできれば、
インターフェイス的な純粋仮想クラスFooInterfaceなんてのも
作れますけど、「そういう枠組みも無い方がいい(型情報が邪魔)」
というケースも、泥臭いコードを書く際には必要になるわけで、
C言語ってのは他の方も書かれているように、そういうケースにも
対応できてそれなりに汎用的な、ある意味よくできた言語です。

Javaがオートマの軽自動車だとしたら、C言語の根底はF1カーですから、
F1カーで一般人が町乗りしたら面倒なのは当然かと。

エンジンの回転数やクラッチワーク等に常に気を使うのは
面倒だという主張は理解しますが、それが、設計思想の違いであって、
用途に合わない使い方(使わされ方)に問題はないですか?

ちなみに、現代でも、例えば組込環境などでクリティカルな処理を
書くと数バイト/数ステップが気になることもあります。

理由としては、大量生産する組込機器(電気製品の中身)だと、
「メモリの値段が製造コストに結構影響するから」だったり、
「上位(例えばJavaアプリ)の動作に積もり積もって
速度差が出るから」だったり、「xxms以下で処理が完了しないと
事故が起きるから」とかだったりしますが。

こういう、メモリ制約/時間制約を受けてソフトが書ける熟練の
プログラマは減ってますし、メモリとの単価差は減ってきてたり、
CPUの速度/値段が下がったりもしてますが、それでも、
「少しでも低い性能のハードの方が安い」とかあるわけで、
減ってると言ってもその環境で最適に動作するためにはやっぱり、
チューニングとしてそういうことはありますよ。

# Javaとかだと気にしない(仮にしたくてもできない)部分ですけど。

投稿日時 - 2007-12-24 17:53:08

補足

ご回答ありがとうございました。
OOPでなくともthisにあたるものは必要になると思うのです。
自分もC言語の役割は未だ失われてはいないだろうと思って
勉強を始めました。C言語の設計思想、もうすこし学んでみようかと
思います。
当初の質問からちょっと脱線させてしまいましたので、
反省しつつこのあたりで締め切りたいと思います。

投稿日時 - 2007-12-24 23:23:55

ANo.3

それは単にC言語というものに対する勘違いでしかありません。
他の回答者様もおっしゃっているようにC言語はアセンブリ言語です。
ベタベタなアセンブリ言語ですから、実装したいことはご自分でコードを書いてください。
それがアセンブリ言語の仕様です。

投稿日時 - 2007-12-24 16:03:54

補足

ご回答ありがとうございます。
型安全なコールバック関数というものを実装したくなりましたが、
どう自分でコードを書いてもC言語では難しそうです。
それをC言語に求めること自体が誤りだということなんですね。

投稿日時 - 2007-12-24 23:11:59

ANo.2

「設計が古い」というのは, まあ確かに設計されたのは古いけど, むしろそれは「設計思想」という観点でいくべきではないかなぁ?
#1 でもいわれてますが, ぶっちゃけいえば, C の設計思想は
・プログラマができることはコンパイラではしない
・プログラマは全て理解している
です. もともと OS を書くための「構造化アセンブラ」として開発されてますし, 当時のシステムを想定すればしかたのないことでしょう. システム全体で 48kword しかないこともありましたから, そのうちの 1word も無駄にはできないという事情があります.

投稿日時 - 2007-12-23 19:57:41

補足

ご回答ありがとうございます。
おっしゃるとおり、確かにAPIの設計者がthisにあたる引数を
ちゃんと定義すればいいわけで、Javaのように言語仕様として
勝手にthisを定義する必要性は無いかと思います。まず間違いなく
thisは定義しておく必要はあるとは思うのですけれど……。
システム全体で48kword!?現在の環境しか知らない自分には
想像もつきませんが、確かに無駄は一切許されない時代も
あったのでしょうね。とはいえ、そのあたりの思想に現代でも
納得のいく意味づけがないと、C言語でのコーディングは
苦痛でしかありません……。

投稿日時 - 2007-12-24 03:52:41

あなたにオススメの質問