質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.09%

MFCでEditにログを吐き出す処理でどちらが良いと思いますか?

解決済

回答 2

投稿

  • 評価
  • クリップ 1
  • VIEW 4,928

score 18

よろしくおねがいします。
MFCのエディットボックスにprintf(...)みたいな感じでログ表示をして欲しいと言われたのでこんな感じで作りました。
どっちが良いか(あるいはどっちも悪いか)を判定してください。

 設定

OS:Windows7 pro 64bit
開発環境:VisualStudio 2005

プロジェクト環境: MFC > ダイアログベース
構成プロパティ: 文字セット > マルチバイト文字セットを使用する
リソースプロパティ: AutoVScroll > True

 提案1

冗長な箇所は省略しています。

#define _LOG(a) {                                               \
                    printf("[%s:%d]", __FUNCTION__, __LINE__);  \
                    _thisPtr->__printedit a;                    \
                    printf("\n");                               \
                }
#define CRLF                "\r\n"

static CsampleDlg *_thisPtr;                  // Dialog Class Pointer

// 標準コンストラクタ
CsampleDlg::CsampleDlg(CWnd* pParent /*=NULL*/)
    : CDialog(CsampleDlg::IDD, pParent)
{
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
    _thisPtr = this;
}

void CsampleDlg::OnBnClickedClog()
{
    for(int i=0; i < 60 ; i++){
        _LOG(("test message output for LogMsg Editbox."));

    }

}

void CsampleDlg::__printedit(CHAR* format, ...)
{
    /* 2バイト文字を使った場合の動作については保証しない */
    static unsigned int _LineCnt = 0;

    unsigned int limit;   // テキストの最大値
    char szTextBuff[250] = {0};
    char tmpBuff[250]    = {0};

    va_list list;
    char *p;
    int  len;

    // エディットボックスに表示できる最大バイト数を取得する
    limit = m_EditLogMsg.GetLimitText();

    // 古いログを削除
    if( _LineCnt > 500 ){
        // 一番上の行を""に置換
        len = m_EditLogMsg.GetLine(0, szTextBuff, 250-1) + sizeof(CRLF);
        m_EditLogMsg.SetSel( 0, len, FALSE );
        m_EditLogMsg.ReplaceSel("",0);  /* 置換 */
    }
    sprintf(szTextBuff, "%05d  ", _LineCnt++);

    va_start( list, format );

    for(len = (int)strlen(szTextBuff), p=format;*p; ++p){  /* formatの先頭から末尾まで */
        if(*p != '%' ){  /* フォーマット指定子に必ず付く % 以外なら、そのまま出力 */
            szTextBuff[len++] = (char)*p;
            continue;
        }
        /* % だったら、その直後の文字に応じて出力 */
        switch( *(++p) ){
            case 'x':   sprintf(tmpBuff,  "%x", va_arg(list, int) );    break;
            case 'd':   sprintf(tmpBuff,  "%d", va_arg(list, int) );    break;
            case 'c':   sprintf(tmpBuff,  "%c", va_arg(list, char) );   break;
            case 's':   sprintf(tmpBuff,  "%s", va_arg(list, char*) );  break;
            default:    sprintf(tmpBuff,  "%c", *p );                   break;
        }
        len += (int)strlen(tmpBuff);
        strcat(szTextBuff, tmpBuff);
    }
    va_end( list );
    strcat(szTextBuff, CRLF);

    // カーソルを最後に移動させる
    m_EditLogMsg.SetSel( limit, limit, FALSE );
    m_EditLogMsg.ReplaceSel(szTextBuff, 0);
}

 提案2

#define _LOG(a) {                                               \
                    printf("[%s:%d]", __FUNCTION__, __LINE__);  \
                    _thisPtr->__printedit a;                    \
                    printf("\n");                               \
                }
#define CRLF                "\r\n"

static CsampleDlg *_thisPtr;                  // Dialog Class Pointer

// 標準コンストラクタ
CsampleDlg::CsampleDlg(CWnd* pParent /*=NULL*/)
    : CDialog(CsampleDlg::IDD, pParent)
{
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
    _thisPtr = this;
}

void CsampleDlg::OnBnClickedClog()
{
    for(int i=0; i < 60 ; i++){
        _LOG(("test message output for LogMsg Editbox."));

    }

}

void CsampleDlg::__printedit(CHAR* format, ...)
{
    static unsigned int lcnt;
    va_list list;
    char szTextBuff[250];
    int limit,k,l,m;

    // 行番号
    sprintf(szTextBuff, "%05d  ", lcnt++);
    int len = (int)strlen(szTextBuff);

    // 文字列のフォーマット
    va_start(list, format);
    vsprintf(szTextBuff+len, format, list);
    strcat(szTextBuff, CRLF);

    limit=m_EditLogMsg.GetLimitText();            // Editに表示できる最大バイト数
    len = m_EditLogMsg.GetWindowTextLength();    // Editに表示されている文字数を取得します

    //新しい文字列がログに追加できない時は
    if(len+(k=strlen(szTextBuff))>limit){
        //古いログを削除します。
        m_EditLogMsg.SetRedraw(FALSE);            //再描画フラグをクリアします。

        if((k=k-(limit-len))<0) k=0;            //削除しなくてはならないバイト数を求めます。
        m=m_EditLogMsg.LineFromChar(k);            //削除しなくてはならないバイト数を含む行数を求めます。
        l=m_EditLogMsg.LineIndex(m);            //その行のインデックスを求めます。
        if(l<k) l=m_EditLogMsg.LineIndex(m+1);    //その行の先頭では削除しなくてはならないバイト数に満たない時、、、
        //次の行のインデックスを求めます。
        m_EditLogMsg.SetSel(0,l);                //削除す範囲を選択します。
        m_EditLogMsg.ReplaceSel("");            //選択した範囲を削除します。

        len=m_EditLogMsg.GetWindowTextLength();    //現在のログの長さを取得します。

        m_EditLogMsg.SetSel(len,len);            //ログの最後を選択します。
        m_EditLogMsg.ReplaceSel(szTextBuff);    //文字列を追加します。

        m_EditLogMsg.SetRedraw();                //再描画フラグをセットします。
    }else{
        m_EditLogMsg.SetSel(len,len);            //ログの最後を選択します。
        m_EditLogMsg.ReplaceSel(szTextBuff);    //文字列を追加します。
    }
}

あとエディットボックスに表示できる限界があるので古い行は削除していますが、表示しっぱなしに出来る方法はないでしょうか?
ご教授お願いいたします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • ttyp03

    2017/02/17 13:16

    要点はどこですか?可変引数の展開方法ですか?ただソース見せられても…。

    キャンセル

  • kuma_dansyaku

    2017/02/20 16:24 編集

    > 要点はどこですか?可変引数の展開方法ですか?ただソース見せられても…。

    要点は__printedit()のリファクタです。 この方法で動作する事は確認しましたが、__printedit()の中身をもう少し綺麗 & 簡単に出来ないかな?と思い質問を投げてみました。

    キャンセル

回答 2

checkベストアンサー

+1

案1と案2で中身が違い過ぎてるので何とも言えないのですが、現状どちらも同じ結果が得られているという前提且つ、「__printedit()の中身をもう少し綺麗 & 簡単に」ということなので、次のようにしてはいかがでしょうか。

