Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function overloading in C using GCC - compiler warnings

I am attempting to implement function overloading in C, and I am very close. I am using C99 so the _Generic keyword introduced in C11 is not available to me. I have developed some working code, but when I compile it I get a couple warnings.

Working example:

#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;
}

Compiling creates the following 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

For a little more debugging information, here is what the main function looks like after the preprocessor does its work:

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

 __builtin_choose_expr(__builtin_types_compatible_p(typeof(1), int ), print_int(1) , __builtin_choose_expr(__builtin_types_compatible_p(typeof(1), char[]), print_string(1), (void)0));
 __builtin_choose_expr(__builtin_types_compatible_p(typeof("this"), int ), print_int("this") , __builtin_choose_expr(__builtin_types_compatible_p(typeof("this"), char[]), print_string("this"), (void)0));

 return 0;
}

How can I make the compilation warnings go away and still have working code?

like image 830
tjwrona1992 Avatar asked Apr 08 '16 18:04

tjwrona1992


3 Answers

In theory, this should work:

#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))

It chooses either print_int or print_string, then applies the chosen function to x.

like image 173
user2357112 supports Monica Avatar answered Oct 31 '22 13:10

user2357112 supports Monica


The GNU page on the build-ins says that the branches not chosen may still generate syntax errors, but I fail to see how mismatched types are syntax errors.

Anyway, you can get rid of the warnings when you move the arguments to the function out of the type-dependent choice. So (in pseudocode in order to make it more readable), instead of

choose_type_of(x, int, print_int(x), 
choose_type_of(x, char[], print_string(x), (void) 0))

do

choose_type_of(x, int, print_int, 
choose_type_of(x, char[], print_string, pass))(x)

That's what user2357112 suggested in a comment. I was working on a similar solution, but I had a hard time to get the default part (the pass above) to work. When I use (void), which should then expand to (void)(x), I get an error about an unmatched parenthesis.

The solution below creates a default printing function that doesn't use its arguments. It could probably be a non-existent function, so that there are problems when linking or something else that produces an error.

#include <stdio.h>

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


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

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

void print_any() {
    printf("unknown\n");
}

int main(void)
{
    int n = 9;
    const char str[] = "hello";

    print(n);
    print(str);

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

    print(3.2);

    return 0;
}
like image 35
M Oehm Avatar answered Oct 31 '22 13:10

M Oehm


Here's an example with several methods that achieve function overloading.

One poster mentioned this

The GNU page on the build-ins says that the branches not chosen may still generate syntax errors, but I fail to see how mismatched types are syntax errors.

The syntax errors they're referring to I believe are the compiler warnings you get because after preprocessing the compiler can see that the types of the arguments to some functions, even though they can never be called are wrong. The solution (which I think is quite neat) is to find a way to hide the types from the compiler. The obvious way is varargs, the less obvious way is is the current answer to the OP's question.

Caveats apply ie not all solutions are type safe and this is entirely specific to GNU...

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

#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))

#define print1(x)                                                                          \
  __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), int   ), print_int1(1,x)   , \
  __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), char[]), print_string1(1,x), \
 (void)0))

#define print2(x)                                                                    \
  __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), int   ), printer(1,x), \
  __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), char[]), printer(2,x), \
 (void)0))

#define TYPE_ID(x) __builtin_types_compatible_p(typeof(x), int   ) * 1 \
                +  __builtin_types_compatible_p(typeof(x), char[]) * 2  

#define print3(x) printer(TYPE_ID(x), x)

#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(COND)?1:-1]

#define print4(x) \
    STATIC_ASSERT(TYPE_ID(x), __LINE__); \
    printer(TYPE_ID(x), x)

void printer(int i, ...) {
  va_list v;
  va_start(v, i); 
  switch(i) {
    case 1:{ 
             int arg = va_arg(v, int);
             printf("int: %d\n", arg);
             va_end(v);
             break;
           }
    case 2:{
             char * arg = va_arg(v, char*);
             printf("char*: %s\n", arg);
             va_end(v);
             break;
           }
    default: {
               fprintf(stderr, "Unknown type, abort\n");
               abort();
             }
  }
}

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

void print_string(char* s) {
    printf("char*: %s\n", s);
}
void print_int1(int i, ...) {
  va_list v;
  va_start(v, i);
  int arg = va_arg(v, int);
  printf("int: %d\n", arg);
  va_end(v);
}

void print_string1(int i, ...) {
  va_list v;
  va_start(v, i);
  char * arg = va_arg(v, char*);
  printf("char*: %s\n", arg);
  va_end(v);
}
int main(int argc, char* argv[]) {
  int  var    = 1729;
  double var1 = 1729;
  //Type safe
  //print(var1);//Comple time error
  print(var);
  print("print");

  /* Following are not Type Safe */
  print1(var1);// BAD... Does nothing.
  print1(var);
  print1("print1");

  print2(var1);// BAD... Does nothing.
  print2(var);
  print2("print2");

  //print3(var1);//Evil... Runtime error
  print3(var);
  print3("print3");

  //Type Safe
  //print4(var1);//Comple time error
  print4(var);
  print4("print4");
  return 0;
}

Source is on github...

https://github.com/harryjackson/doc/blob/master/c/overload_c_functions.c

The switch method with multiple arguments can be found here...

http://locklessinc.com/articles/overloading/

The whole

like image 38
Harry Avatar answered Oct 31 '22 12:10

Harry