Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to make GCC/Clang aware of inheritance in C?

I'm writing a C library that uses some simple object-oriented inheritance much like this:

struct Base {
    int x;
};

struct Derived {
    struct Base base;
    int y;
};

And now I want to pass a Derived* to a function that takes a Base* much like this:

int getx(struct Base *arg) {
    return arg->x;
};

int main() {
    struct Derived d;
    return getx(&d);
};

This works, and is typesafe of course, but the compiler doesn't know this. Is there a way to tell the compiler that this is typesafe? I'm focusing just on GCC and clang here so compiler-specific answers are welcome. I have vague memories of seeing some code that did this using __attribute__((inherits(Base)) or something of the sort but my memory could be lying.

like image 377
Shum Avatar asked Mar 21 '23 22:03

Shum


1 Answers

This is safe in C except that you should cast the argument to Base *. The rule that prohibits aliasing (or, more precisely, that excludes it from being supported in standard C) is in C 2011 6.5, where paragraph 7 states:

An object shall have its stored value accessed only by an lvalue expression that has one of the following types:

— a type compatible with the effective type of the object,

This rule prevents us from taking a pointer to float, converting it to pointer to int, and dereferencing the pointer to int to access the float as an int. (More precisely, it does not prevent us from trying, but it makes the behavior undefined.)

It might seems that your code violates this since it accesses a Derived object using a Base lvalue. However, converting a pointer to Derived to a pointer to Base is supported by C 2011 6.7.2.1 paragraph 15 states:

… A pointer to a structure object, suitably converted, points to its initial member…

So, when we convert the pointer to Derived to a pointer to Base, what we actually have is not a pointer to the Derived object using a different type than it is (which is prohibited) but a pointer to the first member of the Derived object using its actual type, Base, which is perfectly fine.

About the edit: Originally I stated function arguments would be converted to the parameter types. However, C 6.5.2.2 2 requires that each argument have a type that may be assigned to an object with the type of its corresponding parameter (with any qualifications like const removed), and 6.5.16.1 requires that, when assigning one pointer to another, they have compatible types (or meet other conditions not applicable here). Thus, passing a pointer to Derived to a function that takes a pointer to Base violates standard C constraints. However, if you perform the conversion yourself, it is legal. If desired, the conversion could be built into a preprocessor macro that calls the function, so that the code still looks like a simple function call.

like image 131
Eric Postpischil Avatar answered Mar 30 '23 00:03

Eric Postpischil