Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Instead 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.)


This 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?


You'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.


Actually 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.


Ah 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/ (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. :)




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: