How to achieve function overloading in C?
Yes!
In the time since this question was asked, standard C (no extensions) has effectively gained support for function overloading (not operators), thanks to the addition of the _Generic
keyword in C11. (supported in GCC since version 4.9)
(Overloading isn't truly "built-in" in the fashion shown in the question, but it's dead easy to implement something that works like that.)
_Generic
is a compile-time operator in the same family as sizeof
and _Alignof
. It is described in standard section 6.5.1.1. It accepts two main parameters: an expression (which will not be evaluated at runtime), and a type/expression association list that looks a bit like a switch
block. _Generic
gets the overall type of the expression and then "switches" on it to select the end result expression in the list for its type:
_Generic(1, float: 2.0,
char *: "2",
int: 2,
default: get_two_object());
The above expression evaluates to 2
- the type of the controlling expression is int
, so it chooses the expression associated with int
as the value. Nothing of this remains at runtime. (The default
clause is optional: if you leave it off and the type doesn't match, it will cause a compilation error.)
The way this is useful for function overloading is that it can be inserted by the C preprocessor and choose a result expression based on the type of the arguments passed to the controlling macro. So (example from the C standard):
#define cbrt(X) _Generic((X), \
long double: cbrtl, \
default: cbrt, \
float: cbrtf \
)(X)
This macro implements an overloaded cbrt
operation, by dispatching on the type of the argument to the macro, choosing an appropriate implementation function, and then passing the original macro argument to that function.
So to implement your original example, we could do this:
foo_int (int a)
foo_char (char b)
foo_float_int (float c , int d)
#define foo(_1, ...) _Generic((_1), \
int: foo_int, \
char: foo_char, \
float: _Generic((FIRST(__VA_ARGS__,)), \
int: foo_float_int))(_1, __VA_ARGS__)
#define FIRST(A, ...) A
In this case we could have used a default:
association for the third case, but that doesn't demonstrate how to extend the principle to multiple arguments. The end result is that you can use foo(...)
in your code without worrying (much[1]) about the type of its arguments.
For more complicated situations, e.g. functions overloading larger numbers of arguments, or varying numbers, you can use utility macros to automatically generate static dispatch structures:
void print_ii(int a, int b) { printf("int, int\n"); }
void print_di(double a, int b) { printf("double, int\n"); }
void print_iii(int a, int b, int c) { printf("int, int, int\n"); }
void print_default(void) { printf("unknown arguments\n"); }
#define print(...) OVERLOAD(print, (__VA_ARGS__), \
(print_ii, (int, int)), \
(print_di, (double, int)), \
(print_iii, (int, int, int)) \
)
#define OVERLOAD_ARG_TYPES (int, double)
#define OVERLOAD_FUNCTIONS (print)
#include "activate-overloads.h"
int main(void) {
print(44, 47); // prints "int, int"
print(4.4, 47); // prints "double, int"
print(1, 2, 3); // prints "int, int, int"
print(""); // prints "unknown arguments"
}
(implementation here) So with some effort, you can reduce the amount of boilerplate to looking pretty much like a language with native support for overloading.
As an aside, it was already possible to overload on the number of arguments (not the type) in C99.
[1] note that the way C evaluates types might trip you up though. This will choose foo_int
if you try to pass it a character literal, for instance, and you need to mess about a bit if you want your overloads to support string literals. Still overall pretty cool though.
As already stated, overloading in the sense that you mean isn't supported by C. A common idiom to solve the problem is making the function accept a tagged union. This is implemented by a struct
parameter, where the struct
itself consists of some sort of type indicator, such as an enum
, and a union
of the different types of values. Example:
#include <stdio.h>
typedef enum {
T_INT,
T_FLOAT,
T_CHAR,
} my_type;
typedef struct {
my_type type;
union {
int a;
float b;
char c;
} my_union;
} my_struct;
void set_overload (my_struct *whatever)
{
switch (whatever->type)
{
case T_INT:
whatever->my_union.a = 1;
break;
case T_FLOAT:
whatever->my_union.b = 2.0;
break;
case T_CHAR:
whatever->my_union.c = '3';
}
}
void printf_overload (my_struct *whatever) {
switch (whatever->type)
{
case T_INT:
printf("%d\n", whatever->my_union.a);
break;
case T_FLOAT:
printf("%f\n", whatever->my_union.b);
break;
case T_CHAR:
printf("%c\n", whatever->my_union.c);
break;
}
}
int main (int argc, char* argv[])
{
my_struct s;
s.type=T_INT;
set_overload(&s);
printf_overload(&s);
s.type=T_FLOAT;
set_overload(&s);
printf_overload(&s);
s.type=T_CHAR;
set_overload(&s);
printf_overload(&s);
}
There are few possibilities:
- printf style functions (type as an argument)
- opengl style functions (type in function name)
- c subset of c++ (if You can use a c++ compiler)
Here is the clearest and most concise example I've found demonstrating function overloading in C:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int addi(int a, int b) {
return a + b;
}
char *adds(char *a, char *b) {
char *res = malloc(strlen(a) + strlen(b) + 1);
strcpy(res, a);
strcat(res, b);
return res;
}
#define add(a, b) _Generic(a, int: addi, char*: adds)(a, b)
int main(void) {
int a = 1, b = 2;
printf("%d\n", add(a, b)); // 3
char *c = "hello ", *d = "world";
printf("%s\n", add(c, d)); // hello world
return 0;
}
https://gist.github.com/barosl/e0af4a92b2b8cabd05a7