4 ms·
That is creating an extra container object which needs to be allocated separately. Rust doesn't require that. I'm sure that you can construct the container obj
by jeffdavis 8y ago
That is creating an extra container object which needs to be allocated separately. Rust doesn't require that.
I'm sure that you can construct the container object at each call site so that it behaves like rust (and it would just be allocated on the stack), but it would get a little ugly. In rust, it's very seamless and "rust-like" even though you are operating on a native C struct.
Similarly, the Option type magic is pretty darn useful. I'm sure you could do that in C++ as well, but again it would end up ugly because you'd need to invent a new COption type or something.
- kazinator 8y ago> Rust doesn't require that. In the general case, the hardware requires it; that's how computers work. The C library you're working with that has handed you the structure has not reserved any space for you to put anything into the C structure for it to have embellishments like type-dispatched virtual functions. The memory addresses immediately above and below the structure are off-limits (if they exist at all). If you control the allocation and disposal of the memory, then you can easily embed the structure in a larger object. When calling the C code, it just gets a pointer to that part of the object that is the C structure it knows about. Amazingly enough, C++ can do this quite easily. It can be done not just via composition, but also by inheritance: class derived : public some_c_struct { public: virtual method(); }; Then it's just: extern "C" void c_function(some_c_struct *); { derived d; // define easily "on the stack" c_function(&d); // pointer upcasts to base implicitly } All you see here is 1995 draft vintage C++. The idea that C++ doesn't work smoothly with C structures is bizarre; C++ contains a dialect of C which is highly compatible with C90 (to the extent that large programs can be developed such that they compile as C or C++).
- jeffdavis 8y ago> In the general case, the hardware requires it; that's how computers work. In rust, it creates the trait object right as it's calling a function that requires one. But "creates" is too strong a word -- it's just sending the virtual function table pointer along with the structure pointer and any other arguments, probably in registers. Only if you are actually storing the trait object somewhere does it matter much. In your example, you have to construct the "derived" type each time you come from C to C++. Using an extra type is annoying. I think a better C++ example would be trying to do something closer to what rust does and use the C struct pointer most places, and construct the C++ object right before you need it. Would work, but doesn't come out quite as clean as it does in rust. In any case, the main point is that the normal mode of C++ is to add vtable pointers to the structures to achieve dynamic dispatch, and adding stuff to the beginning of a struct does not interoperate with C code very well. The normal mode of rust is to not touch the struct layout and achieve these features through other means. I'm sure there's a way to work against the grain in C++ and make it work, but if you ask me, rust gets it a little cleaner in this particular case. Or you could argue that working against the grain is the normal mode of C++ ;-).