3

Is there any possible way in C (by using any function or any such kind) so that we can print the contents in a structure of different datatypes using a single line? Say for eg:

typedef struct ABC{
   int a;
   char b;
   long c;
}ABC1;

To print this we need to write: printf("%d %s %ld",ABC1.a,ABC1.b,ABC1.c)

Any other method so that i can print like printf("????",????ABC1)??

Parvathi
  • 97
  • 2
  • 8
  • `printf()` does not have some magical format specifier that can correctly recognise an arbitrary user-defined struct type, and cannot work out the type of an argument unless you correctly specify it in the format string. You can create a function which knows how to print a `struct ABC` with the desired format, but it will not know how to print some other struct type. – Peter Dec 21 '15 at 07:35
  • 2
    The format string should be `"%d %c %ld"` to match the actual arguments. Answer to the actual question is no, C has no "introspection" capability to figure out at runtime what a `struct` is made up of. To get and idea of the difficulties, see [Is there a way to print struct members in a loop without naming each member in C?](http://stackoverflow.com/a/27497861). – dxiv Dec 21 '15 at 07:38
  • @dxiv Why would it have to figure it out in run-time? Simply do it in compile-time, see my answer below. – Lundin Dec 21 '15 at 09:11

8 Answers8

2

First of all, the code probably wouldn't compile. You have defined ABC1 to be a typedef of the ABC struct. ABC1 is not a variable, so you cannot view the contents of ABC1.a.

Assuming you created a variable of type ABC1, you cannot "dump" the contents of the structure in a "logical" format using printf. The printf function receives a memory address and displays its contents based on the string given. The printf function cannot know the format of the structure if it only has a pointer to the memory (i.e. a bunch of bytes with no way of knowing what "type" of bytes).

D G
  • 699
  • 7
  • 7
2

No. Best solution would be to write a method like:

  1. void printABC(struct ABC*) or
  2. char* abcToString(struct ABC*) (and then print via %s).

Option 2 is probably better and can be used eg to write to file and so on.

John3136
  • 28,809
  • 4
  • 51
  • 69
1

No, printf() only has support for format specifiers for pre-defined data types, not for user defined data type (struct). Moreover, printf() also has no way to automatically learn the types of member variables of your struct.

You need to roll your own function to achieve the task you want.

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
1

Just like @john3136 said .

but here is an example for 1 .

void printABC(ABC1 *ABC) /* can also use 'struct ABC*' */
{
   printf("%d %c %ld",ABC->a,ABC->b,ABC->c);
   /* Equivalent to the below */
   /* printf("%d %s %ld",(*ABC).a,(*ABC).b,(*ABC).c); */
}

So now everytime you want to print the whole thing you just type something like

    ABC1 xyz;

    /* fill xyz here*/

    printABC(&xyz);

and It should type everything inside of ABC1 in one line.

P.P
  • 117,907
  • 20
  • 175
  • 238
Malek
  • 1,321
  • 2
  • 8
  • 11
  • 2
    Please be careful: you pass **`ABC`** as a pointer, and then use `ABC` **`1`** as an object. You should fix before downvotes fall... – Serge Ballesta Dec 21 '15 at 08:00
  • `void printABC(struct *ABC)` This won't even compile. If you meant `void printABC(struct ABC *abc)` maybe, then `ABC.a` would need to be `abc->d` and so on. – dxiv Dec 21 '15 at 08:20
  • 1
    P.S. With all due respect to @l3x who just edited the answer for the better, but the printf format is still wrong (should be `%c` not `%s`) and the subsequent `printABC(&ABC1);` won't compile since `ABC1` is a typedef. Honestly, I don't understand whomever upvoted this answer *after* my previous comment *after* @SergeBallesta's initial comment. – dxiv Dec 21 '15 at 08:38
  • 1
    @dxiv Sorry, I missed a couple. Should be fine now. – P.P Dec 21 '15 at 08:42
  • @l3x Looks good to me now. Though it's basically *your* answer at this point, if you compare the *before* and *after*. That was a commendable rescue op ;-) – dxiv Dec 21 '15 at 08:52
  • indeed its a whole new answer ! thanks guys . sincere apologies I haven't slept for 2 days but felt like answering some questions . – Malek Dec 21 '15 at 08:54
1

If you are using a somewhat modern C compiler, you can use a type-generic macro:

#include <stdio.h>

typedef struct {
   int a;
   char b;
   long c;
} ABC;

// printf conversion specifiers:
#define CS(x)   \
  _Generic((x), \
    int:  "%d", \
    char: "%c", \
    long: "%ld")


int main (void)
{
  ABC abc = {1, 'a', 2};

  printf(CS(abc.a), abc.a); printf(" ");
  printf(CS(abc.b), abc.b); printf(" ");
  printf(CS(abc.c), abc.c); printf(" ");

  return 0;
}
Lundin
  • 195,001
  • 40
  • 254
  • 396
1

Glibc allows you to establish your own format specifiers for printf and family to let you print UDTs, which would not otherwise be possible, through some extensions.

#include <stdio.h>
#include <printf.h> /* For PA_POINTER */

struct MyStruct
{
    int a;
    double b;
};

struct print_info;
int handler(FILE *stream, const struct print_info *i, 
        const void* const *args)
{
    const struct MyStruct *ptr = *( (const struct MyStruct**) (*args) );
    int rc = fprintf(stream, "a: %d, b: %f\n", ptr->a, ptr->b);

    return rc;
}

int print_arginfo (const struct printf_info *info, size_t n,
                      int *argtypes)
{
  if (n > 0)
    argtypes[0] = PA_POINTER;
  return 1;
}

int main(void)
{
    struct MyStruct s = {55, -3.14};
    /* We're gonna use the M specifier */
    int spec = 'M';
    int rc = register_printf_function(spec, handler, print_arginfo);

    if (rc != 0)
        return 1;

    printf("%M", &s);

};

You may find the documentation here.

edmz
  • 8,220
  • 2
  • 26
  • 45
0

One approach would be to define macros for the format string and corresponding argument:

#define ABCformat "%d,%c:%ld"
#define ABCarg(x) (x).a, (x).b, (x).c

and use it like this:

   ABC1 abc = {1, 'c', 42};
   printf("abc=" ABCformat "\n", ABCarg(abc));
nielsen
  • 5,641
  • 10
  • 27
0

You could use a macro to describe the structure, and then the macro could be used to help create the structure definition, and the pretty printer.

So, your code could look like:

PSTRUCT(ABC, (int, a), (char, b), (long, c));

int main () {
    ABC abc = {1, 'a', 2};
    printf("%s\n", tostring_ABC(&abc));
}

With the advantage that if you define another structure the same way, you will get a new print function available to you.

PSTRUCT(XYZ, (int, x), (double, y), (const char *, z));

The PSTRUCT macro expands into a structure definition and a function that walks the structure and emits values into a string.

#define PSTRUCT(N, ...) \
        typedef struct { XX(PSTRUCT_ARG, __VA_ARGS__) } N; \
        const char *tostring_##N (const N *x) { \
            static _Thread_local char buf[512]; \
            int len = 0; \
            XX(PSTRUCT_ARG_PRINT, __VA_ARGS__) \
            return buf; \
        }

