3 ms·
But the OP title is misleading, this isn't function overloading at all. It's macro overloading. Subtle but important difference. For example, you can't pass
by xamuel 11y ago
But the OP title is misleading, this isn't function overloading at all. It's macro overloading. Subtle but important difference. For example, you can't pass the OP's "add" around as a function pointer. You can't declare its prototype in an outside file (leaving the linker to link it in) without putting its full definition in said outside file. Etc.
It also gives, IMO, better error messages than C++. If we take OP's code and change a and b from int to long, we get:
test.c:20:24: error: controlling expression type 'long' not compatible with any generic association type
printf("%d\n", add(a, b)); // 3
^
test.c:16:28: note: expanded from macro 'add'
#define add(a, b) _Generic(a, int: addi, char*: adds)(a, b)
This is better than the situation in C++ where it would say the function does not exist, and then go through every function with that name (possibly quite a lot) and say why each one doesn't work.
- kazinator 11y agoI want to pass a function as a pointer even though it is overloaded. C++ supports that. For instance if we do: int (*p)(int, int) = add; overload resolution kicks in there. I want detailed error messages about overload resolution. Maybe not all the time: give me the executive summary ("no suitable overload of add was found") and the details if I specify some "-Woverload-detailed" option or whatever. That's a quality of implementation matter; neither ISO C nor ISO C++ spell out what diagnostics should look like and how detailed they should be. Token-level macros are hacky cruft that shouldn't be used as the development bed for new features, period. All this stuff ignores 35 years of progress in the C dialect scene alone, not to mention 60 years of progress in general. Above, add isn't even what is understood in computing as a "first class function". If we make it into some hacky macro, we are taking away even that second class status away from it.
- nothrabannosir 11y ago> I want to pass a function as a pointer even though it is overloaded. C++ supports that. That's his point: C _Generic and C++ are not the same. Which one is better was not his point, just that they're different.
- kazinator 11y ago> Which one is better was not his point, ... The comment is loaded with judgment, and the word "better" actually occurs.
- maxlybbert 11y ago> For example, you can't pass the OP's "add" around as a function pointer. To be fair, in C++ you have to say which overload you want to point a function pointer at. So the fact that you have to specify the real function you want to point at, in C, doesn't seem unreasonable.
- kazinator 11y agoNo, you don't have to say that in C++ and there isn't any way to, in fact. // Well, you do say it ... with type! int (*ptr)(int, int) = add; This will take the address of add(int, int) and not some other add like add(double, double). There is no explicit way to refer to one or the other. You're right in that we can't point to the collection of add functions as such. There is no generic function add of which the overloads are its implementations, such that we can have a pointer to this generic add aggregate and have ptr(a, b) be subject to overload resolution. I think that if you want to be able to construct an object ptr out of a set of plain functions, such that ptr(a, b) chooses among those functions based on arguments, that may be doable. Just not with a plain function pointer.
- maxlybbert 11y agoYou seem to have changed your mind while writing your response, and you appear to have come to the same conclusion I did. Personally, when I learned about _Generic, I wondered why the C committee hadn't just adopted C++-style overloading. I'm convinced the reason is to avoid name mangling. The end result is that the programmer does the name mangling "by hand," with natural consequences for doing so. However, there are some important points, which make _Generic both more flexible than C++ overloading in some ways, and less flexible in other ways: * _Generic only dispatches based on a single parameter. * _Generic doesn't have to expand to a function call; it could expand to a format string, for instance (although if a macro, FOO, expands to a string literal you can't put FOO(X) next to another string literal and expect the compiler to join the two strings). * _Generic doesn't consider type conversions when doing the dispatching. * _Generic dispatching can be as complex as the programmer wishes (e.g., _Generic(X), switch (sizeof(X)) ...); yes, you start with type, but you don't have to limit yourself to exact matches. * _Generic can have a "default:" branch; this is possible in C++ but not obvious (have one overload take "..." to guarantee it will be the worst match, and only chosen when there is no other option).