4

すべてのイベント通知コードを文字列に変換する関数を作成しました。本当にシンプルなもの。

私はのようなconstsの束を持っています

const _bstr_t DIRECTSHOW_MSG_EC_ACTIVATE("A video window is being activated or deactivated.");
const _bstr_t DIRECTSHOW_MSG_EC_BUFFERING_DATA("The graph is buffering data, or has stopped buffering data.");
const _bstr_t DIRECTSHOW_MSG_EC_BUILT("Send by the Video Control when a graph has been built. Not forwarded to applications.");
.... etc....

そして私の機能

TCHAR* GetDirectShowMessageDisplayText( int messageNumber )
{
    switch( messageNumber )
    {
        case EC_ACTIVATE: return DIRECTSHOW_MSG_EC_ACTIVATE;
        case EC_BUFFERING_DATA: return DIRECTSHOW_MSG_EC_BUFFERING_DATA;
        case EC_BUILT: return DIRECTSHOW_MSG_EC_BUILT;
... etc ...

大きな問題ではない。一緒に投げるのに5分かかりました。

...しかし、考えられるすべての値を取得できるとは信じていないので、一致が見つからない場合に「予期しない通知コード (7410)」のようなものを返すデフォルトが必要です。

残念ながら、呼び出し元に文字列のメモリを強制的に削除させずに、有効なポインターを返す方法は考えられません...これは厄介なだけでなく、他の戻り値の単純さと矛盾します。

したがって、ユーザーがバッファと文字列の長さを渡すパラメータに戻り値を変更せずにこれを行う方法は考えられません。私の関数は次のようになります

BOOL GetDirectShowMessageDisplayText( int messageNumber, TCHAR* outBuffer, int bufferLength )
{
    ... etc ...

私は本当にそれをしたくありません。もっと良い方法があるはずです。

ある?

私は 10 年間の休止期間を経て C++ に戻ってきたので、それが明らかな場合でも、何らかの理由で見落としていたことを軽視しないでください。

4

9 に答える 9

2

C++? std::string . 最新のコンピューターのパフォーマンスを損なうことはありません。

ただし、これを過度に最適化する必要がある場合は、次の 3 つのオプションがあります。

  1. 例のバッファを使用してください。
  2. 後でユーザーに文字列を削除してもらいます。このような多くの API は、動的に割り当てられた各種類の戻りデータを削除するための独自の削除機能を提供します。
  3. 各呼び出しで戻り文字列を入力する静的バッファーへのポインターを返します。ただし、これにはいくつかの欠点があります。スレッド セーフではないということです。また、返されたポインタの値が次に誰かが関数を呼び出したときに変更されるため、混乱を招く可能性があります。非スレッドセーフが許容され、制限を文書化する場合は、問題ありません。
于 2010-09-16T21:01:22.810 に答える
0

ある種の自己解放型スマートポインタまたは独自のカスタム文字列クラスを返します。最も使いやすいように、std::stringで定義されているインターフェイスに従う必要があります。

class bstr_string {
    _bstr_t contents;
public:
    bool operator==(const bstr_string& eq);
    ...
    ~bstr_string() {
        // free _bstr_t
    }
};

C ++では、重要な理由がない限り、生のポインターを処理することはありません。常に自己管理クラスを使用します。通常、MicrosoftはインターフェイスをC互換にするために、rawポインターを使用しますが、気にしない場合は、rawポインターを使用しないでください。

于 2010-09-16T21:31:50.960 に答える
0

おそらく、ポインタを返す静的文字列バッファがあります。

std::ostringstream ss;
ss << "Unexpected notification code (" << messageNumber << ")";
static string temp = ss.str(); // static string always has a buffer
return temp.c_str(); // return pointer to buffer

これはスレッド セーフではありません。返されたポインタを永続的に保持し、それを別の で 2 回呼び出すとmessageNumbers、それらはすべて同じバッファ in をtemp指すため、両方のポインタが同じメッセージを指すようになります。ソリューション?関数からa を返しstd::stringます - これは最新の C++ スタイルです。C スタイルのポインターとバッファーを避けるようにしてください。( ANSI とUnicodeの を発明したいようですがtstring、Unicode のみにすることをお勧めします...非 Unicode ビルドをサポートする理由は本当にありますか?)std::stringstd::wstring

于 2010-09-16T21:08:35.163 に答える
0

あなたはすでに を使用_bstr_tしているので、それらを直接返すことができる場合:

_bstr_t GetDirectShowMessageDisplayText(int messageNumber);

実行時に別のメッセージを作成する必要がある場合は、それを にパックすることもできます_bstr_t。RAII のおかげで、所有権が明確になり、使用も簡単になりました。
オーバーヘッドは無視できます (_bstr_t参照カウントを使用します)。呼び出し元のコードは、必要に応じて s への変換を引き続き使用でき_bstr_tます。wchar_t*char*

于 2010-09-16T21:21:54.800 に答える
0

最初のラウンドで、これが単純な C の質問ではなく C++ の質問であることを見逃していました。C++ を手元に用意することで、別の可能性が開かれます。削除するかどうかを通知できる自己管理ポインター クラスです。

class MsgText : public boost::noncopyable
{
   const char* msg;
   bool shouldDelete;

public:
   MsgText(const char *msg, bool shouldDelete = false)
     : msg(msg), shouldDelete(shouldDelete)
   {}
   ~MsgText()
   {
     if (shouldDelete)
       free(msg);
   }
   operator const char*() const
   {
     return msg;
   }
};

const MsgText GetDirectShowMessageDisplayText(int messageNumber)
{
  switch(messageNumber)
  {
    case EC_ACTIVATE:
      return MsgText("A video window is being activated or deactivated.");
    // etc
    default: {
      char *msg = asprintf("Undocumented message (%u)", messageNumber);
      return MsgText(msg, true);
    }
  }
}

(Windows CRT に があるかどうかは覚えていませんが、ない場合asprintfは上記を上に書き直すのは簡単std::stringです。)

ただし、boost::noncopyable の使用に注意してください。この種のオブジェクトをコピーすると、二重解放のリスクがあります。残念ながら、これは message-pretty-printer 関数から返す際に問題を引き起こす可能性があります。それを処理する正しい方法が何であるかはわかりません。実際、私は C++ の第一人者ではありません。

于 2010-09-16T22:17:36.930 に答える
0

簡単な解決策は、単に a を返すことのようですstd::string。これは 1 つの動的メモリ割り当てを意味しますが、いずれにせよ (ユーザーまたは関数のいずれかが明示的に割り当てを行う必要があるため) おそらくそれを得るでしょう。

別の方法として、文字列を書き込む出力イテレータをユーザーが渡せるようにすることもできます。次に、ユーザーは、文字列をいつどのように割り当てて格納するかを完全に制御できます。

于 2010-09-16T21:57:45.343 に答える
0

デフォルトの結果として静的文字列を使用することを宣言するだけです。

TCHAR* GetDirectShowMessageDisplayText( int messageNumber )
{
  switch( messageNumber )
  {
     // ...
     default:
       static TCHAR[] default_value = "This is a default result...";
       return default_value;
  }
}

関数の外で「default_value」を宣言することもできます。

アップデート:

その文字列にメッセージ番号を挿入したい場合は、スレッドセーフではありません (複数のスレッドを使用している場合)。ただし、その問題の解決策は、スレッド固有の文字列を使用することです。Boost.Threadを使用した例を次に示します。

#include <cstdio>
#include <boost/thread/tss.hpp>

#define TCHAR char // This is just because I don't have TCHAR...

static void errorMessageCleanup (TCHAR *msg)
{
    delete []msg;
}

static boost::thread_specific_ptr<TCHAR> errorMsg (errorMessageCleanup);

static TCHAR *
formatErrorMessage (int number)
{
    static const size_t MSG_MAX_SIZE = 256;
    if (errorMsg.get () == NULL)
        errorMsg.reset (new TCHAR [MSG_MAX_SIZE]);
    snprintf (errorMsg.get (), MSG_MAX_SIZE, "Unexpected notification code (%d)", number);
    return errorMsg.get ();
}

int
main ()
{
    printf ("Message: %s\n", formatErrorMessage (1));
}

このソリューションの唯一の制限は、返された文字列をクライアントから他のスレッドに渡すことができないことです。

于 2010-09-16T21:03:09.537 に答える
0

文字列定数にポイントを返す場合、呼び出し元は文字列を削除する必要はありませんnew。文字列によって使用されるメモリを毎回削除する場合にのみ、呼び出し元は文字列を削除する必要があります。エラー メッセージの表の文字列エントリへのポインターを返すだけの場合は、戻り値の型を に変更TCHAR const * constすれば問題ありません。

もちろん、これによってコードのユーザーがポインターによって参照されるメモリを削除しようとするのを防ぐことはできませんが、乱用を防ぐためにできることは限られています。

于 2010-09-16T21:03:27.270 に答える
-1

ここには良い答えはありませんが、このクラッジで十分かもしれません.

const char *GetDirectShowMessageDisplayText(int messageNumber)
{
  switch(messageNumber)
  {
     // ...
     default: {
       static char defaultMessage[] = "Unexpected notification code #4294967296";
       char *pos = defaultMessage + sizeof "Unexpected notification code #" - 1;
       snprintf(pos, sizeof "4294967296" - 1, "%u", messageNumber);
       return defaultMessage;
     }
  }
}

これを行う場合、呼び出し元は、GetDirectShowMessageText から返された文字列が関数の後続の呼び出しによって破壊される可能性があることに注意する必要があります。そして、明らかにスレッドセーフではありません。しかし、それらはアプリケーションにとって許容できる制限かもしれません。

于 2010-09-16T21:12:47.747 に答える