There are helper macros to properly expand the individual fields into the structure definition and to print the field value.

#define PSTRUCT_ARG(X) PSTRUCT_ARG2 X
#define PSTRUCT_ARG2(X, Y) X Y;
#define PSTRUCT_ARG_PRINT(X) PSTRUCT_ARG_PRINT2 X
#define PSTRUCT_ARG_PRINT2(X, Y) \
        len += snprintf(buf+len, sizeof(buf)-len, CS(x->Y), #Y, x->Y);

The CS macro is as defined in Lundin's answer. And the iterating XX macro is defined as:

#define XX(X, ...) \
        XX_X(__VA_ARGS__, XX_5, XX_4, XX_3, XX_2, XX_1) \
        (X, __VA_ARGS__)
#define XX_X(_1,_2,_3,_4,_5,X,...) X
#define XX_1(X, _)      X(_)
#define XX_2(X, _, ...) X(_) XX_1(X, __VA_ARGS__)
#define XX_3(X, _, ...) X(_) XX_2(X, __VA_ARGS__)
#define XX_4(X, _, ...) X(_) XX_3(X, __VA_ARGS__)
#define XX_5(X, _, ...) X(_) XX_4(X, __VA_ARGS__)

How it works is briefly described in a different answer of mine.

For simplicity, the empty structure case is ignored.

Try it online!

jxh
  • 69,070
  • 8
  • 110
  • 193