MASATOの開発日記 2000年11月


11/26

とあるクラスを実装しているときに、ちょっと共通部分があったので、privateメンバ関数(非virtual)にして纏めてみた。 ・・・そしたらそのクラスのhファイルをincludeしている全てのcppファイルがコンパイルされた・・・。 うーむ。まあ、なんでそうなるのかは分かる。VC++も頑張っているのかスキップだのなんだの言って高速に処理してくれる。 のだけれど、hファイルを、外部に公開するためのヘッダファイルと考えると、 公開してないprivate関数を追加した場合は、ヘッダファイルを変更したくないのが本音。 けれどC++の仕様では、private関数を追加するにはクラスの定義を変更しなければならない。 そこで代替策が出てくる。ずばり、staticグローバル関数を使ってしまう。 これならば新しく追加しようと引数の数を変えようとヘッダファイルの変更の必要は無い。 第1引数にクラスオブジェクトへのポインタ(つまり、this)を渡してあげれば、 メンバ変数にも問題無くアクセスできる・・・いやできない!privateやprotectedな変数にはアクセスできないのだ。 じゃあfriend宣言でもするか・・・って結局クラス定義を書き換えなくてはならなくなってしまう。 そこでさらなる代替策。もとのクラスをAとすると、A_friendという新しいクラスを作る。 でもってクラスAの定義の中にfriend A_friend;と入れておく。それでもって、A_friendの定義は、 全てcppの中に書く。Aにprivate関数を加えたくなったら、A_friendにstaticなメンバ関数を追加し、 それの第1引数はA*(つまりthis)とする。うむ完璧だ。いくらA_friendクラスにstaticメンバ関数を加えても、 hファイルは何も変わらない。A_friendクラスからはAのprivate及びprotectedなメンバにアクセスできるので、 Aのprivate関数と同等の処理が出来る。よし、これからはこれで行くか。・・・。・・・。 だあああああ。めんどくせええええええ。これだったらprivate関数追加してコンパイル時間を待った方が楽だ。 private関数をcppに隠蔽するという目的は達したが、楽にコードを書くという目的は達成できなかった。 本末転倒というかなんとゆーか。

(Visual C++)CSocketの使い方(その5)。

その1〜その4ままで作成してきたクラス群の使用例を示します。

まず、最初にサーバー側が受け入れ準備をします。 ポート番号は、他のアプリケーションが使わないような値を指定して下さい。
まずは、適切なクラスにメンバ変数を追加します。 SDI/MDIならば、ドキュメントクラス、ダイアログベースならばダイアログクラスかアプリケーションクラスが お勧めですが、より適切なクラスがあるならばそちらに置いて下さい。

CAcceptSocket m_Accept;
そして、サーバーを待機状態にしたいときにこのコードを実行しましょう。
m_Accept.Create(ポート番号);
m_Accept.Listen();
これで受け入れ準備は完了です。クライアントが接続してくると、勝手にOnAcceptが呼ばれますので、 CClientSocketが作成されて、接続が確立します。なお、このコードのように、accept変数をローカルにすると、変数がスコープを外れた時に、クライアントは接続できなくなりますので注意しましょう。これ以降は、OnAccept内で作成された CClientSocketオブジェクトのメンバ変数m_lpOutputに対して出力し、入力はOnReceive内で m_lpInputから読みこみましょう。

お次はクライアントからの接続方法です。
やはり適切なクラスにメンバ変数を配置します。

CConnectSocket m_Connect;
そして、接続したい時に次のコードを実行します。
connect.Create();
connect.Connect(接続先IP,ポート番号);
connect.Init();
これで接続完了です。これ以降は、connect.m_lpOutputに対してデーターを出力しましょう。 入力はOnReceive内でm_lpInputより読みこみます。 CClientSocketと使い方は同じですね。

m_lpOutputに対してデーターの出力を終えたら、m_lpOutput->Flush()を呼び出さないと実際の送信が終わりませんので注意しましょう。
また、TCPの特徴として、送ったデーターが繋がって届いたり、ばらばらになって届いたりする可能性があります。 ばらばらになって届いた場合は、m_lpInputから入力するときにブロックされるのでさほど問題は無いのですが、 繋がって届いた時にしばしば問題が発生します。送った方は2つのデーターを送ったつもりでも、 受け取る方はそれがくっついた一つのデーターを受け取ります。この場合、OnReceiveは1回しか呼ばれません。 そのため、OnReceiveでは、受け取ったデーターが無くなるまで処理をする、 というコードを記述しなければなりません。そのために便利なのが、m_lpInput->IsBufferEmpty()です。 受け取りバッファが空になるとTRUEになりますので、これがTRUEになるまでm_lpInputに対する読みこみ処理を繰り返し行うようにしましょう。

(2000/1/14) もうちょっと分かりやすくなるよう推敲。

11/3

初心者向けFAQ
Q、VC++でボタンの色を変えたのですが、どうやれば良いのでしょうか?
A、諦めましょう。
Q、VC++で、リストボックスの各行毎に色を変えたいのですが、どうやれば良いのでしょうか?
A、諦めましょう。
Q、VC++で、読みこんだBitmapを保存したいのですが、どうやれば良いのでしょうか?
A、諦めましょう。

Q&Aじゃないですねこれ。もちろん実際には実行する方法はあります。 ですがどれも複雑で初心者には手が出ないのが本当の所でしょう。一言二言のアドバイスで出来るものでもありません。 このように、結構誰もがやりたいと思うもの、つまり需要が高いものでも、実行するのは結構困難である、 というものが結構あります。初心者のうちにそういったものに出会ったらどうすれば良いのでしょうか? 答えは「諦めましょう」です。ええそうなんです。諦めて、別のものを作りましょう。 そうしていって実力を上げたら、過去につまずいたものに挑戦しましょう。 ですが現実の質問では諦めましょう、とは言い難いんですよね〜。相手の実力も分かりませんし。 そこでオーナードローを使いましょう、と答えてしまうんですけどね。 これだけの情報ではまず出来ないでしょう。「何事も諦めが肝心」

(Visual C++)CSocketの使い方(その4)。

お次はクライアントアプリケーションのソケットの記述です。

CConnectSocketに、次のメンバ変数を追加します。

CSocketFile* m_lpFile;
CArchive* m_lpInput;
CArchive* m_lpOutput;
CClientSocketと同様に、これがサーバーとのデーター送受信を行います。

お次はメンバ関数のコードの記述です。以下の関数を加えましょう。

CConnectSocket::CConnectSocket()
{
	m_lpFile = NULL;
	m_lpInput = NULL;
	m_lpOutput = NULL;
}
CConnectSocket::~CConnectSocket()
{
	Release();
}
CConnectSocket::Init()
{
	Release();
	m_lpFile = new CSocketFile(this, TRUE);
	m_lpInput = new CArchive(m_lpFile, CArchive::load);
	m_lpOutput = new CArchive(m_lpFile, CArchive::store);
}
CConnectSocket::Release()
{
	if (m_lpInput != NULL)delete m_lpInput;
	if (m_lpOutput != NULL)delete m_lpOutput;
	if (m_lpFile != NULL)delete m_lpFile;
	m_lpInput = NULL;
	m_lpOutput = NULL;
	m_lpFile = NULL;
}
これで準備は完了です。実際にどの順番で呼ぶのかはまた次回・・・

(2000/1/14) 省略部分も全部書いて推敲。


戻る

masato@mb.kcom.ne.jp