Which is why I'm seriously tempted to add a simple template system on top of C (it has already been done, by the way), then implement containers on top of that.
With that, I get the power without the rabbit hole.
Wouldn't it just be simpler to fuse off the part of your linker that handles vtables? IIRC That'll kill every C++ feature that's objectionable to someone who likes the idea of 'C with templates'
Unless you want a truly simple C template system, in which case aren't the C11 type sensitive macros sufficient?
Hey, that sounds cool. Do you have a link? A quick search doesn't seem to work.
The vtables on the other hand I will steer clear from. Closures are way more useful than class based polymorphism. Combine it with parametric polymorphism (templates), and you won't need to subclass anything ever again —okay, I'm exaggerating a little.
On that topic, it is possible to do cool things with macros with GCC extensions. A simple example of generic macros was discussed about a week ago: https://news.ycombinator.com/item?id=7896280
It's called _Generic (which should be easier to Google), but the fact that you have to modify the macro wherever you want to use it with a new type severely limits its usefulness.
Type erased closures are strictly less useful than subtype polymorphism + virtual functions (another form of type erasure) as the latter allows you to share data among an entire interface.
Closures are simpler, and therefore less cumbersome for 90% of the use cases. I prefer this compromise over the other.
Also, I'm not sure I get this "type erasure" business. In ML languages, there is no such erasure. I guess you need this erasure because the language doesn't support parametric polymorphism to begin with? I'm thinking about Java, which hacked generics after the fact, and maintained backward compatibility through type erasure.
In C++, there are few type erasures. Generally, when we want a function to receive a closure, we write it like this (minus dark corner I have missed):
std::function looks like a regular template class (like any container). And It doesn't look like we should subclass it. So, unless I'm missing something, there is no vtable.
By the way, why do we need std::function at all? Every time I saw it, it was to work around some dark corner of C++ type system or template dark magic.
Because function pointers in C++ carry no state. Given:
class A { void foo(); };
A a;
There is no such construct that lets you store a call to foo specialized for the instance 'a':
??? thing = a.foo; // doesn't exist
The only option in C++ is to take the address of the member function:
&A::foo
But even this is not sufficient as it is equivalent to:
void (*)(A * __this)
That is, the "this" pointer is not stored. Along comes mem_fun:
struct mem_fun{ A *a; void(A::*ptr)(); };
Now you can store a call to foo for the instance a:
mem_fun fun;
fun.a = &a;
fun.ptr = &A::foo;
Adding an operator() to mem_fun lets you treat a mem_fun instance as a function:
void operator()(){ a->*ptr(); }
...
fun()
Now, lets say you need to store this as a generic "callback". Well, how would you do that? Sure, you can store mem_fun instances but what about just plain function pointers? Now you can't store them. In comes std::function. With a little bit of VERY simple to understand magic, you can do:
Most programming languages implicitly type erase closures, C++ does not. Type erasure can come at the cost of heap allocation and indirect function calls. If you need type erasure in C++ you can put any callable type inside std::function.
With that, I get the power without the rabbit hole.