案1をベースに可変引数の展開部分を案2のようにvsprintfを使うようにする。
これだけでスッキリするのではと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/21 14:20


    回答ありがとうございます。

    確かにStringクラスが使えるのに何故char*を使ってたのかなぁ?と思って調べていたら
    そもそもCソースから呼び出す事を前提としていたのでこの形となっていました。
    UI部分をMFC(C++)にして、実際に処理を行うAPIをCソースにしていたのでこの形となっています。
    結局、CソースからMFC側の関数(__printedit)が呼び出せず、最終的にはMFCでコマンドプロンプトを別窓で開く方法を使ってprintfで出力していたのを思い出しました。
    既存のログを変更すると面倒なのでこのままで行きます。


    あとCListBox使った版も作ってみました。
    こっちの方が楽ですね。
    クリップボードにコピーとか考えなければ、メモリがあるだけ追加し続けられるこっちの方が良いかも。

    ```C
    void CsampleDlg::__printedit(CHAR* format, ...)
    {
    va_list list;
    char szTextBuff[250];

    // 行番号
    sprintf(szTextBuff, "%05d ", m_list.GetCount()+1);
    // 文字列のフォーマット
    va_start(list, format);
    vsprintf(&szTextBuff[7], format, list);
    va_end (list);
    strcat(szTextBuff, CRLF);

    m_list.AddString(szTextBuff); // ログ表示
    m_list.SetTopIndex(m_list.GetCount()-1); // 末尾移動
    }
    ```

    キャンセル

+1

MFCを使ってるのだからCStringのFormat関数を使えばいいのにと思うのですが。つまり、CHAR*で受けるのではなくconst CString&でフォーマット文を受け取ったらどうでしょうかということです。(CStringのFormat関数はprintfと構文がほとんど同じなのでそのまま使えると思います)

それと、これは前提を壊すような提案ですが、私がログデータを画面に表示する場合は、ListBoxをよく使います。ListBoxだと行単位なので、追加と削除が行単位になって簡単になります。その代わり、1ログ1行にしておかないと削除が難しくはなります。通常、ログ表示はその場で修正することはないのでListBoxで十分機能は果たせます。
コピーや部分的な切り取りがListBoxだと出来ない、ということは言われるかもしれませんが、ログ表示は最新の表示に更新されることが多いので、コピー選択がし難いことが多いですので(更新頻度にもよりますが)、それほど問題になることはないかと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/21 14:20

    回答ありがとうございます。

    代案2をリファクタし直しました。
    これ以上は簡略化出来そうもないです。

    ```C
    void CsampleDlg::__printedit(CHAR* format, ...)
    {
    static unsigned int lcnt;
    va_list list;
    char szTextBuff[250];

    // 行番号
    sprintf(szTextBuff, "%05d ", lcnt++);
    // 文字列のフォーマット
    va_start(list, format);
    vsprintf(&szTextBuff[7], format, list);
    va_end (list);
    strcat(szTextBuff, CRLF);

    int limit = m_EditLogMsg.GetLimitText(); // Editに表示できる最大文字数
    int len = m_EditLogMsg.GetWindowTextLength(); // Editに表示されている文字数を取得します
    int k = strlen(szTextBuff);
    //新しい文字列がログに追加できない時は
    if((len+k)>limit){
    int d, r, l;

    //古いログを削除します。
    m_EditLogMsg.SetRedraw(FALSE); //再描画フラグをクリアします。

    d=k-(limit-len); //削除しなくてはならないバイト数を求めます。
    r=m_EditLogMsg.LineFromChar(k); //削除しなくてはならないバイト数を含む行数を求めます。
    l=m_EditLogMsg.LineIndex(r); //その行のインデックスを求めます。
    if(l<d){
    l=m_EditLogMsg.LineIndex(r+1); //その行の先頭では削除しなくてはならないバイト数に満たない時、、次の行のインデックスを求めます。
    }
    m_EditLogMsg.SetSel(0,l); //削除する範囲を選択します。
    m_EditLogMsg.ReplaceSel(""); //選択した範囲を削除します。

    len=m_EditLogMsg.GetWindowTextLength(); // Editに表示されている文字数を取得します。

    m_EditLogMsg.SetRedraw(); //再描画フラグをセットします。
    }
    m_EditLogMsg.SetSel(len,len); //ログの最後を選択します。
    m_EditLogMsg.ReplaceSel(szTextBuff); //文字列を追加します。
    }
    ```


    あと代案1は可変長のログの場合だと止まる事があって使い物になりませんでした。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.09%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る