0

I am looking to create a function that returns a string from format and the variable argument list. Something like:

const char* strfmt(char *format, ...);  

It seems to me that there ought to be a way to format a string and replace with the appropriate arguments and return the result. The closest I've seen still prints to the console (stdout):

void minimal_printf(char *fmt, ...) {
    va_list ap;
    char *p, *sval;
    int ival;
    double dval;
    va_start(ap, fmt);
 
    for (p = fmt; *p; p++) {
        if (*p != '%') {
            putchar(*p);
            continue;
        }

        switch (*++p) {
            case 'd':
                ival = va_arg(ap, int);
                printf("%d", ival);
                break;
            case 'f':
                dval = va_arg(ap, double);
                printf("%f", dval);
                break;
            case 's':
                for (sval = va_arg(ap, char *); *sval; sval++)
                putchar(*sval);
                break;
            default:
                putchar(*p);
                break;
        }
    }

    va_end(ap);
}

Also looks like the loop doesn't end correctly. Running this in my debugger causes a it to exit with an error code after 5 seconds:

(Process terminated with status -1073741819 (0 minute(s), 5 second(s))

I'm not so concerned about the looping issue because this isn't the solution I'll use in the end.

How do we not have a function that does this?

IAbstract
  • 19,551
  • 15
  • 98
  • 146
  • 4
    [snprintf](https://linux.die.net/man/3/snprintf) and [vsnprintf](https://linux.die.net/man/3/vsnprintf)? – kaylum Jul 15 '21 at 01:25
  • @kaylum: I understand that these functions require declaring a `const char*` with a known or expected size and passing into the function call. I was hoping to avoid that. I might as well require `strfmt(char* fmt, int numargs, ...)`. – IAbstract Jul 15 '21 at 01:44
  • Not sure I quite understand your exact requirements. `vsnprintf` does not require you to know the numargs. It accepts a `va_list`. – kaylum Jul 15 '21 at 01:47
  • 1
    It's not part of the C standard, but some implementations offer the ```asprintf``` variant, which will allocate space for the formatted string for you. – sj95126 Jul 15 '21 at 01:48
  • It sounds like you want a function that combines the functionality of `sprintf` and `strdup`, i.e. one that allocates and returns storage for the formatted string. You would later have to free the returned string yourself. – Tom Karzes Jul 15 '21 at 01:59
  • See: https://stackoverflow.com/a/19409495/3889449 – Marco Bonelli Jul 15 '21 at 02:00
  • Call `va_start` as you do now. Call `vsnprintf` with a zero-length buffer. It returns the number of characters it will produce. Allocate a buffer of that many bytes, plus one for the terminating null. Use `va_end` and `va_start` to reinitialize the `va_list`. Call `vsnprintf` with the new buffer. Call `va_end`. Return the new buffer. – Eric Postpischil Jul 15 '21 at 02:00
  • ex: `n = vsnprintf(p, size, fmt, ap);` creates a sized string, not even an estimate but an arbitrary number. The requirements is simple, create *resultstring* from *fmtstring* and *args*. Ex: `const char* message = strfmt("Hello, %s!", "World"); // "Hello, World!"` – IAbstract Jul 15 '21 at 02:10
  • @EricPostpischil: can you put that into an answer? – IAbstract Jul 15 '21 at 02:11
  • Another good possibility, mentioned in an answer at the duplicate question, involves the Posix `open_memstream` function. – Steve Summit Jul 15 '21 at 02:17
  • Why does every answer assume I want a Posix solution or something that isn't part of ANSI C? Nothing in the linked answer provides a solution for what I'm asking. – IAbstract Jul 15 '21 at 02:56
  • There is/was a proposal to make `asprintf()` and `vasprintf()` a part of Standard C, but it has not yet been accepted. They are a part of TR-24731-2, which you can find more about at http://open-std.org/jtc1/sc22/wg14 (the Standard C committee web site) or in [Do you use the TR-27431 "safe" functions?](https://stackoverflow.com/q/372980/15168). There isn't any standard function that does the job you want — so you either use the non-standard functions (they are not part of POSIX either, yet) or you write your own. It's annoying, but currently unavoidable. It may change when C2x is finalized. – Jonathan Leffler Jul 15 '21 at 05:11
  • The interface you seek is not provided directly by `asprintf()` or `vasprintf()`, but it would not be hard to wrap them in an inline function that did do what you wanted. The proposed interfaces are `int asprintf(char ** restrict ptr, const char * restrict format, ...);` and `int vasprintf(char ** restrict ptr, const char * restrict format, va_list arg);` — you'd need something like `const char *your_printf(const char *fmt, ...) { va_list args; va_start(args, fmt); char *result = NULL; if (vasprintf(&result, fmt, args) < 0) { free(result); result = NULL; } va_end(args); return result; }`. – Jonathan Leffler Jul 15 '21 at 05:20

0 Answers0