With the new std::async in c++11, I thought I might go about trying to implement an async version of OutputDebugString to free me of some of the performance downs that result from my usual heavy printing of every little detail through the usual OutputDebugString function.
So here is my original sync OutputDebugString implementation(which works):
static void OutputDebugStringN(const char *format, ...)
{
char outstring[256];
memset(outstring, 0, sizeof(outstring));
try
{
va_list args = {0};
va_start(args, format); //args = (va_list) (&format+1);
vsprintf_s(outstring, format, args);
va_end(args);
OutputDebugString(outstring);
}
catch (...) //most likely reference val arg error (va_list doesn't support ref args)
{
OutputDebugString("[OutputDebugStringN] Something went wrong\n");
}
}
and the following my very naiive attempt at an async version(which doesn't work):
static void OutputDebugStringN(const char *format, ...)
{
auto future = std::async([]{
char outstring[256];
memset(outstring, 0, sizeof(outstring));
try
{
va_list args = {0};
va_start(args, format); //args = (va_list) (&format+1);
vsprintf_s(outstring, format, args);
va_end(args);
OutputDebugString(outstring);
}
catch (...) //most likely reference val arg error (va_list doesn't support ref args)
{
OutputDebugString("[OutputDebugStringN] Something went wrong\n");
}
});
}
And since the above doesn't work I'm now at the point where I'm starting to think that async calling OutputDebugStringN might be better than trying to launch an async job inside the function itself as so (which works, but cumbersome):
auto dstring = std::async([]{ OutputDebugStringN("[NovelScript::ParseTokens] searched bookmark: \"%s\" does not exist\n", bookmark.c_str());} );
So here are two questions I'd like to ask:
Criticism on the above code and any other comments are also much welcome.
I think you should have a queue for your messages instead of starting a thread with each call to your function, that way your messages will be output clean and in the right order.
So your function e.g. OutputDebugStringN(const char *format, ... )
would create the message string and then queue the string that a seperate printout thread reads from. That thread would call OutputDebugString
.
Here's an example - not complete though, no error handling and print_from_queue should be modified to run until some termination condition and be a bit more friendly to the CPU.
std::mutex g_m;
std::deque<std::string> que;
std::atomic<bool> endcond = false;
void queue(std::string msg)
{
std::lock_guard<mutex> _(g_m);
que.push_back(msg);
}
void print_from_queue()
{
while ( !endcond )
{
if ( que.size() )
{
std::lock_guard<mutex> _(g_m);
std::string msg = que.front();
que.pop_front();
OutputDebugStringA(msg.c_str());
}
}
}
int debugf( const char *format,... )
{
std::vector<char> line(256);
va_list args;
va_start( args, format );
int len = vsprintf_s( &line[0], line.size(), format, args );
va_end( args );
queue( &line[0] );
return len;
}
int _tmain(int argc, _TCHAR* argv[])
{
auto thr = std::async( std::launch::async, print_from_queue );
debugf("message1");
debugf("message2");
...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With