views:

145

answers:

9

I've created a function that will convert all the event notification codes to strings. Pretty simple stuff really.

I've got a bunch of consts like

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....

and my function

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 ...

No big deal. Took me 5 minutes to throw together.

... but I simply don't trust that I've got all the possible values, so I want to have a default to return something like "Unexpected notification code (7410)" if no matches are found.

Unfortunately, I can't think of anyway to return a valid pointer, without forcing the caller to delete the string's memory ... which is not only nasty, but also conflicts with the simplicity of the other return values.

So I can't think of any way to do this without changing the return value to a parameter where the user passes in a buffer and a string length. Which would make my function look like

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

I really don't want to do that. There must be a better way.

Is there?

I'm coming back to C++ after a 10 year hiatus, so if it's something obvious, don't discount that I've overlooked it for a reason.

+2  A: 

C++? std::string. It's not going to destroy the performance on any modern computer.

However if you have some need to over-optimize this, you have three options:

  1. Go with the buffer your example has.
  2. Have the users delete the string afterwards. Many APIs like this provide their own delete function for deleting each kind of dynamically allocated return data.
  3. Return a pointer to a static buffer which you fill in with the return string on each call. This does have some drawbacks, though, in that it's not thread safe, and it can be confusing because the returned pointer's value will change the next time someone calls the function. If non-thread-safety is acceptable and you document the limitations, it should be all right though.
Matti Virkkunen
That's overkill if all he wants to do is return a pointer to a string constant.
Timo Geusch
std::string here? That doesn't sound good even if this is C++. There is no point in returning a dynamically allocated string here.
Vlad Lazarenko
I'd favour a std::string over unsafe use of pointers - better slow and correct than fast and wrong. And it's probably *fast enough* either way.
AshleysBrain
@Ashley, but you can use pointers safely. Even if std::string is not going to hurt overall performance, it might add a latency on non-real-time systems because of a memory allocation. This might be a big deal, for example, for embedded or high-frequency trading systems.
Vlad Lazarenko
@Vlad: but is this question about HFT systems?
jalf
@jalf: There is no clarity as for where this function will be used which IMO assumes the generic and easy solution that is applicable in most cases and not the easiest and simplest solution with a huge limitation of single-threading. Especially taking into account that these days almost every application/library must be thread-safe.
Vlad Lazarenko
@Vlad: how is `std::string` less thread-safe than the other options?
jalf
@jalf: I did not say std::string is not thread-safe. It is an overkill. As for latency, memory allocation could be a problem.
Vlad Lazarenko
A: 

Just declare use a static string as a default result:

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

You may also declare "default_value" outside of the function.

UPDATE:

If you want to insert a message number in that string then it won't be thread-safe (if you are using multiple threads). However, the solution for that problem is to use thread-specific string. Here is an example using 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));
}

The only limitation of this solution is that returned string cannot be passed by the client to the other thread.

Vlad Lazarenko
Thanks, I considered that, but I can't insert the messageNumber into the string.
John MacIntyre
@John: Sorry I didn't not notice that you want to put a number in that string. I've updated my answer accordingly.
Vlad Lazarenko
A: 

If you are returning a point to a string constant, the caller will not have to delete the string - they'll only have to if you are new-ing the memory used by the string every time. If you're just returning a pointer to a string entry in a table of error messages, I would change the return type to TCHAR const * const and you should be OK.

Of course this will not prevent users of your code to attempt to delete the memory referenced by the pointer but there is only so much you can do to prevent abuse.

Timo Geusch
Thanks. The problem is that while I can return const TCHAR* for all my known messageNumber's, the default, which I would need to generate, would need to be allocated somehow. That's why I've got the problem.
John MacIntyre
Is the default a fixed string? If that's the case then the simplest way is probably Zack's suggestion.
Timo Geusch
A: 

Perhaps have a static string buffer you return a pointer to:

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

This is not thread safe, and if you persistently hold the returned pointer and call it twice with different messageNumbers, they all point to the same buffer in temp - so both pointers now point to the same message. The solution? Return a std::string from the function - that's modern C++ style, try to avoid C style pointers and buffers. (It looks like you might want to invent a tstring which would be std::string in ANSI and std::wstring in unicode, although I'd recommend just going unicode-only... do you really have any reason to support non-unicode builds?)

AshleysBrain
Not thread safe.
Vlad Lazarenko
That might be an acceptable limitation, depending on what the larger code is trying to do.
Zack
@Vlad I was alluding to that with my mention of "If you call from multiple threads" - edited to clarify.
AshleysBrain
A: 

There's no good answer here, but this kludge might suffice.

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;
     }
  }
}

If you do this, callers must be aware that the string they get back from GetDirectShowMessageText might be clobbered by a subsequent call to the function. And it's not thread safe, obviously. But those might be acceptable limitations for your application.

Zack
I'd consider this a perfectly valid solution and indeed use it myself all the time. (Though, seriously, just make the buffer 100 chars and snprintf the whole thing each time -- way easier!) You can use TLS to get some thread safety, and/or a set of buffers (used round-robin) if you want to do something like call the function multiple times in another printf and so on.
brone
@Zack: This code is absolutely not thread safe.
Vlad Lazarenko
Yeah, it's that clobbering that I've been trying to avoid.
John MacIntyre
@Vlad: Did you miss the part where I said "this is not thread safe"? Thread safety is *not* always a requirement.
Zack
A: 

You already use _bstr_t, so if you can just return those directly:

_bstr_t GetDirectShowMessageDisplayText(int messageNumber);

If you need to build a different message at runtime you can pack it into a _bstr_t too. Now the ownership is clear and the use is still simple thanks to RAII.
The overhead is negligible (_bstr_t uses ref-counting) and the calling code can still use _bstr_ts conversion to wchar_t* and char* if needed.

Georg Fritzsche
A: 

You return some sort of self-releasing smart pointer or your own custom string class. You should follow the interface as it's defined in std::string for easiest use.

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

In C++, you never deal with raw pointers unless you have an important reason, you always use self-managing classes. Usually, Microsoft use raw pointers because they want their interfaces to be C-compatible, but if you don't care, then don't use raw pointers.

DeadMG
As i pointed out in my answer, `_bstr_t` already does the job - see [MSDN](http://msdn.microsoft.com/en-us/library/zthfhkd6%28VS.80%29.aspx).
Georg Fritzsche
A: 

The simple solution does seem to be to just return a std::string. It does imply one dynamic memory allocation, but you'd probably get that in any case (as either the user or your function would have to make the allocation explicitly)

An alternative might be to allow the user to pass in an output iterator which you write the string into. Then the user is given complete control over how and when to allocate and store the string.

jalf
A: 

On the first go-round I missed that this was a C++ question rather than a plain C question. Having C++ to hand opens up another possibility: a self-managing pointer class that can be told whether or not to delete.

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);
    }
  }
}

(I don't remember if Windows CRT has asprintf, but it's easy enough to rewrite the above on top of std::string if it doesn't.)

Note the use of boost::noncopyable, though - if you copy this kind of object you risk double frees. Unfortunately, that may cause problems with returning it from your message-pretty-printer function. I'm not sure what the right way to deal with that is, I'm not actually much of a C++ guru.

Zack
Same here: As i pointed out in my answer, `_bstr_t` already does the job - see [MSDN](http://msdn.microsoft.com/en-us/library/zthfhkd6%28VS.80%29.aspx).
Georg Fritzsche