简体   繁体   中英

How to create user defined format specifiers in C

I am new to C programming. I was curious about whether I can make my own user-defined format specifier. But, unfortunately I don't know where to start from. Can someone please guide me. I read from somewhere that the header files of C are also written in C. So is it possible like copying the code from there and modifying it will do the work? I was saying like say I have defined a structure:

struct data
{
    int d;
    char str;
};

Now we have typedef ed it to say data:

typedef struct data data;

what I want is when I will be writing the print statement then, if I have made a format specifier for data as %q then:

printf("%q",x);   // Where x is a data type variable.

It will print the int part and then the String part on it's own.

Please help.

Here is an example how to define a printf custom conversion for systems that use the gnu C standard library. The program first prints the glibc version in case the printf API changes (as it has in the past):

#include <stdio.h>
#include <printf.h>
#include <gnu/libc-version.h> // for glibc version information

struct data
{
    int d;
    char str;
};


int
print_struct_ptr (FILE *stream,
              const struct printf_info *info,
              const void *const *args)
{
  const struct data *aStruct;
  int len;

  aStruct = *((const struct data **) (args[0]));
  /* Simply print the members to the stream */
  len = fprintf (stream, "[struct data at %p: d=%d, str=%c]\n",
        aStruct, aStruct->d, aStruct->str);
  return len;
}

int
print_struct_arginfo_sz (const struct printf_info *info, size_t n,
                      int *argtypes, int *size)
{
  /* We always take exactly one argument and this is a pointer to the
     structure.. */
  if (n > 0)
    argtypes[0] = PA_POINTER;
  return 1;
}

int main(void)
{
  struct data myStruct = {3, 'A'};
  printf("glibc version: %s\n", gnu_get_libc_version());
  register_printf_specifier('M', print_struct_ptr, print_struct_arginfo_sz);
  return printf("%M\n", &myStruct) > 0 ? 0 : -1;
}

This prints your data structure. (By the way, it is odd to name a character member str — is that supposed to be a pointer ? I left it as-is for now.)

The first thing to observe is that I pass a pointer to your struct. This makes it easier to register a customized conversion because printf knows how to extract a pointer from the variable argument list. That the argument is a pointer is indicated by inserting PA_POINTER in print_struct_arginfo_sz() . The other info fields are not needed here.

Because we pass a built-in type we need to define only two fairly simple callback functions. The first one, print_struct() , indeed prints the structure to which the passed pointer points simply by accessing its data members through the pointer. The second function, as mentioned, informs printf() what data type to extract (a pointer).

Finally, the register_printf_specifier() call in main() connects the dots: It registers these two callbacks (the function pointers are passed as arguments), connecting them with the format specifier 'M' .

Here is a sample session. The compiler warns about the "unknown" conversion specifier and, because the conversion is not recognized as such, about an argument that has no conversion specifier. This is one of the rare cases where such warnings can be ignored.

$ gcc -Wall -o printf-customization printf-customization.c && ./printf-customization
printf-customization.c: In function ‘main’:
printf-customization.c:43:19: warning: unknown conversion type character ‘M’ in format [-Wformat=]
   return printf("%M\n", &myStruct) > 0 ? 0 : -1;
                   ^
printf-customization.c:43:17: warning: too many arguments for format [-Wformat-extra-args]
   return printf("%M\n", &myStruct) > 0 ? 0 : -1;
                 ^~~~~~
glibc version: 2.24
[struct data at 0xbf9b6448: d=3, str=A]

That's not possible.

I would suggest to create a function that takes the struct as an argument and in its body it prints all the fields of the struct.

Then instead of:

printf("%q",x);   // Where x is a data type variable.

you can use your function to print, like this:

print_struct(x);

Or you can use a function pointer, like @WilliamPursell commented:

char *fmt = build_format_string();
printf(fmt, x);

Read more in Print the structure fields and values in C and Using functions to print structures .


If you want to print all the fields of the struct, then I suggest reading this Run through a structure and print all the values?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM