1

I have been going through source code of python. It looks like every object is derived from PyObject. But, in C, there is no concept of object oriented programming. So, how exactly is this implemented without inheritance?

2
  • Doesn't the source code you're reading tell you how exactly this is implemented? Commented Jul 16, 2019 at 13:57
  • And if C did support inheritance, how do you think such support got there? Any language feature is ultimately implemented in a language that itself does not have that feature. Commented Jul 16, 2019 at 14:03

2 Answers 2

2

Your assertion that C has no concept of object-oriented programming is wrong. C doesn't explicitly have OOP, and it wasn't built with it in mind, but you can certainly do OOP things with C without too much effort. This comes from leveraging the fact that C doesn't actually really care what a struct's internal memory layout looks like. If you have two structs:

struct A {
    int field1;
    int field2;
    double field3;
};

struct B {
    A fieldA
    int field4;
    float field5;
};

then that essentially lets B behave as a subclass of A. After all, the first part of B's memory layout is exactly the same as A's memory layout. If you pass it around as a void pointer, then you can typecast away and C doesn't really care:

void doSomething(void *obj) {
    int field2value = ((A*) obj).field2;
    float field5value = ((B*) obj).field5;
    printf("field2: %d\nfield5: %f", field2value, field5value);
}

You tell C what type you think the void pointer is supposed to be, and it makes that happen. And you get unexpected behavior if you guess wrong, or you get a segfault if the type you think it's supposed to be is larger than the type it actually is. You can use this to clumsily implement inheritance:

void constructA(void* obj) {
    a = (A*) obj
    a.field1 = 4;
    a.field2 = 2;
    a.field3 = 3.14;
}

void constructB(void *obj) {
    constructA(obj);
    b = (B*) obj;
    b.field4 = 7;
    b.field5 = 6.28;
}

int main() {
    B *myObj = malloc(sizeof(B));
    constructB(myObj);
    free(myObj);
}   

If one of A's variables is a function pointer, then that's fine. That function pointer gets passed down alongside the rest. You can call it from anywhere, after all. You can supplant its functionality in a "subclass", and then still call the original version later on, if you don't actually override its spot in memory - or if you do, you could have your replacement manually call the function it was pointing to.

A lot of advanced C code uses a similar pattern for replicating the idea of inheritance (or, alternatively, just uses C++, which optimizes this whole arrangement and abstracts it away so that programmers can work with a more intuitive syntax).


But even then, that's missing the point. One of my favorite things about python is, at the deepest level, its consistency - everything is an object, and all objects are basically hashmaps with names pointing to a references. Python's idea of duck typing works not because the underlying C code has any idea of inheritance, but because the code just looks for an attribute with the right name, and if it finds one, it uses it.

Subclasses in python, then, are the same as above - a new python object that initializes itself from the top down, adding more and more fields to the hashmap as it gets closer to the bottom.

Sign up to request clarification or add additional context in comments.

Comments

2

What makes the Object Oriented programming paradigm is the relation between "classes" as templates for a data set and functions that will operate on this data set. And, the inheritance mechanism which is a relation from a class to ancestor classes.

These relations, however, do not depend on a particular language Syntax - just that they are present in anyway.

So, nothing stops one from doing "object orientation" in C, and in fact, organized libraries, even without an OO framework, end up with an organization related to OO.

It happens that the Python object system is entirely defined in pure C, with objects having a __class__ slot that points to its class with a C pointer - only when "viwed" from Python the full represenation of the class is resented. Classes in their turn having a __mro__ and __bases__ slots that point to the different arrangements of superclasses (the pointers this time are for containers that will be seen from Python as sequences).

So, when coding in C using the definitions and API of the Python runtime, one can use OOP just in the same way as coding in Python - and in fact use Python objects that are interoperable with the Python language. (The cython project will even transpile a superset of the Python language to C and provide transparent ways f writing native code with Python syntax)

There are other frameworks available to C that provide different OOP systems, that are equaly conformant, for example, glib - which defines "gobject" and is the base for all GTK+ and GNOME applications.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.