6 ms·
I think the main drawback for every C implementation, including this one, is that you still have to write a new version of every algorithm for every type that y
by tines 10y ago
I think the main drawback for every C implementation, including this one, is that you still have to write a new version of every algorithm for every type that you want to use your array struct with, unless you put all your functions in macros too. And even then, they'll only work with pointers, not with other data structures like the C++ algorithms will, unless you start doing dynamic dispatch which is what we want to avoid.
I think C++ solves this in a neater way (not saying it's good, just better) with templates, the iterator idea, and the algorithms library because you only write things once and the code is only generated for each type (not each use of the function like it would with macros).
- 1wd 10y agoInstead of putting it all in macros you could write the algorithms once in a "template include file" array_template_impl.h: #define CONCAT2(a, b) a##b #define CONCAT(a, b) CONCAT2(a, b) #define ARRAY CONCAT(array_,ARRAY_ELEMENT_TYPE) #define ARRAY_FUNC(name) CONCAT(CONCAT(array_,ARRAY_ELEMENT_TYPE), CONCAT(_,name)) typedef struct { size_t size; size_t capacity; ARRAY_ELEMENT_TYPE *data; } ARRAY; ARRAY *ARRAY_FUNC(create)(size_t size) { ARRAY *p = malloc(sizeof(ARRAY)); p->size = size; p->capacity = size; if(size) { p->data = malloc(size * sizeof(ARRAY_ELEMENT_TYPE)); } else { p->data = NULL; } return p; } //... #undef ARRAY #undef ARRAY_FUNC #undef ARRAY_ELEMENT_TYPE Then you can use them with the #include code generation trick like this: #include <stdlib.h> #define ARRAY_ELEMENT_TYPE int #include "array_template_impl.h" #define ARRAY_ELEMENT_TYPE float #include "array_template_impl.h" int main() { array_int *ai = array_int_create(0); array_float *af = array_float_create(0); for(int i = 0; i < 10; ++i) { array_int_push_back(ai, i); array_float_push_back(af, i * 0.3f); } array_int_free(ai); array_float_free(af); return 0; } (In practice you might want even more indirection files: Put #define and #include in array_int.c. and array_float.c, and create also array_int.h, array_float.h and array_template_decl.h with just the usual declarations.)
- deleted 10y ago[deleted]
- tines 10y agoThis is a cool technique, but I tried compiling the first block of code you posted (with ARRAY_ELEMENT_TYPE being defined to be int* ) and got "error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘*’ token" from gcc 6.2 using the flags that to3m specified in one of the parent posts posts. Is there a special trick I can use to get that to work?
- 1wd 10y agoYou'd need a second #define, something like: #define ARRAY_ELEMENT_NAME int_ptr #define ARRAY_ELEMENT_TYPE int* #include "array_template_impl.h" And then use ARRAY_ELEMENT_NAME only in the definitions of ARRAY and ARRAY_FUNC, and use ARRAY_ELEMENT_TYPE instead everywhere else.
- tines 10y agoActually now that I think about it, it might be better to just make a typedef and then you can go back to using the same name everywhere. I think that would solve the problem. This technique seems to take care of the bulk of the use of templates in C++, i.e. simple data structure or function definitions. But it's not a full replacement, because I don't think you can use this to do things like pass an integer to one of these templates, then have that template use another template and pass a calculation based on that number to the other template like you could do in C++. Something like this: template<int X> struct foo_t { /* ... */ }; template<int Y> struct bar_t { foo_t<Y / 2> foo; }; so that bar_t<50> and bar_t<24> are different types which contain a foo_t<25> and foo_t<12> respectively. But it's cool nevertheless.
- 1wd 10y agoAh yes, using a typedef would work too, nice. It for sure not quite the same as C++ templates, but if you can tolerate crazy things you can do a lot with the preprocessor. See http://www.boost.org/libs/preprocessor/ http://www.boost.org/libs/preprocessor/ (supports both C++ and C). If you have foo_template.h: #define FOO_T CONCAT(CONCAT(foo_,X),_t) struct FOO_T { /* ... */ }; and bar_template.h: #define X Y/2 #include "foo_template.h" #define BAT_T CONCAT(CONCAT(bar_,Y),_t) struct BAR_T { FOO_T foo; }; It might almost work, but you'd need to pull out some more tricks I'm sure. Maybe BOOST_PP_DIV(Y,2) would help. In practice I'd prefer something sane. :)