4 ms·
The lack of generics means your array implementation is either going to either: - be implemented with macros and token pasting, and result in a ton of mental o
by dbaupp 10y ago
The lack of generics means your array implementation is either going to either:
- be implemented with macros and token pasting, and result in a ton of mental overhead because you'll have a pile of types like array_foo for an array of `foo`s, and array_bar for an array of `bar`s, along with a pile of corresponding `foo * array_foo_get(array_foo, size_t)` and `bar * array_bar_get(array_bar, size_t)` functions.
- or, have a runtime cost and lose type safety by storing void* and casting when accessing.
The first case is even worse than it sounds: e.g. I don't know how you handle arrays of types with spaces in them (like `unsigned char`, or `struct bar`) with a macro. And, we haven't even thought about const correctness yet, which would probably require having const_array_foo, const_array_bar (etc.) types defined too.
(And, of course, these only solve one facet of the problems with C's pointers: there's no way to defend against use-after-free or dangling pointers.)
- nwmcsween 10y agoYou're missing main glaring issue with parametric polymorphism, bloat.
- dbaupp 10y agoWhile "unnecessary"/extra generated code is a trade-off one has to consider when choosing to use the specialize/monomorphise-everything implementation of parametric polymorphism (it isn't the only one), it isn't a problem in this case: the types themselves are a compile-time abstraction and don't exist at runtime, and the functions are all tiny (a branch, a memory access and a function call/abort). Additionally, all the functions should be inlined anyway because the function call overhead will likely be as much or more than the actual code, and, more importantly, inlining enables other optimisations (removing the branch, vectorising the memory access, etc.). Once inlined, the code will be the same as the manual/macro-based approach of writing `if` statements around each array[index] access.
- dreta 10y agoNo, you just allocate enough space to store an extra int at the start for the length, and return a typed pointer to the actual data. Then you need an accessor that checks bounds, if you want safe access. Both of these problems are solved by simple macros.
- AReallyGoodName 10y agoArrays and pointers in C already have that int. That's why sizeof() works. The issue is an extra if statement on every single array and pointer access.
- dbaupp 10y agoThey don't, sizeof is a compile-time constant. On a pointer, sizeof() just reports the size of the pointer itself (i.e. 4 or 8 bytes on most modern platforms), not the size of the data to which it points (and sizeof(*pointer) reports the size of the type to which pointer points, it doesn't know anything about how many values of that type are stored). For an array, the length is known statically (i.e. it's in the type), and so the computation can be done at compile time.
- steveklabnik 10y agoAnd this is such a misconception that it's often cited as a footgun: http://www.cplusplus.com/faq/sequences/arrays/sizeof-array/ http://www.cplusplus.com/faq/sequences/arrays/sizeof-array/ > A beginner will often try something along the lines of size = sizeof( myarray ) (which is incorrect).
- caf 10y agosizeof isn't always a compile-time constant: if it's applied to variably-modified type, it's not (obviously).
- dbaupp 10y agoOh, yes, I'd forgotten about VLAs... Compile-time constant, except for sometimes.
- dbaupp 10y agoSo you want the array to have type foo * ? Ignoring that this doesn't let the compiler help the programmer with arrays (you still have to manually remember to use the accessor, not []), you also have to manually remember which pointers are pointers and which are arrays, and this representation doesn't work for pointing into subsections of an array (a similar problem to C-style strings), nor does it work well for putting arrays on the stack, which means one is forced to allocate every array (both of which mean the safe C is likely slower than the equivalent in Rust or even C++).
- lacampbell 10y agoC macros-faking-generics really aren't that bad (your "unsigned char" case is really easy to solve - use another macro). It's a bit goofy having different types floating around like "array_of_int", "array_of_float", but you can create them in a single line when needed, and once you create them, they work, and efficiently. They're not an ideal solution by any stretch, but it's not the nightmare scenario you envision wrt generic data structures in C.
- dbaupp 10y agoI would be interested in the specifics of the spaces thing (knowing more about ones tools is good) but "10 minutes" to handle all the various issues and bugs that will inevitably pop up from wrangling macros?
- lacampbell 10y agoYeah 10 minutes is quite a bit off, at least for me. The various issues and bugs that arose all came about when developing the data structures themselves - they didn't crop up in actual usage. Though my approach was to use a separate header and source file for each new data type I parameterised them by. So I had an "int_array.c" and an "int_array.h". And inside the header would just be function declarations generated by the macro. I basically didn't like the idea of dumping in a whole implementation of a data structure and all its operations with a macro every single time I wanted to use it. YMMV but I found it worked well.
- dbaupp 10y agoThat approach of a single invocation was what I would personally assume would be done (or put it in the header that defines the data type, for a custom one), but I'm not sure how that solves spaces?
- civility 10y agoNo more spaces: typedef unsigned char uchar; DECLARE_AND_IMPLEMENT_ARRAY_API(uchar)