Re: [C++] ostrstream vs. sprintf

From: George Greer (greerga@circlemud.org)
Date: 02/06/02


D.A.:

On Wed, 6 Feb 2002, Henrik Stuart wrote:

>   However, the benefit of cout over the printf-family is... type
>   safety and overloading. :o)
>
>   Consider this:
>
>   short s = 37;
>   int n = 38;
>   fprintf(fp, "%d %d", s, n); // fp being a FILE*
>   fout << s << ' ' << n; // fout being a std::ofstream

Unless you want to be notified that you're no longer printing an integer
but now a float.  Perhaps in 'a == b' cases where comparing a floating
point number is bad?  It's all fuzzy and some cases get handled by the
compiler, such as GCC's -Wfloat-equal.  Nothing like type-safety in your
*printf functions griping at you (via compiler) for putting the wrong types
in there.

>   However, the iostream capabilities do not come to their full right
>   until we look at an example where we want the representation of
>   something when written to always be the same - an obvious choice to
>   look at as an example would be outputting a matrix:
>
>   std::ostream& operator<<(std::ostream& os, const Matrix& m) {
>     for (int i = 0; i < m.rows(); ++i) {
>       for (int j = 0; j < m.cols(); ++j) {
>         os << m.at(i,j) << ", ";
>       }
>       os << std::endl;
>     }
>     os << std::endl;
>     return os;
>   }

Cheesy example follows:

char *sprintmatrix(const matrix *m, const char *format)
{
  char *ret;
  int total = 0, pos = 0;
  int i, j;

  for (i = 0; i < m->rows; i++) {
    for (j = 0; j < m->cols; j++) {
      total += snprintf(NULL, 0, format, m->a[i][j]);
      total += 2; /* ", " */
    }
    total++;    /* \n */
  }
  total++;      /* \n */

  ret = malloc(total + 1);
  for (i = 0; i < m->rows; i++) {
    for (j = 0; j < m->cols; j++) {
      pos += snprintf(ret + pos, total - pos, format, m->a[i][j]);
      pos += strlcpy(ret + pos, ", ", 2);
    }
    ret[pos++] = '\n';
  }
  ret[pos++] = '\n';

  return ret;
}

Caveats:

  1) I'm not sure "snprintf(NULL, 0, ..." would work.  I'd then use a
     dummy buffer (as small as 1 byte should work) to get the size.

  2) Two passes isn't exactly wonderful.  I'd prefer to pass <string,size>
     to the function like snprintf does and just punt when running out of
     room.  Alternatively, you could just worst-case the size and use that.

  3) This isn't an example of what is good, just that it can be done.

  4) It's not checking for errors from snprintf() due to MailerCode(tm).

  5) You could just use something like GLIB's:

        /* calculate a string size, guarranteed to fit format + args.
         */
        guint   g_printf_string_upper_bound (const gchar* format,
                                             va_list      args);

     or its:

        gchar*   g_strdup_printf        (const gchar *format,
                                         ...) G_GNUC_PRINTF (1, 2);

  6) You'd probably make 'format' part of 'matrix *' if you did it a lot.

>   Normally, with a few changes, we would reproduce the double
>   for-loop every time we want to print a matrix (the apt pupil would
>   most likely wrap this in a few functions) - however, the force in
>   the iostream library lies in the inheritance, so with just the
>   above code we can do all of this:
>
>   Matrix m<4,4>; // creates a 4x4 matrix
>   std::cout << m;
>   std::ofstream fout("outfile.txt");
>   fout << m;
>   fout.close();

And in my code you just change the "format" parameter to pass "%g" instead
of "%d". As an added bonus, say you wanted brackets around all the
values..."[%d]" is your friend.  Or perhaps you want them all 5 digits
wide..."[%5d]", done.

NOTE: This doesn't mean I don't want a string library.  In fact, I'd really
like to have one.  Doesn't the '\0' terminator seem like a crass violation
of data:metadata to you?  The string's end is encoded within the string
itself due to the lack of a meta-length field.  I just don't see such
things as the silver bullet to bad code.

--
George Greer
greerga@circlemud.org

--
   +---------------------------------------------------------------+
   | FAQ: http://qsilver.queensu.ca/~fletchra/Circle/list-faq.html |
   | Archives: http://post.queensu.ca/listserv/wwwarch/circle.html |
   | Newbie List:  http://groups.yahoo.com/group/circle-newbies/   |
   +---------------------------------------------------------------+



This archive was generated by hypermail 2b30 : 06/25/03 PDT