[Ace-users] [ace-users] Proposal: ACE_OS::asprintf(), etc.

J.T. Conklin jtc at acorntoolworks.com
Fri Oct 26 09:48:40 CDT 2007


Similar to the getprogname() issue I mentioned yesterday, another
thing we ran into while porting our system from NetBSD to Solaris 
was the use of asprintf() and vasprintf().

asprintf() is similar to sprintf(), but instead of passing a char *
you pass the address of a char*, which is set to a malloc() buffer
containing the formatted output.  This is useful in cases where you
don't know the size of the buffer needed. While you can do the same
thing with two calls to snprintf() -- the first to find the buffer
size, the second to format the string into an appropriately sized
buffer -- asprintf() nicely ties this together in a single call.

The asprintf(), etc. functions are fairly widely implemented: *BSD,
Linux, etc.  It appears they may even become part of the C standard
some time in the future. While preparing this message, I googled up a
*.pdf of C Standard Committee Draft N1248 --- See
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1248.pdf for details.

I think that adding ACE_OS::asprintf() wrapper facades would be
generally useful.

While implementing these functions, I noticed some anomalies in some
of the other ACE_OS::*printf() wrapper facades:

 * There was no wide character version of ACE_OS::printf().

 * There were no wide or narrow versions of ACE_OS::vprintf().

 * There were no wide or narrow versions of ACE_OS::vfprintf().

 * In some cases, ACE_OS::*printf() was implemented in terms of the
   corresponding ACE_OS::v*printf() function; in others it was done 
   in terms of the system's ::v*printf() function. 

   Some of this was because of missing ACE_OS::vprintf() and
   ACE_OS::vfprintf() as noted above.  But now that I've added those
   functions, I changed ACE_OS::*printf() to use ACE_OS::v*printf().
   This ensures consistent behavior (ACE_OS::{v,}s{n,}printf() has
   a pretty complicated nest of conditionals, and the code appears
   to have diverged (hopefully only syntactically) over the years).

   In most cases, ACE_OS::v*printf() is an inline that simply invokes
   ::v*printf(), so there should be no time or space overhead.

Here is the patch I'm proposing.

    --jtc

Index: OS_NS_stdio.cpp
===================================================================
--- OS_NS_stdio.cpp	(revision 79869)
+++ OS_NS_stdio.cpp	(working copy)
@@ -257,14 +257,17 @@
 
 #endif /* ACE_WIN32 */
 
+// The following *printf functions aren't inline because
+// they use varargs.
+
 int
 ACE_OS::fprintf (FILE *fp, const char *format, ...)
 {
-  ACE_OS_TRACE ("ACE_OS::fprintf");
+  // ACE_OS_TRACE ("ACE_OS::fprintf");
   int result = 0;
   va_list ap;
   va_start (ap, format);
-  ACE_OSCALL (::vfprintf (fp, format, ap), int, -1, result);
+  result = ACE_OS::vfprintf (fp, format, ap);
   va_end (ap);
   return result;
 }
@@ -273,28 +276,41 @@
 int
 ACE_OS::fprintf (FILE *fp, const wchar_t *format, ...)
 {
-  ACE_OS_TRACE ("ACE_OS::fprintf");
-
-# if !defined (ACE_HAS_VFWPRINTF)
-  ACE_UNUSED_ARG (fp);
-  ACE_UNUSED_ARG (format);
-  ACE_NOTSUP_RETURN (-1);
-
-# else
+  // ACE_OS_TRACE ("ACE_OS::fprintf");
   int result = 0;
   va_list ap;
   va_start (ap, format);
-  ACE_OSCALL (ACE_STD_NAMESPACE::vfwprintf (fp, format, ap), int, -1, result);
+  result = ACE_OS::vfprintf (fp, format, ap);
   va_end (ap);
   return result;
-
-# endif /* ACE_HAS_VFWPRINTF */
 }
 #endif /* ACE_HAS_WCHAR */
 
+int
+ACE_OS::asprintf (char **bufp, const char *format, ...)
+{
+  // ACE_OS_TRACE ("ACE_OS::aprintf");
+  int result;
+  va_list ap;
+  va_start (ap, format);
+  result = ACE_OS::vasprintf (bufp, format, ap);
+  va_end (ap);
+  return result;
+}
 
-// The following *printf functions aren't inline because
-// they use varargs.
+#if defined (ACE_HAS_WCHAR)
+int
+ACE_OS::asprintf (wchar_t **bufp, const wchar_t *format, ...)
+{
+  // ACE_OS_TRACE ("ACE_OS::aprintf");
+  int result;
+  va_list ap;
+  va_start (ap, format);
+  result = ACE_OS::vasprintf (bufp, format, ap);
+  va_end (ap);
+  return result;
+}
+#endif /* ACE_HAS_WCHAR */
 
 int
 ACE_OS::printf (const char *format, ...)
