2

I wish to accomplish function overloading in C, but I am attempting to run my code on a Unix server that does not have C11 support therefore the _Generic keyword is not available.

(Upgrading the server so it has a newer version of GCC is not an option).

Are there any alternatives to using _Generic to simulate effective function overloading in C?

6
  • Not really; you'd have to identify which function should be called at each location and call the correct function, instead of letting _Generic do that for you more or less automatically. Why can't you install GCC 5.3.0 (say) in $HOME/gcc/bin and then use that to build the software? GCC is just a program; if you can compile and install the binary that really needs _Generic, you can compile and install a binary that provides support for _Generic — even if no-one else on the machine knows that it is there. Commented Apr 8, 2016 at 16:09
  • The version of GCC we use came with the version of RedHat. The Unix admins are the only ones who can touch it and the only way that will happen is with the next RedHat upgrade which is not anytime soon. Commented Apr 8, 2016 at 16:16
  • 2
    Nonsense. You don't have to use /bin/gcc or /usr/bin/gcc; you can use 'their' version of GCC to build 'your' version of GCC and then use 'your' GCC to build your software. I do this all the time — on RHEL systems with archaic compilers that don't diagnose all the problems that a modern compiler can diagnose. Commented Apr 8, 2016 at 16:22
  • Perhaps the P99_GENERIC macro from Jens Gustedt's P99 might be useful. I haven't used it myself, though, and I don't know how much preprocessing overhead it adds. Commented Apr 8, 2016 at 16:25
  • 1
    @JonathanLeffler, +1, that's certainly possible, but in some environments the powers that be frown on using a "non-approved" compiler for production code, and installing your own compiler and using that would be a bad idea. I have no idea if that's the case here, or if the OP's code is just for personal use, but it's not always a good idea to do it, even thought it's technically possible. Commented Apr 8, 2016 at 16:51

4 Answers 4

4

You can do a limited form of overloading, for some argument types, like so:

void func_int(int);
void func_long(long);
void func_longlong(long long);

#define FUNC(X) \ 
  (sizeof(X) <= sizeof(int) ? func_int(X) \
  : sizeof(X) == sizeof(long) ? func_long(X) \
  : func_longlong(X))

This will allow you to use FUNC(i) and have it call different functions. It's limited, because you can only distinguish types by their size. That means if sizeof(int) == sizeof(long) then you will never call func_long, and if sizeof(long) == sizeof(long long) then you will never call func_longlong. Also, you can't overload for other types, such as double, if sizeof(double) is the same as one of the integer types you're testing for.

It can be used to overload for e.g. float, double or long double, where you might have different implementations of a function that calculate more or less precisely depending on the precision (i.e. number of bits) in the argument type.

Sign up to request clarification or add additional context in comments.

Comments

3

The GCC manual explicitly shows a GNU99 (-std=gnu99) workaround since at least version 3.1.1.

There are limitations, of course: all variants must have the same return type, and all function variants must make syntactic sense. The latter is often the cause of various compile errors (invalid types for function variant parameters). That can be avoided by declaring the functions without parameter prototypes; however, one must then remember that default type promotions will then take place (float are promoted to double, and all integer types smaller than int are promoted to int or unsigned int). Consider this example program:

#define  _GNU_SOURCE /* for asprintf() */
#include <stdlib.h>
#include <stdio.h>

typedef struct {
    double  x;
    double  y;
    double  z;
    double  d;
} plane;

static const char *foo_char_array();
static const char *foo_int();
static const char *foo_long();
static const char *foo_double();
static const char *foo_float();
static const char *foo_short();
static const char *foo_plane();

#define foo(x) \
    ( __builtin_choose_expr( __builtin_types_compatible_p(typeof(x), int),     foo_int(x),        \
      __builtin_choose_expr( __builtin_types_compatible_p(typeof(x), long),    foo_long(x),       \
      __builtin_choose_expr( __builtin_types_compatible_p(typeof(x), short),   foo_short(x),      \
      __builtin_choose_expr( __builtin_types_compatible_p(typeof(x), float),   foo_float(x),      \
      __builtin_choose_expr( __builtin_types_compatible_p(typeof(x), double),  foo_double(x),     \
      __builtin_choose_expr( __builtin_types_compatible_p(typeof(x), plane),   foo_plane(x),      \
      __builtin_choose_expr( __builtin_types_compatible_p(typeof(x), char []), foo_char_array(x), \
      (void)0 ))))))) )


int main(void)
{
    double d = 1.0;
    float  f = 2.0f;
    short  s = 3;
    long   n = 4L;
    plane  p = { 5.0, 6.0, 7.0, 8.0 };

    printf("foo(9) = %s\n", foo(9));
    printf("foo(10L) = %s\n", foo(10L));
    printf("foo(11.0f) = %s\n", foo(11.0f));
    printf("foo(12.0) = %s\n", foo(12.0));
    printf("foo(\"bar\") = %s\n", foo("bar"));
    printf("foo(d) = %s\n", foo(d));
    printf("foo(f) = %s\n", foo(f));
    printf("foo(s) = %s\n", foo(s));
    printf("foo(n) = %s\n", foo(n));
    printf("foo(p) = %s\n", foo(p));
    return EXIT_SUCCESS;
}

