プログラミング ノート

2007.1.6初
2007.8.10改

■UNIXプログラミング

□ ネットワークプログラミング(サーバー)

関連リンク

基本

SIGPIPEを無視 signal( )
リスニングソケットの作成 socket( )
ソケットオプションの設定 setsocketopt( )
アドレスファミリ・ポート番号・IPアドレス設定 struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
sin.sin_addr.s_addr = htonl(INADDR_ANY);
バインド bind( )
接続を待つ listen( )
コネクト待ちでブロックをしないようにする。 fcntl( )
接続要求があったかのチェック poll( )
コネクションの受付 accept( )
データの読み出し select( )
read( )
データの書き出し write( )
10 クローズ処理 close(connect_socket);
close(listen_socket);
それぞれの関数につき、JMprojectなどで調べると詳しく解説されている。ネットワークソケットの基本はsocket.7を参照すべし。

ポイント

□ プロセス制御

キーワード

□ 共有メモリー

キーワード

■VC++ プログラミング

関連リンク

□ コントロール

□ 数値型変数の使い方

コントロール変数の数値を取り込んだり、コントロールへ表示したりするためにUpdateData( )関数の実行が必要。
UpdateData(TRUE); メンバー変数へ取り込み

・・・処理・・・

UpdateData(FALSE); コントロールへ書き込み
処理に必要ないからといって、UpdateData(TRUE) を省略してはいけない。 UpdateData(FALSE) を実行した途端にコントロールの操作が以前の状態に戻ってしまう。

□ コントロールクラスインスタンスの使い方

CButton ボタン
CEtit エディトボックス
CStatic スタティックテキスト
CRadio ラジオボタン
CCombo コンボボックス

□ コントロール配列の作り方

  1. クラスメンバーにコントロールクラスインスタンスの配列を作る。
    CButton b[10];
  2. コントロールを配置する。このとき、IDCが連続していることを確認する。確認はResource.hにて確認する。
  3. CTestDlg::DoDataExchange( )にて以下のような処理を行う。
    INT nIDB = IDC_EX0;
    for(int i = 0; i < 10; i++){
     DDX_Control(pDX, nIDB + i, b[i]);

    }

□ コントロール配列のハンドラー関数の作り方

ハンドラー関数を宣言しておく。通常通りにダイアログエディタからボタンなどをクリックして作成してもよい。
ヘッダーファイル上で、宣言した関数に引数として(UINT nID)を持つようにする。もちろん関数の定義も同様に。
ダイアログエディタから関数を作った場合はメッセージマップ設定にてON_BN_CLICKED( )などで宣言した関数のマッピングが行われているが、これは削除しておく。
afx_msg void OnBnClickedCdt0(UINT nID); //関数宣言に引数を追加
ON_BN_CLICKED(IDC_MONITOR, OnBnClickedCdt0) //この行は削除
代わりにメッセージマップ設定部分で、ON_CONTROL_RANGE を使って定義を行う。。
ON_CONTROL_RANGE(BN_CLICKED, IDC_CDT0, IDC_CDT7, OnBnClickedCdt0)

□ ラジオボタンの使い方 (2007.1.14記)

  1. グループにしたいラジオボタンのタブオーダーを連続させる。
  2. 先頭となるラジオボタンのプロパティーにてグループをチェック
  3. 変数が作れるようになる。
  4. 変数の初期設定が「-1」で無選択。「0」で、先頭のボタンが選択となる。
  5. ラジオボタングループの次のタブオーダーを持つコントロールのグループプロパティーを「true」にしておく。
    ※「Warning: skipping non-radio button in group.」というワーニングはこの設定ができていないときに出る。(2007.5.20追記)

□ コンボボックス使用上の注意 (2007.2.4記)

標準・ドロップダウン・ドロップダウンリスト とあるが、あらかじめ入力してあるリストをドロップダウンできるようするには以下の手順が必要。

  1. 「標準」にして、リストが表示できるエリアを作る。(コントロールのサイズを調整できるようになる)
  2. 「ドロップダウン」に変更する。

メンバー変数に「int」型を選ぶには「ドロップダウンリスト」にしなければならない。