@@ -303,12 +319,26 @@
   int result;
   va_list ap;
   va_start (ap, format);
-  ACE_OSCALL (::vprintf (format, ap), int, -1, result);
+  result = ACE_OS::vprintf (format, ap);
   va_end (ap);
   return result;
 }
 
+#if defined (ACE_HAS_WCHAR)
 int
+ACE_OS::printf (const wchar_t *format, ...)
+{
+  // ACE_OS_TRACE ("ACE_OS::printf");
+  int result;
+  va_list ap;
+  va_start (ap, format);
+  result = ACE_OS::vprintf (format, ap);
+  va_end (ap);
+  return result;
+}
+#endif /* ACE_HAS_WCHAR */
+
+int
 ACE_OS::snprintf (char *buf, size_t maxlen, const char *format, ...)
 {
   // ACE_OS_TRACE ("ACE_OS::snprintf");
@@ -321,53 +351,16 @@
 }
 
 #if defined (ACE_HAS_WCHAR)
-
 int
 ACE_OS::snprintf (wchar_t *buf, size_t maxlen, const wchar_t *format, ...)
 {
   // ACE_OS_TRACE ("ACE_OS::snprintf");
-# if (defined _XOPEN_SOURCE && (_XOPEN_SOURCE - 0) >= 500) || \
-     (defined (sun) && !defined (_XPG4) || defined(_XPG5)) || \
-     defined (ACE_WIN32) || defined (ACE_HAS_VSWPRINTF)
   int result;
   va_list ap;
   va_start (ap, format);
-#  if 0 /* defined (ACE_HAS_TR24731_2005_CRT) */
-  // _vsnwprintf_s() doesn't report the length needed when it truncates. This
-  // info is needed for the API contract return value, so don't use this.
-  // There's adequate protection via the maxlen.
-  result = _vsnwprintf_s (buf, maxlen, _TRUNCATE, format, ap);
-#  elif defined (ACE_WIN32)
-  // Microsoft's vswprintf() doesn't have the maxlen argument that
-  // XPG4/UNIX98 define. They do, however, recommend use of _vsnwprintf()
-  // as a substitute, which does have the same signature as the UNIX98 one.
-  ACE_OSCALL (::_vsnwprintf (buf, maxlen, format, ap), int, -1, result);
-  // Win32 doesn't regard a full buffer with no 0-terminate as an overrun.
-  if (result == static_cast <int> (maxlen))
-    result = -1;
-
-  // Win32 doesn't 0-terminate the string if it overruns maxlen.
-  if (result == -1)
-    buf[maxlen-1] = '\0';
-#  else
-  ACE_OSCALL (::vswprintf (buf, maxlen, format, ap), int, -1, result);
-#  endif /* ACE_WIN32 */
+  result = ACE_OS::vsnprintf (buf, maxlen, format, ap);
   va_end (ap);
-  // In out-of-range conditions, C99 defines vsnprintf to return the number
-  // of characters that would have been written if enough space was available.
-  // Earlier variants of the vsnprintf() (e.g. UNIX98) defined it to return
-  // -1. This method follows the C99 standard, but needs to guess at the
-  // value; uses maxlen + 1.
-  if (result == -1)
-    result = static_cast <int> (maxlen + 1);
   return result;
-
-# else
-  ACE_UNUSED_ARG (buf);
-  ACE_UNUSED_ARG (maxlen);
-  ACE_UNUSED_ARG (format);
-  ACE_NOTSUP_RETURN (-1);
-# endif /* _XOPEN_SOURCE ...  */
 }
 #endif /* ACE_HAS_WCHAR */
 
@@ -375,11 +368,10 @@
 ACE_OS::sprintf (char *buf, const char *format, ...)
 {
   // ACE_OS_TRACE ("ACE_OS::sprintf");
-
   int result;
   va_list ap;
   va_start (ap, format);
-  ACE_OSCALL (::vsprintf (buf, format, ap), int, -1, result);
+  result = ACE_OS::vsprintf (buf, format, ap);
   va_end (ap);
   return result;
 }
