2

I am porting an application on Mac OS X which was written for Windows.

In this application, there are many instances of _vscwprintf and _vscprintf.

This question helped me to implement _vsprintf on Mac OS X. But same technique for _vswprintf is not working.

Can anyone give the alternative of _vscwprintf on Mac OS X? Or there any equivalent method for this?

Community
  • 1
  • 1
doptimusprime
  • 9,115
  • 6
  • 52
  • 90
  • 1
    See also [Getting the length of a formatted string from `wsprintf()`](http://stackoverflow.com/questions/6132009/getting-the-length-of-a-formatted-string-from-wsprintf). – Jonathan Leffler Jul 23 '13 at 03:16

2 Answers2

6

Microsoft describes the functions as returning the number of characters that would be used if the string were formatted — note that they are documented as not including the null terminator.

int _vscprintf(
   const char *format,
   va_list argptr 
);
int _vscwprintf(
   const wchar_t *format,
   va_list argptr 
);

Initial answer

These functions can, therefore, be emulated with vsprintf() and vswprintf():

int _vscprintf(const char *format, va_list argptr)
{
    return(vsnprintf(0, 0, format, argptr));
}

int _vscwprintf(const wchar_t *format, va_list argptr)
{
    return(vswprintf(0, 0, format, argptr));
}

It is up to you whether you remove the leading underscore; I would.

Note that the _vscwprintf() implementation above is flawed; see the code below.


vscprintf() and scprintf()

Apologies: I wrote vsprintf() where I needed to write vsnprintf() (now fixed in the code above); however, vswprintf() already has the safer interface with the buffer length, so there is no vsnwprintf(). There's a reason I prefer to test compile code before (or shortly after) posting it — it's been irksome not having the wherewithal to do so for a couple of days.

Here's an SSCCE for vscprintf() (and scprintf()):

#include <stdio.h>
#include <stdarg.h>

extern int vscprintf(const char *format, va_list argptr);
extern int scprintf(const char *format, ...);

int vscprintf(const char *format, va_list argptr)
{
    return(vsnprintf(0, 0, format, argptr));
}

int scprintf(const char *format, ...)
{
    va_list args;
    va_start(args, format);
    int rc = vscprintf(format, args);
    va_end(args);
    return rc;
}

int main(void)
{
    int l = scprintf("%-8s %8d\n", "abc", 123);
    if (l > 0)
    {
        char buffer[l+1];
        int n = snprintf(buffer, sizeof(buffer), "%-8s %8d\n", "abc", 123);
        printf("%d = %d: %s", l, n, buffer);
    }
    return 0;
}

Output:

18 = 18: abc           123

vscwprintf() and scwprintf()

It turns out to be harder to simulate _vscwprintf() because the vswprintf() function is not as helpful as the vsnprintf() function. Specifically, vswprintf() reports an error if the formatted string won't fit in the formatted space, whereas vsnprintf() reports the number of characters that would have been needed in the buffer if it was going to fit. Hence, you have to work by trial and error:

#include <stdio.h>
#include <stdarg.h>
#include <wchar.h>

extern int vscwprintf(const wchar_t *format, va_list argptr);
extern int scwprintf(const wchar_t *format, ...);

int vscwprintf(const wchar_t *format, va_list argptr)
{
    // Unlike vsnprintf(), vswprintf() does not tell you how many
    // characters would have been written if there was space enough in
    // the buffer - it just reports an error when there is not enough
    // space.  Assume a moderately large machine so kilobytes of wchar_t
    // on the stack is not a problem.
    int buf_size = 1024;
    while (buf_size < 1024 * 1024)
    {
        va_list args;
        va_copy(args, argptr);
        wchar_t buffer[buf_size];
        int fmt_size = vswprintf(buffer, sizeof(buffer)/sizeof(buffer[0]), format, args);
        if (fmt_size >= 0)
            return fmt_size;
        buf_size *= 2;
    }
    return -1;
}

int scwprintf(const wchar_t *format, ...)
{
    va_list args;
    va_start(args, format);
    int rc = vscwprintf(format, args);
    va_end(args);
    return rc;
}

int main(void)
{
    int l = scwprintf(L"%-8ls %8d\n", L"abc", 123);
    if (l > 0)
    {
        wchar_t buffer[l+1];
        int n = swprintf(buffer, sizeof(buffer)/sizeof(buffer[0]), L"%-8ls %8d\n", L"abc", 123);
        wprintf(L"%d = %d: %ls", l, n, buffer);
    }
    return 0;
}

When run, this produces the output

18 = 18: abc           123

(the same as before).

Tested on Mac OS X 10.8.3 using GCC 4.7.3 (which was built on Mac OS X 10.7.5, but that shouldn't cause any problems).

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Thanks for the answer. But the problem with this is that it is not working for me. I am using it in following way: va_list copyarg; // Copy of the argument. int len; // Return value. va_copy(copyarg, argptr); len = vswprintf(NULL, 0, pFormat, copyarg); va_end(copyarg); return len; Am I doing it right? What is the problem in it? Value of len (return value) is -1. – doptimusprime May 03 '13 at 05:41
  • I had forgotten that you would need to copy the `va_list` in the calling code; the Microsoft page says nothing about it explicitly, but if you call a `v*printf()` function, the `va_list` is used up by the call. What you show should work (`va_copy()` is like `strcpy()` with assignment order — target first, source second); what I show should work too. I am temporarily without easy access to a machine that has a compiler (don't ask!) aboard, so I can't validate with an SSCCE. – Jonathan Leffler May 03 '13 at 14:35
  • For char *, I did the same way as you mentioned and is working fine. However, same way is not working for wchar_t * string. Please provide solution for wchar_t * also. – doptimusprime May 06 '13 at 03:45
  • @dbasic: I've provided an answer for wide strings, but you can read the manual pages too. If you don't know how to do that, it is high time you did. You can find the POSIX descriptions of functions in C99 (and POSIX itself, of course) at http://www.opengroup.org/onlinepubs/9699919799/toc.htm. – Jonathan Leffler May 06 '13 at 04:24
  • 1
    The _vscwprintf replacement doesn't work, because vswprintf returns -1 when the string to write is larger than the buffer size. (By comparison, vsnprintf returns the number of characters that would have been written had the buffer been large enough.) No idea why there's a difference. Does anyone have a working replacement for _vscwprintf? – rho21 Feb 17 '16 at 15:39
  • @rho21: The last chunk of code shown above notes the behaviour of `vwsprintf()` and iterates when `vswprintf()` reports failure, allocating twice as much space to play in each time so that it can get around the problem. Are you saying that code is not working? Granted, if your string needs more than a (binary) million `wchar_t` values, my code gives up, but you can fix that to suit your requirements. At bigger sizes, continuing to use the stack would be ill-advised; you'd use `malloc()` and `realloc()` — or just `realloc()` — remembering to `free()` the data before returning from the function. – Jonathan Leffler Feb 17 '16 at 15:54
  • Sorry, I saw the answer I was after at the top and didn't look further down. In this case, the code I'm adapting is measuring in order to avoid multiple allocs, so I'll have to go with a different work-around. Thanks for your quick reply. – rho21 Feb 17 '16 at 16:11
  • @rho21: Note that the code as written will usually only do a single allocation unless you're in the habit of formatting messages that are longer than 1 K characters. You can tune the initial allocation to suit, but for many people, 1 K is overkill. Also, as written, it is not using `malloc()`; it uses a VLA (variable length array) allocated on the stack. Any alternative approach is likely to involve (re)implementing the guts of the *w*printf routine(s) to scan through the format string and the arguments; that is extremely hard to do accurately. Good luck with your endeavours. – Jonathan Leffler Feb 17 '16 at 16:57
  • You might also want to look at [`vasprintf()`](http://linux.die.net/man/3/vasprintf) and relatives. However, I've not found online documentation for a `vaswprintf()` variant — but it was a part of the TR 24731-2 "Extensions to the C Library — Part II: Dynamic Allocation Functions". See [Do you use the TR 24731 'safe' functions?](https://stackoverflow.com/questions/372980/do-you-use-the-tr-24731-safe-functions) for references to the documents. – Jonathan Leffler Feb 17 '16 at 17:48
1

I recommend using open_wmemstream instead. Something like that:

#include <stdio.h>
#include <string>
#include <stdarg.h> 

using namespace std;

wstring wstring_format(const wchar_t* format, ...)
{
    wchar_t* buf;
    size_t size;
    FILE* stream = open_wmemstream(&buf, &size);

    va_list args;
    va_start(args, format);
    vfwprintf(stream, format, args);
    va_end(args);

    fclose(stream);
    wstring result(buf, buf + size);
    free(buf);

    return result;
}
Andrey Belykh
  • 2,578
  • 4
  • 32
  • 46