0

What would be the proper way to do the following in C?

typedef struct Book {
    char* title;
    unsigned int year;
    void // print ??;
} Book;
void print_book(Book *book)
{
    printf("{\n\ttitle: \"%s\",\n\tyear: %d\n}\n", book->title, book->year);
}

int main(int argc, char * argv[])
{
    Book romeo = {
        .title="Rome & Juliet",
        .year=2000
    };
    print_book(&romeo); // how can I do romeo.print() instead?
}

What would be the correct way to define the struct member print to point to the print_book function?

gsamaras
  • 71,951
  • 46
  • 188
  • 305
carl.hiass
  • 1,526
  • 1
  • 6
  • 26
  • 2
    `romeo.print()` can't work in C, only in C++. In C the best you can do would require `romeo.print(&romeo)`, which isn't really better than `print_book(&romeo)`. And storing the function pointer would require extra space in every instance of the struct. – interjay Nov 01 '20 at 19:49

2 Answers2

3

The standard way to declare it would be:

typedef struct Book {
    char* title;
    unsigned int year;
    void (*func)(struct Book *book);
} Book;

I had to use struct Book instead of Book because the Book type has not yet been defined.

A better way to do it is:

typedef struct Book Book;
struct Book {
    char* title;
    unsigned int year;
    void (*func)(Book *book);
};

This creates the typedef before the structure definition, so the typedef name can then be used inside the structure.

To call it, you can do:

Book romeo;
romeo.func = print_book;
(*romeo.func)(&romeo);

In this context, you can optionally apply the & operator to print_book, e.g. romeo.func = &print_book;. The two are equivalent.

Note that I've called the function pointer func, but you can of course use any name you like (e.g. print, as in your post).

Tom Karzes
  • 22,815
  • 2
  • 22
  • 41
  • what does `(*func)` do? I've never seen that before in C. – carl.hiass Nov 01 '20 at 19:51
  • 1
    That's the traditional way to declare a function pointer (as opposed to an actual function definition). – Tom Karzes Nov 01 '20 at 19:52
  • @carl.hiass I suggest you read about **Function Pointers**. – gsamaras Nov 01 '20 at 19:52
  • @TomKarzes I see -- thank you. How would I then call the `print` member on the `romeo` object? – carl.hiass Nov 01 '20 at 19:53
  • @gsamaras sure can you provide a link with a good tutorial/documentation or something? – carl.hiass Nov 01 '20 at 19:54
  • @carl.hiass If you have `Book book;` and you set `book.func`, then you could call it as `(*book.func)(&book);` for example. The syntax has loosened up a bit in terms of including the `*`, but I like the call to match the declaration. That way it's clear that it's a function pointer. – Tom Karzes Nov 01 '20 at 19:55
  • @carl.hiass that is a plethora of options out there ;) Here is one I had used some years ago: https://www.cprogramming.com/tutorial/function-pointers.html – gsamaras Nov 01 '20 at 19:55
  • You'd need to call `romeo.print(&romeo)` or given a book pointer, `bookp->print(bookp)`. It isn't as canvas C++ where the `this` pointer is provided automatically. – Jonathan Leffler Nov 01 '20 at 19:58
  • @TomKarzes could you please show an example of the `func` function and how it would be called? I keep getting a seg fault when trying to access the `func` (which I've renamed from print). – carl.hiass Nov 01 '20 at 20:07
  • 1
    @carl.hiass Sorry for the name change, I hadn't realize that you had intended it to be called `print`, but you can use any name you like. The example call from my previous comment should work. I just added it to the answer - refresh the page and take a look. – Tom Karzes Nov 01 '20 at 20:11
0

Generally speaking, you can't, because your print() function does not know about the Book containing it (or a pointer to it). It would have to be a function which takes a Book or const Book * as a parameter.

Also, it is not a good idea to mix-up the logic for printing a book with the fundamental definition of a book. Maybe in some other context, books are printed differently? They're still books. In most (?) programming languages supporting objects with methods, simple abstract objects don't include methods for printing themselves - that is done using stand-alone functions or other constructs.

einpoklum
  • 118,144
  • 57
  • 340
  • 684