@@ -388,46 +380,77 @@
 int
 ACE_OS::sprintf (wchar_t *buf, const wchar_t *format, ...)
 {
-  ACE_OS_TRACE ("ACE_OS::sprintf");
-
-# if (defined _XOPEN_SOURCE && (_XOPEN_SOURCE - 0) >= 500) || \
-     (defined (sun) && !defined (_XPG4) || defined(_XPG5)) || \
-      defined (ACE_HAS_DINKUM_STL) || defined (__DMC__) || \
-      defined (ACE_HAS_VSWPRINTF) || \
-     (defined (ACE_WIN32_VC8) && !defined (ACE_HAS_WINCE) && \
-      _MSC_FULL_VER > 140050000)
-
-  // The XPG4/UNIX98/C99 signature of the wide-char sprintf has a
-  // maxlen argument. Since this method doesn't supply one, pass in
-  // a length that works (ULONG_MAX doesn't on all platform since some check
-  // to see if the operation will remain in bounds). If this isn't ok, use
-  // ACE_OS::snprintf().
+  // ACE_OS_TRACE ("ACE_OS::sprintf");
   int result;
   va_list ap;
   va_start (ap, format);
-  ACE_OSCALL (ACE_STD_NAMESPACE::vswprintf (buf, 4096, format, ap), int, -1, result);
+  result = ACE_OS::vsprintf (buf, format, ap);
   va_end (ap);
   return result;
+}
+#endif /* ACE_HAS_WCHAR */
 
-# elif defined (ACE_WIN32)
-  // Pre-VC8 Windows has vswprintf, but the signature is from the older ISO C
-  // standard. Also see ACE_OS::snprintf() for more info on this.
+#if !defined (ACE_HAS_VASPRINTF)
+int
+ACE_OS::vasprintf_emulation(char **bufp, const char *format, va_list argptr)
+{
+  int size;
 
-  int result;
   va_list ap;
-  va_start (ap, format);
-  ACE_OSCALL (::vswprintf (buf, format, ap), int, -1, result);
+  va_copy (ap, argptr);
+  size = ACE_OS::vsnprintf(NULL, 0, format, ap);
   va_end (ap);
-  return result;
 
-# else
+  if (size != -1) 
+    {
+      char *buf = reinterpret_cast<char*>(ACE_OS::malloc(size + 1));
+      if (!buf)
+	return -1;
 
-  ACE_UNUSED_ARG (buf);
-  ACE_UNUSED_ARG (format);
-  ACE_NOTSUP_RETURN (-1);
+      va_list aq;
+      va_copy (aq, argptr);
+      size = ACE_OS::vsnprintf(buf, size + 1, format, aq);
+      va_end (aq);
 
-# endif /* XPG5 || ACE_HAS_DINKUM_STL */
+      if (size != -1)
+        *bufp = buf;
+    }
+
+  return size;
 }
+#endif /* !ACE_HAS_VASPRINTF */
+
+#if defined (ACE_HAS_WCHAR)
+#if !defined (ACE_HAS_VASWPRINTF)
+int
+ACE_OS::vaswprintf_emulation(wchar_t **bufp, const wchar_t *format, va_list argptr)
+{
+  int size;
+
+  va_list ap;
+  va_copy (ap, argptr);
+  size = ACE_OS::vsnprintf(NULL, 0, format, ap);
+  va_end (ap);
+
+  if (size != -1) 
+    {
+      wchar_t *buf = reinterpret_cast<wchar_t*>
+	(ACE_OS::malloc((size + 1) * sizeof(wchar_t)));
+      if (!buf)
+	return -1;
+
+      va_list aq;
+      va_copy (aq, argptr);
+      size = ACE_OS::vsnprintf(buf, size + 1, format, aq);
+      va_end (aq);
+
+      if (size != -1)
+        *bufp = buf;
+    }
+
+  return size;
+}
+#endif /* !ACE_HAS_VASWPRINTF */
 #endif /* ACE_HAS_WCHAR */
 
 ACE_END_VERSIONED_NAMESPACE_DECL
Index: OS_NS_stdio.h
===================================================================
--- OS_NS_stdio.h	(revision 79869)
+++ OS_NS_stdio.h	(working copy)
@@ -208,6 +208,14 @@
 # endif /* ACE_LACKS_CUSERID */
   //@}
 
+  extern ACE_Export
+  int asprintf (char **bufp, const char* format, ...);
+
+# if defined (ACE_HAS_WCHAR)
+  extern ACE_Export
+  int asprintf (wchar_t **bufp, const wchar_t* format, ...);
+#endif /* ACE_HAS_WCHAR */
+
   ACE_NAMESPACE_INLINE_FUNCTION
   int fclose (FILE *fp);
 
@@ -408,6 +416,11 @@
   extern ACE_Export
   int printf (const char *format, ...);
 
+#if defined (ACE_HAS_WCHAR)
+  extern ACE_Export
+  int printf (const wchar_t *format, ...);
+#endif
+
   ACE_NAMESPACE_INLINE_FUNCTION
   int puts (const char *s);
 
@@ -458,6 +471,15 @@
 #endif /* ACE_HAS_WCHAR */
 
   ACE_NAMESPACE_INLINE_FUNCTION
+  int vasprintf (char **bufp, const char *format, va_list argptr);
+
+  ACE_NAMESPACE_INLINE_FUNCTION
+  int vprintf (const char *format, va_list argptr);
+
+  ACE_NAMESPACE_INLINE_FUNCTION
+  int vfprintf (FILE *fp, const char *format, va_list argptr);
+
+  ACE_NAMESPACE_INLINE_FUNCTION
   int vsprintf (char *buffer, const char *format, va_list argptr);
 
   ACE_NAMESPACE_INLINE_FUNCTION
