Resend: Re-implement MSVCRT *printf functions

Mike Hearn mh at
Sat Nov 27 17:15:24 CST 2004

On Sat, 27 Nov 2004 20:37:21 +0000, Aneurin Price wrote:
> So, I've had a go over this all, and here is a new version. Is it a step 
> in the right direction? I still don't really know what I should be doing 
> to improve this.

Yep, that's a lot better. There is still room for improvement

1) You add new files by diffing against /dev/null:

diff -u /dev/null dlls/msvcrt/printf.c >>/tmp/patch

otherwise AJ can't apply the patch easily.

2) The code style needs a bit of work:

   - There's not enough whitespace. Put spaces in between
     operators, ie instead of:


       specifier.flags &= ~SHORT_CHAR;

     and put blank lines in between variable declarations and code.
     Blank lines help split up large blocks of code and make them
     look easier to read.

   - Comments like this:


     are a bit hard to read. Just write /* size */, ie no doubling
     of asterisks, no extraneous lines, and not left aligned in the

   - Function names like these:


     are also hard to read, it'd be better as g_float_to_stringW.
     I understand that looks a bit odd, but it's the convention.

   - I know this one isn't your fault but you may want to use this:
     typedef mswchar MSVCRT_wchar_t;

     to reduce visual noise.

   - Please don't embed flag constants directly like this:

     MSVCRT_wchar_t *input=(MSVCRT_wchar_t *)HeapAlloc(GetProcessHeap(),8,(1+strlen(format))*sizeof(MSVCRT_wchar_t));

     eg what is 8 in this context? Use HEAP_ZERO_MEMORY or whatever.

   - Feel free to wrap lines if they get too long

   - Usually better to write this:

          MSVCRT_wchar_t  buf[2048]
	  MSVCRT_whcar_T *mem = buf;

     than this:

          MSVCRT_wchar_t buf[2048], *mem = buf;

   - Use the else keyword! Eg instead of this:

     if (num>0)
        length=ceil(log10(num)); /**Need to determine the string length**/
     if (num==0)                  /**necessary to hold the output.     **/
     if (num<0)

     write this:

     if (num > 0)   /* need to determine the string length */
        length = ceil(log10(num)); 
     else if (num == 0)  /* necessary to hold the output */
        length = 1;
     else if (num < 0)
        length = ceil(log10(-num));

     By the way, this seems a bit redundant: you can use abs() to 
     ensure a number is always positive, eg:

     if (num) 
	/* need to determine string length */
	length = ceil(log10(abs(num)));
	length =  1;

   - Multi-line comments shouldn't use a separate comment for each line:

     while (num)
        *tempstring++=digits[(num%10)]; /**Find each digit (starting at the little end)**/
        num/=10;                        /**tempstring then points to the output string, backwards**/
        charsdone++;                    /**Ie. it's used as a stack from which chars are popped  **/
     }                                   /**and pushed on to the output.                          **/

     could be:

     while (num)
	/* Find each digit (starting at the little end).
         * tempstring then points to the output string, backwards,
	 * ie it's used as a stack from which chars are popped. 

        *tempstring++ = digits[(num % 10)]; /* what's up with the superflous () here? */
        num /= 10;
   - What is this ?

     if ((specifier.precision!=-1) && (specifier.width>specifier.precision))
        (specifier.flags&=~ZERO_PAD); /**If there is a precision specified, and it is less**/
                                      /**than the specified width, don't use 0s to pad    **/

     This is exactly the sort of thing Alexandre meant when he said 
     the patch was hard to read. This is an if statement that looks like it does 
     nothing but actually does. It's also combined with one of those hard to read
     comments. Why is the statement enclosed in unnecessary brackets which
     make it look like a part of the expression? Write it like this instead:

     /* If there is a precision specified, and it is less than the specified width, 
      * don't use zeros to pad. 
     if ((specifier.precision != -1) && (specifier.width > specifier.precision))
	specifier.flags &= ~ZERO_PAD;

3) Why do you mix up MSVCRT_malloc, MSVCRT_free and HeapAlloc/HeapFree?

4) Watch out for  // style comments. It's an easy mistake to make (i have
done it before) but they aren't allowed.

5) #include "printfhelpers.c"  <-- this sort of thing is very unusual. Why
not use standard linkage, or include it all in one file? If the file is
getting too big then don't just #include other C files as that's
unexpected, split them up into separate compilation units.

6) Use a FIXME() especially in unusual situations that won't spam the user:

      if (specifier.precision>=4)
            *(tempstring-4)='N';    /**FIXME:Is this actually the right string? MSDN  **/

   Always do what the native DLL does, ignore MSDN if it's wrong. 
   If you decide the miss bits out, that's OK but make sure it's obvious with a nice
   big FIXME("Not quite the same as native\n"); statement so people debugging can
   get a hint.

7) If you want to switch some code off (why is it being submitted if it's unnecessary?)
   use #if 0/#endif not multi-line comments like this:

   /*    if (specifier.flags&PRECISION_COUNT)
        return floattostringw(args,digits,specifier);
    }   */

   as it's quite easy to miss them if you're not using a syntax-highlighting editor
   (a patch viewer for instance)

That's a lot of comments! Still, don't be discouraged, the code itself seems fairly sound,
it's clearly been tested against native which is great, and it seems very complete which
is also a good start. Good luck!

thanks -mike

More information about the wine-devel mailing list