.Netの場合(もしかしたら6.0もこっちが正しい操作かな)(2007.8.10記)

  1. フォームにドロップするときの範囲がドロップダウンエリアになる。(画像@)
  2. ドロップダウンエリアを編集するには▼マークをクリックすると(画像A)
  3. ドロップダウンエリアが表示されるので下の■をドラッグして編集する。(画像B)
@  A  B

□ Winsockアプリを作る □

□ Winsock (2007.1.8記)

CAsyncSocketクラスを使う。
詳細は書籍「スタンダードVisual C++」による。

□ サーバー □

CAsyncSocketの派生クラスとして、接続用クラス(例:CListenSocket)と通信用クラス(例:CReceiveSocket)を作成する。
ソケット作成:CAsyncSocket::Create(接続用クラス)
接続待ち:CAsyncSocket::Listen(接続用クラス)
接続要求あり:CAsyncSocket::OnAccept (オーバーライドして処理を記述する)(接続用クラス)
接続:CAsyncSocket::Accept(接続用クラス)
受信データあり:CAsyncSocket::OnReceive (オーバーライドして処理を記述する)(通信用クラス)
接続:CAsyncSocket::AsyncSelect(通信用クラス)
送信:CAsyncSocket::Send(通信用クラス)
受信:CAsyncSocket::Receive(通信用クラス)
切断:CAsyncSocket::ShutDown(通信用クラス)

<処理の流れ>

1.接続待ち状態の作成
アプリケーションクラスにて、接続用クラスのインスタンスを作成。ソケットを作り(Create)、接続待ちとする(Listen)。
2.通信の確立
クライアントから接続要求があると、先ほど作った接続用クラスのインスタンスのOnAcceptが呼び出される。
接続し(Accept)、通信用クラスのインスタンスを作成して、通信処理を開始(AsyncSelect)する。
3.受信データ到来
受信できるデータが存在すると、通信用クラスのインスタンスのOnReceiveが呼び出される。
データを受信(Receive)する。
OnReceiveの最後に「delete this」で、自分を消す必要がある。
4.データの送信
通信用クラスインスタンスのSendで送信する。
5.切断
通信用クラスインスタンスのShutDownで切断する。

□ クライアント □

CAsyncSocketの派生クラスとして、通信用クラス(例:CReceiveSocket)を作成する。
ソケット作成:CAsyncSocket::Create(通信用クラス)
接続要求:CAsyncSocket::Connect(通信用クラス)
接続通知:CAsyncSocket::OnConnect (通知を表示する場合などはオーバーライドして処理を記述する)(通信用クラス)
受信データあり:CAsyncSocket::OnReceive (オーバーライドして処理を記述する)(通信用クラス)
送信:CAsyncSocket::Send(通信用クラス)
受信:CAsyncSocket::Receive(通信用クラス)
切断:CAsyncSocket::ShutDown(通信用クラス)

<処理の流れ>

1.通信用ソケットの作成
アプリケーションクラスにて、通信用クラスのインスタンスを作成。ソケットを作り(Create)、接続要求をする(Connect)。
2.通信の確立
サーバーから接続許可が返ると、先ほど作った通信用クラスのインスタンスのOnConnectが呼び出される。
表示などを行う場合はオーバーライドして処理を記述する。
3.受信データ到来
受信できるデータが存在すると、通信用クラスのインスタンスのOnReceiveが呼び出される。
データを受信(Receive)する。
4.データの送信
通信用クラスインスタンスのSendで送信する。
5.切断
通信用クラスインスタンスのShutDownで切断する。

1.プロジェクトの作成でWindowsソケットにチェックを入れる。

※プロジェクトウィザードで「Windowsソケット」にチェックしなかった場合、

@stdafx.hの最終行に

#include <afxsock.h>

を追加する。

AAppClass::InitInstanceに初期化処理を追加する。

if (!AfxSocketInit())
{
AfxMessageBox("ソケットの初期化に失敗しました。");
return FALSE;
}

以上で、MFC拡張機能が使えるようになるらしい。(その他の注意事項があるかもしれない。未確認)

2.ソケット用クラスの追加

クラスウィザードでソケット用クラスを作成する。

□ グラフィクス表示 (2007.3.11記)

