0

Let's say I want to create two sub-types of a type in C. For example:

typedef struct Car {
    char *make;
} Car;

typedef struct Book {
    char *title; 
    char *author;
} Book;

What are the options for doing this? I come from a python background, so am used to being able to do something like:

class Item:
    pass

class Car(Item):
    ...

class Book(Item):
    ...

The only thing that comes to mind for C is doing a union or enum but then it seems like it will have a ton of un-used fields. For example:

typedef struct Item {
    enum {Car, Book} type; // hide the class here
    char *make;
    char *title; // but now will have a bunch of null fields depending on `Item` type
} Item;

Or:

typedef struct Item {
    union {
        Car;
        Book;
    } item;
} Item;

What options are there to do this sort of pseudo-subclassing in C? My goal here is to be able to pass 'multiple types' to the same function, in this case Car and Book.

David542
  • 104,438
  • 178
  • 489
  • 842
  • Are you sure you want to go through with this? It isn't really natural in C, hence the problems. You'd probably use `struct Item { enum { Car, Book } type; union { Book book; Car car; }; };` or thereabouts. The item structure is then one `int` bigger than the largest member of the "class" (C doesn't have classes, of course). – Jonathan Leffler Jan 13 '21 at 23:13
  • @JonathanLeffler to be honest, I'm not sure and not advanced enough to know the difference. If I were to try and do something similar to the above though -- where I want to be able to, for example, pass both a Book and a Car to the same function (let's say an Inventory function) how could that be done? – David542 Jan 13 '21 at 23:17
  • 1
    What's the point of inheriting from an empty class in your Python example? Normally you would have methods in `Item` that would then apply to `Book` and `Car`. If there's nothing to inherit, there's no reason for the class relationship. – Barmar Jan 13 '21 at 23:18
  • @Barmar I wouldn't do that in the first place in python because I can pass multiple types to the same function. I just used that as an example to show what I was trying to do. – David542 Jan 13 '21 at 23:19
  • That's what I assumed, until your comment about what should go into `struct Item`. What should go into `struct Item` is the C equivalent of what goes into `class Item` in Python. – Barmar Jan 13 '21 at 23:21
  • @Barmar I see. In that case are you allowed to create a blank struct in C like `typedef struct Item {} Item;` or is that illegal? – David542 Jan 13 '21 at 23:21
  • 1
    See https://stackoverflow.com/questions/755305/empty-structure-in-c – Barmar Jan 13 '21 at 23:22
  • @JonathanLeffler fro trying to accomplish something like the above, what would you suggest doing? – David542 Jan 13 '21 at 23:44
  • 2
    If I needed to do it, I'd probably use something close to the discriminated union I showed in my first comment — or I'd use a variant containing pointers to the different structure types in the union instead of using copies of the types. Embedding the types probably makes things easier. I'm using C11 anonymous union members; given `struct Item *ip;`, you could refer to `ip->type` and `ip->car` would be the `Car` member, and `ip->book` would be the `Book` member. You can refer to the elements of the two different types correctly. Don't be seduced by `void *`; it loses all type checking. – Jonathan Leffler Jan 13 '21 at 23:48

1 Answers1

2

Put the common superclass as an initial member of each subclass.

typedef struct Car {
    Item item;
    char *make;
} Car;

typedef struct Book {
    Item item;
    char *title;
    char *author;
} Book;

You can then cast a Book* or Car* to Item* when calling generic Item functions.

Another option is a discriminated union.

typedef struct Item {
    // general Item stuff goes here
    enum {Car, Book} type;
    union {
        Car car;
        Book book;
    };
} Item;

But if you need to do a lot of this, maybe you should use C++ instead of C, so you have real class hierarchies.

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • thanks. What would `typedef struct Item {...} Item` look like in the first example then? – David542 Jan 13 '21 at 23:16
  • It's whatever common stuff you want to inherit in all the subclasses. – Barmar Jan 13 '21 at 23:16
  • If it's empty like your Python example, then there's no need for `Item` in the first place. – Barmar Jan 13 '21 at 23:17
  • is the second method you've described common in C? Or the whole 'class-thing' is not much used in C? – David542 Jan 13 '21 at 23:43
  • I don't think it's common. But an example that's similar is `struct sockaddr` in sockets. This is a generic type, but callers use specific types like `struct sockaddr_in` and cast the pointer to the generic type. – Barmar Jan 13 '21 at 23:46