Open emersion opened 1 month ago
This would probably by an improvement. It could even be done without breaking ABI.
Another approach that I've considered is to approximate type traits via struct members. The interface would still be partially opaque, but the first members for the structs would be exposed. For example:
struct aml_handler {
struct aml_obj base;
struct aml_trait_startable startable;
struct aml_trait_pollable pollable;
};
int aml_get_fd(struct aml_pollable* obj);
Then you could do...
struct aml_handler handler;
struct aml aml;
...
int handler_fd = aml_get_fd(&handler->pollable);
int aml_fd = aml_get_fd(&aml->pollable);
aml_start(aml_get_default(), &handler->startable);
At least, this would be an interesting experiment in interface design.
It could even be done without breaking ABI.
Technically speaking using _Generic
would be an API break, for instance this would fail compilation after the change:
struct aml_handler *handler = ...;
void *obj = handler;
return aml_get_fd(obj);
Though probably nobody does this in practice.
approximate type traits via struct members
This would be better. I can see these potential downsides:
struct aml_handler
struct definition needs to be public. If you want private fields, you need to re-define a private struct wrapping the public one, and cast.struct aml_obj base
from struct aml_trait_startable
or struct aml_trait_pollable
. Maybe that'd be fine for simple getters, but probably wouldn't work for more involved functions. Could be worked around by storing a pointer to the struct aml_obj base
inside the "trait" structs perhaps.Technically speaking using _Generic would be an API break, for instance this would fail compilation after the change
I don't mind breaking API as much as ABI.
That being said, I've been wanting to redesign the interface for a while anyway based on some observations that I've made:
If/when I do redesign it, I think I'll just do the right thing and duplicate those functions across different types. I did it this way originally because of laziness and a sudden urge to question conventional wisdom.
I agree with all of the points above.
I haven't removed the superfluous things from the API, but at least type safety is addressed: #14
Very nice, thanks for that!
The public API allows the same functions to be used on different kinds of objects. This makes it pretty error-prone: it's easy to call a function on an invalid type without any compile-time error (either an aml type not accepted by the function in which case it's a runtime error, or a completely foreign type in which case it's just fireworks). C11
_Generic
could be used to indicate which types are accepted by a function, e.g:(Note that in general I am not a fan of functions accepting multiple types of arguments, and would just recommend duplicating the functions into multiple wrappers.)