4

我創建了一個將所有事件通知代碼轉換爲字符串的函數。非常簡單的東西。是否可以安全地從函數中返回一個TCHAR *

我有一堆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++,所以如果它是明顯的東西,不要打折我忽略它的原因。

+0

如果要返回的消息是靜態常量,或者它們是即時生成的,則我不太清楚。如果前者,似乎沒有問題,並且您可以直接返回指向它們的指針,因爲它們永遠不需要解除分配。 – jalf 2010-09-16 21:52:24

回答

2

C++? std::string。它不會破壞任何現代計算機的性能。

但是如果你有一些必要過分優化這個,你有三種選擇:

  1. 去與你的例子有緩衝。
  2. 之後讓用戶刪除字符串。許多這樣的API提供了自己的刪除功能,用於刪除每種動態分配的返回數據。
  3. 返回一個指向靜態緩衝區的指針,該靜態緩衝區在每次調用時都使用返回字符串填充。但是,這確實有一些缺點,因爲它不是線程安全的,並且可能會引起混淆,因爲在下次調用函數時返回的指針值會發生變化。如果非線程安全性是可以接受的,並且你記錄了這些限制,它應該沒問題。
+0

如果他想要做的只是返回一個指向字符串常量的指針,這是過度的。 – 2010-09-16 21:04:09

+0

std :: string在這裏?這聽起來不太好,即使這是C++。在這裏返回一個動態分配的字符串沒有意義。 – 2010-09-16 21:04:10

+1

我喜歡std :: string不安全的使用指針 - 比fast和wrong更好的緩慢和正確。它可能*速度夠快*無論哪種方式。 – AshleysBrain 2010-09-16 21:20:04

0

只是聲明使用靜態字符串作爲默認結果:

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

您也可以聲明函數「DEFAULT_VALUE」之外。

UPDATE:

如果你想在字符串中插入一個消息編號,然後它不會是線程安全的(如果你使用多線程)。但是,該問題的解決方案是使用thread-specific字符串。下面是使用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)); 
} 

這種解決方案的唯一限制是返回的字符串不能被客戶端傳遞給其他線程。

+0

謝謝,我認爲,但我不能將messageNumber插入到字符串中。 – 2010-09-16 21:03:47

+0

@John:對不起,我沒有注意到你想在該字符串中輸入一個數字。我相應地更新了我的答案。 – 2010-09-16 21:18:30

0

如果要返回一個指向一個字符串常量,調用者不會有要刪除的路線 - 他們將只需要如果你是new -ing通過每一次的字符串使用的內存。如果您只是返回指向錯誤消息表中字符串條目的指針,則會將返回類型更改爲TCHAR const * const,您應該確定。

當然,這不會阻止您的代碼的用戶嘗試刪除指針引用的內存,但只有這麼多,你可以做,以防止濫用。

+0

謝謝。問題是,雖然我可以爲我所有的已知messageNumber返回const TCHAR *,但我需要生成的默認值需要以某種方式分配。這就是爲什麼我有這個問題。 – 2010-09-16 21:06:43

+0

默認是固定字符串嗎?如果是這種情況,那麼最簡單的方法可能就是扎克的建議。 – 2010-09-16 22:19:37

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 

這不是線程安全的,如果你持續按住返回的指針,並用不同的messageNumbers調用它兩次,他們都指向temp中的相同緩衝區 - 因此兩個指針現在指向相同的消息。解決方案?從函數返回std::string - 這是現代C++風格,儘量避免C風格指針和緩衝區。 (看起來你可能想發明一個tstring這將是ANSI的std::string和unicode的std::wstring,雖然我建議僅僅使用unicode ...你真的有理由支持非unicode構建嗎?)

+1

非線程安全。 – 2010-09-16 21:12:16

+0

這可能是一個可以接受的限制,具體取決於較大的代碼嘗試執行的操作。 – zwol 2010-09-16 21:14:59

+0

@弗拉德我提到「如果你從多個線索調用」 - 我編輯澄清。 – AshleysBrain 2010-09-16 21:18:45

-1

這裏沒有好的答案,但是這個kludge可能就足夠了。

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後面的字符串可能通過向函數後續調用重挫。顯然,它不是線程安全的。但這些對你的應用來說可能是可以接受的限制

+0

我認爲這是一個完美有效的解決方案,並且確實一直在使用它。 (但是,嚴格來說,每次只需要緩衝區100字符和snprintf - 方式更簡單!)如果你想要使用TLS來獲得一些線程安全性和/或一組緩衝區(使用循環)在另一個printf中多次調用該函數等等。 – 2010-09-16 21:17:44

+2

此代碼絕對不是線程安全的。 – 2010-09-16 21:19:53

+0

是的,這是我一直試圖避免的破壞。 – 2010-09-16 21:21:31

0

你已經使用_bstr_t,所以如果你可以只返回那些直接:

_bstr_t GetDirectShowMessageDisplayText(int messageNumber); 

如果您需要在運行時建立一個不同的消息,你可以打包成一個_bstr_t了。現在所有權清晰,並且由於RAII,使用仍然很簡單。
開銷可以忽略不計(_bstr_t使用重新計數),如果需要,調用代碼仍然可以使用_bstr_t s轉換爲wchar_t*char*

0

您返回某種自釋放智能指針或您自己的自定義字符串類。您應該按照在std :: string中定義的接口來使用,以便於使用。

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

在C++中,你永遠不與原始指針對付,除非你有一個重要的原因,你總是使用自我管理課程。通常,微軟使用原始指針是因爲他們希望它們的接口是C兼容的,但是如果你不在意,那麼就不要使用原始指針。

+0

正如我在回答中指出的那樣,'_bstr_t'已經完成了這項工作 - 請參閱[MSDN](http://msdn.microsoft.com/zh-cn/library/zthfhkd6%28VS.80%29.aspx)。 – 2010-09-16 22:43:25

0

簡單的解決方案似乎只是返回std::string。它確實意味着一個動態的內存分配,但是無論如何你可能會得到這個結果(因爲用戶或你的函數都必須明確地進行分配)

另一種可能是允許用戶通過輸出迭代器,您將字符串寫入。然後用戶完全控制如何以及何時分配和存儲字符串。

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 ::的不可複製但是,如果您複製這種類型的對象,則會冒雙倍的風險。不幸的是,這可能會導致您的消息 - 漂亮打印機功能返回問題。我不知道處理這個問題的正確方法是,我實際上並不是C++大師。

+0

同樣在這裏:正如我在我的答案中指出的,'_bstr_t'已經完成了這項工作 - 請參閱[MSDN](http://msdn.microsoft.com/zh-cn/library/zthfhkd6%28VS.80%29.aspx )。 – 2010-09-17 10:57:31

相關問題