SDIアプリやMDIアプリならCViewクラスにてグラフィック表示を行う。(画面中への文字表示も含む。
ダイアログベースのアプリではCViewクラスは使わないでも構わない。(使っても構わない) 詳しくはテクニカルレシピの「VC++の基礎知識」を参照のこと。

・アプリケーションに「ファイルなどのデータを画面に表示する」という考えがあれば、CViewクラス+CDocumentクラスでの処理を考えた方がよい(らしい)。 操作や管理が簡単。
垂れ流しデータの表示や一時的な表示であれば、CViewクラスだけで行うなり、ダイアログに直接表示するなりで構わないだろう。

・グラフィクスの表示、文字の表示、画像の表示などは基本的に同じものである。 デバイスコンテキストを取得して、それに対して操作を行うことで画面への表示を行う。

・デバイスコンテキストは、ダイアログやピクチャーコントロールなどでも取得できる。(CViewクラスがなくても取得できる。)

ピクチャーコントロールを使う時の注意
  1. ピクチャーコントロールをフォームに貼る。
  2. IDを「ID_STATIC」から「ID_PICT」などに変更する。
  3. クラスウィザートにて変数を作成するが、このとき、カテゴリーを「コントロール」にすること。
□ デバイスコンテキストの取得
CViewクラスの場合、OnDraw()関数の引数にデバイスコンテキストがあるのでそれを使う。
ダイアログやピクチャーコントロールの場合は
CDC* pDC = GetDC();
CDC* pDC = m_pict.GetDC();
というふうにGetDC()で取得する。
□ デバイスコンテキストの操作
1.グラフィクス表示
  1. デバイスコンテキスト取得 CDC* pDC = GetDC();
  2. 描画領域を取得 CRect rect; GetClientRect(&rect); (これはなくてもいいかも)
  3. ペンやブラシを作成。
    CPen pen_black(PS_SOLID,1,RGB(0,0,0));
    CBrush br_white(RGB(255,255,255));
  4. 現在のペンやブラシを保持する。
    CBrush* oldbr=pDC->SelectObject(&br_white);
    CPen* oldpen=pDC->SelectObject(&pen_black);
  5. 領域を塗りつぶしたり、 pDC->Rectangle(&rect);
  6. 線を描いたり
    pDC->SelectObject(&pen_gray);
    for(int i=1;i<10;i++){
     pDC->MoveTo(0,i*rect.Height()/10);
     pDC->LineTo(rect.Width(),i*rect.Height()/10);
    }
  7. 最後にペンとブラシを元に戻す
    pDC->SelectObject(oldbr);
    pDC->SelectObject(oldpen);
2.画像表示
□ 再描画
テクニカルレシピのここを参照願います。

□ アプリケーション上からダイアログを開く (2007.2.4記)

  1. リソースビューからダイアログを追加する。
  2. ダイアログを開き、選択し、クラスウィザードを開く。
  3. 新しいクラスを作成する。
  4. メインのダイアログのメンバーに新しく作ったダイアログを加える。
  5. DoModal()にて開く。

こうすれば、ダイアログをいちいち作らないので、メンバー変数の内容が保存される。メインのダイアログからはクラスメンバーとして見えるので dlgGyroSetting.m_xxx というふうにアクセスできる。

□ モードレスダイアログの表示とダイアログ間でのデータ受け渡し (2007.3.12記)

まず、モードレスダイアログの作成

非常にめんどくさい。モードレスダイアログを作成するにはnewを使ってCDialog::Create()で作成、ShowWindow()で表示して最後はdeleteで削除しなければならない。 以下、大まかな流れを記す。

1.モードレスで開くダイアログクラスを作成。もちろんCDialogを継承する。 CTestDlgクラスとする。

ダイアログはリソースビューから「リソースの追加」で行う。リソースエディターにて表示されているダイアログ枠を右クリックし、「クラスの追加」を選択する。 「プロジェクト」⇒「クラスの追加」とした場合も、マニュアルでリソースIDを指定することで作成できるが、結局同じことをせねばならない。

2.呼び出し元のダイアログにメンバーとしてCTestDlgクラスのポインターを持つ。 CTestDlg *m_pTest とする。

3.ポインターの初期化は呼び出し元のOnInitDialog()にて記述 (NULLにする)

4.呼び出し元クラスにて、ダイアログを閉じた時の処理関数を作る。 EndTestDlg()とする。(内容はポインターをNULLに戻すだけでよい)

5.モードレスで開くダイアログクラス(CTestDlg)に呼び出し元ウィンドウのポインターをメンバーに追加する。 CWnd *m_pParentとする。

6.モードレスで開くダイアログクラス(CTestDlg)のコンストラクタでm_pParentを初期化。(NULLにする)

7.モードレスで開くダイアログクラス(CTestDlg)にモードレスでダイアログを作成するメソッドを追加。 ModelessCreate()とする。

8.CTestDlg::ModelessCreate()ではCDialog::Create()を使ってダイアログを作成、ShowWindow()で表示する。

9.呼び出し元クラスにてダイアログを開く記述をする。 newでインスタンスを作成し、ModelessCreate(this)を呼び出す。

10.モードレスで開くダイアログクラス(CTestDlg)にてダイアログを閉じる記述を行う。

  1. PostNcDestroy() をオーバーライドする。 m_pParent->EndTestDlg()を呼び出して、delete this。 それからCDialog::PostNcDestroy()を実行。
  2. WM_CLOSEメッセージでの処理。OnClose()をオーバーライドする。 DestroyWindow()を呼び出す。以上。

11.なお、それぞれのヘッダーファイルをインクルードしておく必要がある。

モードレスダイアログの作成についてはここを参考にしました。ちなみに厳密には間違いがあるらしいのでサンプルプログラムは若干異なっています。@Createはboolを返すわけではない。 ACreateはオーバーライドできないので同名関数を作るのはまずい。

次にダイアログ間でのデータ受け渡し

それぞれのクラスメンバーにポインターを保持しているので、必要に応じてデータ受け渡し用の関数を呼び出せば良い。ここではコントロールを使ってコントロールのメンバー変数を作成せずにコントロールに値を表示する手段として記載する。

void CMyDlg::OnChangeEdit1()
{
 CString sText;
 CEdit *pEdit;
 pEdit = (CEdit*)GetDlgItem(IDC_EDIT1);
 pEdit->GetWindowText(sText);

 m_pTest->SendData(sText);
}
IDよりコントロールを取得して(GetDlgItem() )、GetWindowText()にて内容を取得。 SendData()にて送る。
void CTestDlg::SendData(CString sText){
 SetDlgItemText(IDC_STTEXT, sText);
}
受け取った値をSetDlgItemText()で反映させる。

以上のサンプルプログラムはこれ

□ デバッグ文字列の表示 (2007.5.20記)

変数を表示したい個所に「OutputDebugString("文字列");」を挿入する。

その他のデバッグのやり方は以下を参考に。

■C++, STL

□ 書式設定 (2007.1.20記)

stringを使うとき、sprintf( )のような書式設定をしたいときは、sstreamとマニピュレータを使う。

基本的な用法は以下のサンプルを参照のこと。サンプル中では「fixed」をストリーム中で使っているが、うまくいかない。
oss.setf(ios::fixed); としなければならなかった。 原因はいまのところ不明。

※トグルになっているので次の指定があるまで有効となることに注意。

#include <sstream>
#include <iomanip>
setw(4) ←4桁で整形
setfill('0') ←「0」で埋める
setprecision(1) ←小数点第一位まで

ストリングストリームは、他のストリームと違い内容の初期化しないので、明示的に初期化を行う。

ostringstream oss;
 ・
 ・
 ・
oss.str("");

C++ストリーム


monkey 2004/11/06(土) 23:36:58

> 欲をいえば、
> sprintfとかの書式指定(%05dとか%3.2f)が
> 使えればいいのですが...

書式指定にはマニピュレータと言われるものを使います。

// sample
#include <sstream>
#include <iomanip>
#include <string>
#include <iostream>

int main()
{
    using namespace std;

    int    n = 123;
    double r = 1.2;

    ostringstream oss;
    oss << setw( 5 ) << setfill( '0' ) << n << " "
        << setw( 3 ) << fixed << setprecision( 2 ) << r;

    string s = oss.str();

    cout << s << endl;
}