static const char *foo_char_array(char x[]) { return "char []"; }
static const char *foo_int(int x) { static char buffer[40]; snprintf(buffer, sizeof buffer, "(int)%d", x); return (const char *)buffer; }
static const char *foo_long(long x) { static char buffer[40]; snprintf(buffer, sizeof buffer, "(long)%ld", x); return (const char *)buffer; }
static const char *foo_float(double x) { static char buffer[40]; snprintf(buffer, sizeof buffer, "%af", x); return (const char *)buffer; }
static const char *foo_double(double x) { static char buffer[40]; snprintf(buffer, sizeof buffer, "%a", x); return (const char *)buffer; }
static const char *foo_short(int x) { static char buffer[40]; snprintf(buffer, sizeof buffer, "(short)%d", x); return (const char *)buffer; }
static const char *foo_plane(plane p) { static char buffer[120]; snprintf(buffer, sizeof buffer, "(plane){ .x=%g, .y=%g, .z=%g, .d=%g }", p.x, p.y, p.z, p.d); return (const char *)buffer; }

You do not need to determine the type based on a single parameter; you can do e.g. __builtin_types_compatible_p(typeof(x), double) && __builtin_types_compatible_p(typeof(y), double) to verify both x and y are of type double.

When compiled and run, the above program will output

foo(9) = (int)9
foo(10L) = (long)10
foo(11.0f) = 0x1.6p+3f
foo(12.0) = 0x1.8p+3
foo("bar") = char []
foo(d) = 0x1p+0
foo(f) = 0x1p+1f
foo(s) = (short)3
foo(n) = (long)4
foo(p) = (plane){ .x=5, .y=6, .z=7, .d=8 }

tested on 32-bit x86 Linux (ILP32), as well as on x86-64 (LP64). And yes, the above program will leak memory, since it never free()s the dynamically allocated strings returned by the foo_..() function variants.

Comments

0

I found a method that appears to work, however I still get a couple warnings at compile time...

Working code:

#include <stdio.h>

#define print(x)                                                                        \
__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), int   ), print_int(x)   , \
__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), char[]), print_string(x), \
(void)0))

void print_int(int i) {
    printf("int: %d\n", i);
}

void print_string(char* s) {
    printf("char*: %s\n", s);
}

int main(int argc, char* argv[]) {

    print(1);
    print("this");

    return 0;
}

output:

int: 1
char*: thing

Compiler warnings:

gcc overload.c -o main
overload.c: In function 'main':
overload.c:19: warning: passing argument 1 of 'print_string' makes pointer from integer without a cast
overload.c:20: warning: passing argument 1 of 'print_int' makes integer from pointer without a cast

3 Comments

You gotta treat it like _Generic. Move (x) outside and the warnings will go away.
What do you mean by move (x) outside?
#define print(x) \ __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), int ), print_int , \ __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), char[]), print_string, \ (void)0))(x)
0

It is somehow possible using function pointers, and nameless struct inside a union. Here comes an example in which we overload the add and mul functions. There are two unions LIBI, and LIBF containing nameless structures. LIBI contains the function pointers add and mulc which only use integer values. LIBF is the same as LIBI except that add and mul use float variables. In addition, we need to create addi, muli, addf, and mulf functions outside of these unions. Functions pointers in unions will be referred to these 4 functions. For example, add in LIBI is referred to addi because addi uses int values and add in LIBF is referred to addf as it uses only float variables. This example can also be used as a way of emulating namespace in C which is absent in the language. Unions act like the namespace in this example.

#include<stdio.h>
#include<stdlib.h>

union {
  struct {
    void (*add)(int *, int);
    void (*mul)(int *, int);
   };
   }LIBI;

union {
   struct {
   void (*add)(float *, float);
   void (*mul)(float *, float);
   };
 }LIBF;

void addi(int *a, int c){
  *a += c; 
 }

void addf(float *a, float c){
  *a += c;  
  }
void muli(int *a, int c){
  *a *= c; 
  }

void mulf(float *a, float c){
   *a *= c; 
  }

 int main(void){

 LIBI.add = addi;
 LIBF.add = addf;
 LIBI.mul = muli;
 LIBF.mul = mulf;

 int ia = 10;
 int ib = 2;
 float fa = 20.0f;
 float fb = 2.0f;

 LIBI.add(&ia,ib);
 LIBF.add(&fa,fb);

 printf("%d\n",ia);
 printf("%f\n",fa);

 LIBI.mul(&ia,ib);
 LIBF.mul(&fa,fb);

 printf("%d\n",ia);
 printf("%f\n",fa);

 return 0; 
 }

1 Comment

Code only answers arent encouraged as they dont provide much information for future readers please provide some explanation to what you have written

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.