@@ -465,12 +487,33 @@
 
 # if defined (ACE_HAS_WCHAR)
   ACE_NAMESPACE_INLINE_FUNCTION
+  int vasprintf (wchar_t **bufp, const wchar_t *format, va_list argptr);
+
+  ACE_NAMESPACE_INLINE_FUNCTION
+  int vprintf (const wchar_t *format, va_list argptr);
+
+  ACE_NAMESPACE_INLINE_FUNCTION
+  int vfprintf (FILE *fp, const wchar_t *format, va_list argptr);
+
+  ACE_NAMESPACE_INLINE_FUNCTION
   int vsprintf (wchar_t *buffer, const wchar_t *format, va_list argptr);
 
   ACE_NAMESPACE_INLINE_FUNCTION
   int vsnprintf (wchar_t *buffer, size_t maxlen, const wchar_t *format, va_list argptr);
 # endif /* ACE_HAS_WCHAR */
 
+#if !defined (ACE_HAS_VASPRINTF)
+  extern ACE_Export
+  int vasprintf_emulation (char **bufp, const char *format, va_list argptr); 
+#endif /* !ACE_HAS_VASPRINTF */
+
+#if defined (ACE_HAS_WCHAR)
+#if !defined (ACE_HAS_VASWPRINTF)
+  extern ACE_Export
+  int vaswprintf_emulation (wchar_t **bufp, const wchar_t *format, va_list argptr);
+#endif /* !ACE_HAS_VASWPRINTF */
+#endif /* ACE_HAS_WCHAR */
+
 } /* namespace ACE_OS */
 
 ACE_END_VERSIONED_NAMESPACE_DECL
Index: OS_NS_stdio.inl
===================================================================
--- OS_NS_stdio.inl	(revision 79869)
+++ OS_NS_stdio.inl	(working copy)
@@ -949,6 +949,69 @@
 #endif /* ACE_HAS_WCHAR */
 
 ACE_INLINE int
+ACE_OS::vasprintf (char **bufp, const char* format, va_list argptr)
+{
+#if defined (ACE_HAS_VASPRINTF)
+  return ::vasprintf (bufp, format, argptr);
+#else
+  return ACE_OS::vasprintf_emulation (bufp, format, argptr); 
+#endif /* ACE_HAS_VASPRINTF */
+}
+
+#if defined (ACE_HAS_WCHAR)
+ACE_INLINE int
+ACE_OS::vasprintf (wchar_t **bufp, const wchar_t* format, va_list argptr)
+{
+#if defined (ACE_HAS_VASWPRINTF)
+  return ::vaswprintf (bufp, format, argptr);
+#else
+  return ACE_OS::vaswprintf_emulation (bufp, format, argptr);
+#endif /* ACE_HAS_VASWPRINTF */
+}
+#endif /* ACE_HAS_WCHAR */
+
+ACE_INLINE int
+ACE_OS::vprintf (const char *format, va_list argptr)
+{
+  return ::vprintf (format, argptr);
+}
+
+#if defined (ACE_HAS_WCHAR)
+ACE_INLINE int
+ACE_OS::vprintf (const wchar_t *format, va_list argptr)
+{
+#if defined (ACE_HAS_VWPRINTF)
+  return ::vwprintf (format, argptr);
+#else
+  ACE_UNUSED_ARG (format);
+  ACE_UNUSED_ARG (argptr);
+  ACE_NOTSUP_RETURN (-1);
+#endif /* ACE_HAS_VWPRINTF */
+}
+#endif /* ACE_HAS_WCHAR */
+
+ACE_INLINE int
+ACE_OS::vfprintf (FILE *fp, const char *format, va_list argptr)
+{
+  return ::vfprintf (fp, format, argptr);
+}
+
+#if defined (ACE_HAS_WCHAR)
+ACE_INLINE int
+ACE_OS::vfprintf (FILE *fp, const wchar_t *format, va_list argptr)
+{
+#if defined (ACE_HAS_VFWPRINTF)
+  return ::vfwprintf (fp, format, argptr);
+#else
+  ACE_UNUSED_ARG (fp);
+  ACE_UNUSED_ARG (format);
+  ACE_UNUSED_ARG (argptr);
+  ACE_NOTSUP_RETURN (-1);
+#endif /* ACE_HAS_VFWPRINTF */
+}
+#endif /* ACE_HAS_WCHAR */
+
+ACE_INLINE int
 ACE_OS::vsprintf (char *buffer, const char *format, va_list argptr)
 {
   return ::vsprintf (buffer, format, argptr);

 
-- 
J.T. Conklin



More information about the Ace-users mailing list