Not without casting. qsort is still:
void qsort_r(void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *, void *),
void *arg);Inheritance is what will bite hard when hand-rolling OOP in C. For one, you can forget about the compiler enforcing substitutability and co/contravariance for you.
I think that's the ultimate culprit in everyone hating inheritance. If it weren't for Java, I think we'd all have a healthier view of OO in general.
I learned OO with C++ (pre C++-11), and now I work at a Java shop, but I'm luck that I get to write R&D code in whatever I need to, and I spend most of my time in Python.
In C++ and Python, you get to pick the best tool for the job. If I just need a simple function, I use it. If I need run-time polymorphism, I can use it. If I need duck-typing I can do it (in Python).
Without the need for strict rules (always/never do inheritance) I can pick what makes the best (fastest? most readable? most maintainable? most extensible? - It depends on context) code for the job.
Related to TFA, I rarely use inheritance because it doesn't make sense (unless you shoehorn it in like everyone in the threat is complaining about). But in the cases where it really does work (there really is a "is a" relation), then it does make life easier and it is the right thing.
Context matters, and human judgement and experience is often better than rules.
Everything is an object so there's no if-it's-an-object do this but if-it's-not-an-object do that.
Those first two you can do in (modern) Java. The third is a mess to be avoided at all costs. Interfaces and lambdas will cover most reasonable use cases for polymorphism.
https://www.cs.virginia.edu/~evans/cs655/readings/smalltalk....
Dynamic dispatch is implemented under the hood with lookup tables and function pointers. Sometimes, it is nice for a language to wrap a fiddly thing in a more abstract structure to make it easier to read, understand, and write.