LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   Efficient use of C string libraries with C++ strings? (https://www.linuxquestions.org/questions/programming-9/efficient-use-of-c-string-libraries-with-c-strings-633764/)

R00ts 04-07-2008 05:13 PM

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 :)

dmail 04-07-2008 08:05 PM

If you have more than one new line character then have you thought of using a different container like a vector?

Code:

std::vector<char> v(string_length);

....
 if (vsnprintf(&v[0], string_length, message, arg_list) < 0)
...


R00ts 04-08-2008 10:09 AM

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

dmail 04-08-2008 11:02 AM

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?

R00ts 04-08-2008 11:43 AM

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


Thanks for your help!


All times are GMT -5. The time now is 10:27 AM.