Avoiding the bloated printf

I decided to recreate my code without using fprintf or putc or similar. I did fine until I needed to print an integer. I needed a way to out put it in a non binary form. I’m not sure what makes fprintf bloated perhaps it’s this very thing that does it. I came up with this code:


#include <stdarg.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>

/*
 * This is for printing error messages. It can only take 2
 * unsigned long long ints and the last argument is to tell it where to
 * put them. It then takes up to 32 string ptrs. If you to print an int or
 * similar then set where to the spot you wish it to be printed using a bit
 * mask. The first 32 bits are for the first unsigned long long int and the
 * second for the second unsigned long long int. Remember that it does no
 * parsing of errnos by default. Remember to pass a NULL pointer at the end
 * of all arguments. The first argument is the file descriptor to write to.
 *
 *
 * So, to sum up, this is a very limited printf.
 */

signed int write_str2_des(signed int filedes, unsigned long long int \
printint1, unsigned long long int printint2, unsigned long long int where, ...)
{
   va_list myvars;
   static const unsigned char badstr] = "Fatal: no message.
\0";
   static const unsigned char *one = "1\0", *zero = "0\0";
   static unsigned char *tobewritten;
   static ssize_t written;
   static size_t writeme;
  
   errno = 0;
  
   va_start(myvars, where);
  
   tobewritten = (unsigned char *)va_arg(myvars, char *);
  
   if(tobewritten == NULL)
   {
       tobewritten = badstr;
      
       writeme = strlen(tobewritten);
      
       do
       {
           written = write(filedes, tobewritten, writeme);
           writeme = writeme - (long unsigned int)written;
           tobewritten += writeme;
       } while(writeme && (!errno || errno == EINTR));
   }
  
   do
   {
       if(where & 0X1)
       {
           /*
            * This is the easy way to write an
            * [un]signed [short] [long] [long] integer.
            */
          
           while(printint1)
           {
               if(printint1 & 0X8000000000000000)
               {
                   writeme = sizeof(*one);
                  
                   do
                   {
                       written = write(filedes, one, writeme);
                       writeme -= (long unsigned int)written;
                   } while(writeme && (!errno || errno == EINTR));
               }
               else
               {
                   writeme = sizeof(*zero);
                  
                   do
                   {
                       written = write(filedes, zero, writeme);
                       writeme -= (long unsigned int)written;
                   } while(writeme && (!errno || errno == EINTR));
               }
              
               printint1 = printint1 << 1;
           }
          
       }
      
  
       //Check the value of errno as I might need to abort early.
       if(errno)
       {
           return(errno);
       }
      
       if(where & 0X80000000)
       {
           /*
            * This is the easy way to write an
            * [un]signed [short] [long] [long] integer.
            */
          
           while(printint2)
           {
               if(printint2 & 0X8000000000000000)
               {
                   writeme = sizeof(*one);
                  
                   do
                   {
                       written = write(filedes, one, writeme);
                       writeme -= (long unsigned int)written;
                   } while(writeme && (!errno || errno == EINTR));
               }
               else
               {
                   writeme = sizeof(*zero);
                  
                   do
                   {
                       written = write(filedes, zero, writeme);
                       writeme -= (long unsigned int)written;
                   } while(writeme && (!errno || errno == EINTR));
               }
              
               printint2 = printint2 << 1;
           }
       }
      
      
       //Check the value of errno as I might need to abort early.
       if(errno)
       {
           return(errno);
       }
      
      
      
       writeme = strlen(tobewritten);
      
       do
       {
           written = write(filedes, tobewritten, writeme);
           writeme = writeme - (long unsigned int)written;
           tobewritten += writeme;
       } while(writeme && (!errno || errno == EINTR));
      
       tobewritten = (unsigned char *)va_arg(myvars, char *);
      
       if(where > 1)
       {
           where /= 2;
       }
       else
       {
           where = 0;
       }
      
   } while(tobewritten != NULL && !errno);
  
   va_end(myvars);
  
   return(errno);
}

My function compiles to 5.6 KB as a lib, 21 KB total. When I use fprintf then it’s 26.2 KB.
It’s simple, quick, and for someone who does not know binary, confusing.
I want your professional opinion of this code.
What do you think?
Is it a good alternative to fprintf?
Why ask if it compiles and works? Because I want an opinion. My code does not have to break to ask about it, right? I mean, I want to write code that works, is well commented, is helpful to the user, and is generally good code.
There are 2 concerns I have.
One, the ints are not printed in a user friendly format. My error stings should be descriptive enough though.
Two, I have no idea how the computer holds floats so this thing at this time does not print floats. Anyone know how the computer holds floats? A good way to print them?

Hi ballsystemlord,

In my view there are other aspects about printf (and scanf !) that are nasty.

The strings passed to these two functions to tell them what to print or scan differ !
For me that’s the most nasty thing.

Otherwise I don’t think that it’s worth spending significant amounts of lifetime on
re-inventing the wheel all the time.

Efficiency - in terms of lifetime :slight_smile: - may just as well be to adopt reliable solutions found,
if they only take a few milliseconds during the execution of a program.

If you develop new code to replace existing code,
this only is worth it, if your new code is considerably faster,
and perhaps even more reliable - but don’t you think that the
guys & dolls working on that subject beforehand would have been
numb or blind - for sure they were not !!

But to develop such code, on the other hand, will cost you days,
weeks, or even months of your lifetime.
Is it worth it ?

I mean, aren’t there really no other problems that could need a development of code
than particularly printf ?

My advice:
Spare your time, take printf() as it is, and take scanf() as it is,
and go on.
Means, look for some other subject for your work that
could benefit you and others much more !

When I was 30 years old,
I still thought I could optimize everything
(including C code for I/O routines like printf …).

It only took me 5 years more to learn, that efficiency may
rather mean to identify these very small pieces of code that really
make the difference with respect to speed,
while on the other hand, efficiency with respect to my lifetime
in most cases will mean to write readable code or code that
is easy to read and maintain - code so simple that errors
can be detected easily.
Code with many comments in it, and code that isn’t nested.

Imagine that you write a printf() that saves you 2 ms of time
when you execute it (that would be quite a lot, already !).

How many hours, days or even weeks do you spend to write that?
Do you think you finally gain anything from that?

OK, full stop.

I still have something else.

In your code you had

if(printint1 & 0X8000000000000000)

There may be a bug here.

In C it doesn’t matter whether you give an integer as decimal or hex (your 0x8…).

But what matters is the size of that integer.

0x8000000000000000 for sure won’t fit to a 2 bytes integer.

At least it will have to be a (long int).

Sorry if I don’t like to count the zeros in your hex number above,
but I’m quite sure that you should at least append an ‘L’, means
0x8000000000000000L

I’m too tired now to thoroughly check it out,
if a suffix of ‘L’ in this case would even be sufficient.

If it wouldn’t, then your code would have been everything else
but efficient, because it could produce errorneous behaviour,
the reason of which would be quite difficult to detect,
because the error wouldn’t be that obvious.

That’s a very different aspect of efficiency !

Wish you all the best !
Mike

I will not quote you due to the length of the post.
Your words are most wise, I was thinking though, that the developers of the dietlibc had some other method of printing in mind. Upon reading code from various projects produced by people intent on avoiding the bloated printf I realized that they used plain old write which is unsuitable for my purposes. So, I shall keep this function as it will make my life easier when debugging some of my code. But other then that I shall use printf.