logoalt Hacker News

o11cyesterday at 6:17 PM5 repliesview on HN

For your level 2 code, `uint64_t data[];` is wrong for types whose alignment is greater than `uint64_t`, and also wasteful for types whose alignment is smaller (for example, under an ilp32 ABI on 64-bit architectures).

For your level 3 code, it should be `int main() { List(Foo) foo_list = {NULL};`

Note that working around a lack of `typeof` means you can't return anything. Also, your particular workaround allows `const`ness errors since `==` is symmetrical.

You can't safely omit `payload` since you need it to know the correct size. Consider a `List(int64_t)` and you try to add an `int32_t` to it - this should be fine, but you can't `sizeof` the `int32_t`. Your code is actually lacking quite a bit to make this work.

=====

There are 2 major limitations to generics in C right now:

* Delegating to a vtable (internal or external) is limited in functionality, since structs cannot contain macros, only functions.

* Delegating to an external vtable (mandatory to avoid overhead) means that you have to forward-declare all of the types you'll ever use a vtable with. So far the best approach I've found is to declare (but not define) static functions in the same forwarding header I declare the typedefs in; note that GCC and Clang differ in what phase the "undefined static" warning appears in for the case where you don't actually include that particular type's header in a given TU.

(think about writing a function that accepts either `struct SizedBuffer {void *p; size_t len;};` or `struct BoundedBuffer {void *begin; void *end;};`, and also const versions thereof - all from different headers).


Replies

rectangyesterday at 8:06 PM

> Delegating to an external vtable (mandatory to avoid overhead) means that you have to forward-declare all of the types you'll ever use a vtable with.

We went down the rabbit hole of writing a compiler for this as part of a project I used to work on (Apache Clownfish[1], a subproject of the retired Apache Lucy project). We started off parsing .h files, but eventually it made sense to create our own small header language (.cfh "Clownfish Header" files).

Here's some generated code for invoking the CharBuf version of the "Clone" method defined in parent class "Obj":

    typedef cfish_CharBuf*
    (*CFISH_CharBuf_Clone_t)(cfish_CharBuf* self);

    extern uint32_t CFISH_CharBuf_Clone_OFFSET;

    static inline cfish_CharBuf*
    CFISH_CharBuf_Clone(cfish_CharBuf* self) {
        const CFISH_CharBuf_Clone_t method
            = (CFISH_CharBuf_Clone_t)cfish_obj_method(
                self,
                CFISH_CharBuf_Clone_OFFSET
            );
        return method(self);
    }
Usage:

    cfish_CharBuf *charbuf = cfish_CharBuf_new();
    cfish_CharBuf *clone = CFISH_CharBuf_Clone(charbuf);
We had our reasons for going to these extremes: the point of Clownfish was to provide a least-common-denominator object model for bindings to multiple dynamic languages (similar problem domain to SWIG), and the .cfh files also were used to derive types for the binding languages. But there was truly an absurd amount of boilerplate being generated to get around the issue you identify.

This is why almost everybody just uses casts to void* for the invocant, skipping type safety.

[1] https://github.com/apache/lucy-clownfish

show 1 reply
kccqzyyesterday at 8:24 PM

> it should be `int main() { List(Foo) foo_list = {NULL};`

In C `int main()` means the function takes an unknown number of arguments. You need `int main(void)` to mean it doesn't take any arguments. This is a fact frequently forgotten by those who write C++.

show 2 replies
EPWN3Dyesterday at 6:49 PM

I would love for `union`s to be federated, that is, a type could declare itself as thought it was part of a union with another type, without having to pre-declare all possible types in one place.

show 2 replies
n_plus_1_accyesterday at 8:20 PM

This is also problematic, because there might be padding and the calculated size might be too small:

`malloc(sizeof(*node) + data_size);`

show 1 reply