4

Is it possible to write a new format specifier in C? For example, let's say, %g is a format specifier which prints the ip address in A.B.C.D format from the unsigned integer equivalent.

int ip_addr = Some integer

printf("ip address = %g", ip_addr);

output: prints ip address in A.B.C.D format

hippietrail
  • 15,848
  • 18
  • 99
  • 158
Abhishek Sagar
  • 1,189
  • 4
  • 20
  • 44
  • 1
    You can write whatever you want in [tag:c], but you can't add a format specifier to the standard library function `printf()`. You can however write a variadic function that calls `printf()` for every format specifier and a custom routine for your specifiers, furthermore you can make your program call your `printf()` function instead and use the same name. But I would not recommend that. – Iharob Al Asimi Jan 06 '16 at 16:58
  • 3
    Step 1) Avoid using existing format specifiers like `"%g"` which is for `double`. – chux - Reinstate Monica Jan 06 '16 at 17:01
  • Hmm, IPs have 32 bits and should be unsigned. `int` is a bad idea for both reasons. – too honest for this site Jan 06 '16 at 19:17

4 Answers4

7

You would have to reimplement the printf function (more precisely its format parsing function). What you can do is wrap printf inside a function that supports ip addresses or do this:

#define FMT_IP "%d.%d.%d.%d"
#define FMT_IP_ARG(ip) getA(ip), getB(ip), getC(ip), getD(ip)

Where the getX functions or macros get the X part of the given integer representing an IP address.

Then:

printf("ip address = " FMT_IP "\n", FMT_IP_ARG(ip));
mikedu95
  • 1,725
  • 2
  • 12
  • 24
2

Adding a new specifier involves re-writing printf() or crafting your own function that handles the new specifier as well as printf() and calling that. Both of these approaches are difficult.


An alternative is to use "%s" and craft your own function that forms a string.

With C99 or later, use a compound literal to form the needed buffer space.

#include <stdio.h>
#define MYIP_N 16
#define MYIP(ip) myip((char [MYIP_N]){""}, (ip))

char *myip(char *dest, int ip) {
  snprintf(dest, MYIP_N, "%d.%d.%d.%d", (ip >> 24) & 255, (ip >> 16) & 255,
      (ip >> 8) & 255, (ip >> 0) & 255);
  return dest;
}


int main(void) {
  int ip1 = 0x01020304;
  int ip2 = 0x05060708;
  printf("%s %s\n", MYIP(ip1), MYIP(ip2));
  return 0;
}

Output

1.2.3.4 5.6.7.8

This works with printf() or fprintf(), sprintf(), even puts()

puts(MYIP(ip1));

1.2.3.4
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
2

I'd go for @chux's answer, which is simple and portable provided you can know how large a temporary buffer you need.

However, if you are using the GNU library (glibc), you can extend printf with new specifiers. It is highly recommended that you use upper-case letters as format codes, because almost all lower-case format codes are in use and the rest may be used by future C/Posix standards. The facility is documented in the glibc manual.

Based on the GNU facility, a similar pretty-much-compatible facility was written for the BSD library. Apparently, it is available on Mac OS X, although I can't vouch for that fact. I did find it in the Apple on-line man pages for xprintf(3) and xprintf(5).

There are examples in the documentation linked above.

Community
  • 1
  • 1
rici
  • 234,347
  • 28
  • 237
  • 341
0

You can't change standard library functions although you can write a wrapper function which will accept %g and will in turn call printf.

rootkea
  • 1,474
  • 2
  • 12
  • 32