Efficient use of C string libraries with C++ strings?
ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
Efficient use of C string libraries with C++ strings?
I have some...odd constraints on a project that I am working on. I'm not allowed to use C++ I/O (ie streams with the << and >> operator), but I am allowed to use C++ strings. I have a moderately complex logging manager class that accepts a printf-style formatted string and argument list, prints the string to a char buffer, then uses that buffer to construct a C++ string (which is later used to do some more complex string parsing operations that I didn't want to write using C strings). Here's the function in question:
Code:
void tLogManager::Message(eLogVerbosity level, const char* message, ...)
{
if (level > _verbose_threshold || message == NULL)
{
return;
}
va_list arg_list;
va_start(arg_list, message);
// Use vsnprintf to get the size of the string as it would be rendered
int string_length = vsnprintf(NULL, 0, message, arg_list);
if (string_length <= 0)
{
Warning(__FILE__, __LINE__, "invalid formatted string: %s\n", message);
}
string_length++;
// Now use vsnprintf to construct the string
char* buffer = reinterpret_cast<char*>(malloc(sizeof(char) * string_length));
if (buffer == NULL)
{
Warning(__FILE__, __LINE__, "could not create temporary buffer for string\n");
}
if (vsnprintf(buffer, string_length, message, arg_list) < 0)
{
va_end(arg_list);
Warning(__FILE__, __LINE__, "invalid formatted string: %s\n", message);
return;
}
va_end(arg_list);
string msg_string(buffer);
// This loop examines the entire string to make sure that all non-empty lines of text are
// preceeded with "# ". The message string may contain multiple lines of text that are
// separated by newline characters in the string
_ParseMessageString(msg_string);
printf("%s", msg_string.c_str());
if (_IsLogFileOpen())
{
fprintf(_log_file, "%s", msg_string.c_str());
}
free(buffer);
}
This code works. What I really don't like about it though is that I allocate double the amount of memory for the string than I need (once for the char buffer, a second time when constructing the C++ string). I tried to circumvent this by removing the char buffer and instead trying to use the C++ string exclusively, but it didn't work correctly (my parsing code detected two newline characters for a string instead of one). Plus it just didn't seem very safe to me, since I had to const_cast the return value of msg_string.c_str() so that the second call to vsnprintf could accept the char pointer.
Anyway I'm not sure if I'll find any answers here (or if there even is an answer) to how to do this in a more optimal way. Just looking to see if anyone had any insights on how this could be done better. Thanks
Yeah, I guess using a vector would be a bit cleaner since I don't have to do a malloc/free on a char pointer, but it still doesn't solve my larger problem of allocating double the amount of memory for the string that is needed. (I have to use C++ strings in this function no matter what, because the parsing code uses C++ string methods to manipulate the characters).
Maybe I am missing something but can you explain why
Quote:
"I tried to circumvent this by removing the char buffer and instead trying to use the C++ string exclusively, but it didn't work correctly (my parsing code detected two newline characters for a string instead of one)...
Quote:
"... Plus it just didn't seem very safe to me, since I had to const_cast the return value of msg_string.c_str() so that the second call to vsnprintf could accept the char pointer."
This is not correct you can use the index operator with returns a reference and take the address which is the address of the original char.
Code:
void tLogManager::Message(eLogVerbosity level, const char* message, ...)
{
if (level > _verbose_threshold || message == NULL)
{
return;
}
va_list arg_list;
va_start(arg_list, message);
// Use vsnprintf to get the size of the string as it would be rendered
int string_length = vsnprintf(NULL, 0, message, arg_list);
if (string_length <= 0)
{
Warning(__FILE__, __LINE__, "invalid formatted string: %s\n", message);
}
string_length++;
// Now use vsnprintf to construct the string
std::string buffer;
buffer.resize(string_length);
if (vsnprintf(&buffer[0], string_length, message, arg_list) < 0)
{
va_end(arg_list);
Warning(__FILE__, __LINE__, "invalid formatted string: %s\n", message);
return;
}
va_end(arg_list);
// This loop examines the entire string to make sure that all non-empty lines of text are
// preceeded with "# ". The message string may contain multiple lines of text that are
// separated by newline characters in the string
_ParseMessageString(buffer);
printf("%s", buffer.c_str());
if (_IsLogFileOpen())
{
fprintf(_log_file, "%s", buffer.c_str());
}
}
[edit]
And out of curiosity why can you not use streams?
Ohhhh, now I see where you were going with this. I forgot that you can use indexing on strings to gain access to the characters. Alright, cool I think that solves my problems.
As for why I can't use streams: I think they are just being dumb. My group does a lot of driver development and I think they said something about streams not working correctly or something, but even then I'm developing a user application, and none of this code will be run in kernel space. I don't think they have very good reasons honestly other than "that's the way we've always done it", but I'm still relatively new here and I have to pass code reviews, so I must submit.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.