Version 9 of Useful C Commands For Tcl

Updated 2004-11-17 14:59:35 by RHS

RHS 15Nov2004

A sprintf-like command that returns a Tcl_Obj. I use it to do things like:

 Tcl_ListObjAppendElement(interp, bcListObj, RHS_MakeStringObj("The offset: %d", pcOffset));

I'm sure there's places in the code that would run into problems when not using gcc, but thats all I use. If people want to improve this, that would be fantastic. Now, for the actual code...

 // This is coded specifically for glibc
 Tcl_Obj *
 RHS_MakeStringObj(const char *fmt, ...) {
    /* Guess we need no more than 100 bytes. */
    int n, size = 100;
    char *p;
    Tcl_Obj *retObj;

    va_list ap;
    if ((p = Tcl_Alloc (size)) == NULL)
        return (Tcl_Obj *)NULL;
    while (1) {
        /* Try to print in the allocated space. */
        va_start(ap, fmt);
        n = vsnprintf (p, size, fmt, ap);
        va_end(ap);
        /* If that worked, return the string. */
        if (n > -1 && n < size) {
            retObj = Tcl_NewStringObj(p, strlen(p));
            Tcl_Free(p);
            return  retObj;
        }
        /* Else try again with more space. */
        if (n > -1)    /* glibc 2.1 */
            size = n+1; /* precisely what is needed */
        else           /* glibc 2.0 */
            size *= 2;  /* twice the old size */
        if ((p = Tcl_Realloc (p, size)) == NULL) {
            return (Tcl_Obj *)NULL;
        }
    }
 }

RHS It appears that the microsoft compiler uses the name _vsnprintf rather than vsnprintf. So, if you're using MSVC, you may need to change the name.

From chat:

 <patthoyts>        In fact, on BSD it looks like you need stdio.h and stdarg.h

MAK Since you mention gcc, I suggest:

 #ifndef __GNUC__
 #   define  __attribute__(x)
 #endif

 Tcl_Obj *
 RHS_MakeStringObj(const char *fmt, ...) __attribute__((format(printf, 1, 2))))
 ...

This tells gcc that the form of the arguments is the same as a printf and that it should check to make sure that the number of arguments and their types are appropriate given the format string, and issue errors/warnings if not, just as if you mess up a printf() call.

(Btw, that's great. It would be cool to TIP that into the core and use it for [L1 ].)

RHS 16Nov2004 Having been thinking about what it would take to add such a thing into the core, what I have found leads me to believe that the best option is to write code that calculates the size the string needed to store the results of a printf type input. With that, it would be possible to write the above code simple by just calculating the size needed, malloc'ing it, and using sprintf to write to it... then creating the Tcl_Obj from that string. This would free us from having to use nvsprintf, which is not particularly portable. I'll put some free time (what little I have, as I just bought a house) into writing such a beast. If anyone already has such code and is willing to release it under the Tcl license, that would simplify things :)

MAK I'd imagine you could use Tcl_FormatObjCmd() as a starting point.

RHS Tcl_FormatObjCmd() handles a bit more than I had planned on implementing. Specifically, it handles XPG3 positional arguments, which I hadn't planned on handling (as far as I can tell, they'd be a royal pain do deal with using varargs). There's a couple simple implementations of "take format string and values and figure out the length" out there, and I was planning on going that route.


Category Dev. Tools