1

I'm trying to achieve method overloading in C, preferably using macros so the header/library can handle the definitions and declarations instead of putting that onto the user. Now I've read this answer about using _Generic however the problem is my functions have struct types. So there's no way to evaluate their type using a __typeof__ of _Generic. I wonder if there is any way at all.

This is how my header file looks like (which is all we should be concerned with)-

#pragma once

// Macros for generic like use of the data structure

// Macros for the stack type itself
/*
 * Define stack with corresponding type
 * Converts Stack(int) to intstack_t
*/
#define Stack(type) type stack_t

// Macros for functions
/*
 * Define createStack function
 * Converts createStack(int, 5) to create_intstack(5)
*/
#define createStack(type, capacity) create_ ##type ##stack(capacity)

/*
 * Define destroyStack function
 * Converts destroyStack(someIntStack) to destroy_intstack(someIntStack)
 * Where someIntStack is a pointer to a variable of type intstack_t
*/
#define destroyStack(stack) _Generic(stack, intstack_t*: destroy_intstack, charstack_t*: destroy_charstack)(stack)

/*
 * Define push function
 * Converts push(someIntStack, data) to intstack_push(someIntStack, data)
 * Where someIntStack is a pointer to a variable of type intstack_t
*/
#define push(stack, data) _Generic(stack, intstack_t*: intstack_push, charstack_t*: charstack_push)(stack, data)

// Stack structure definition(s)

// int stack definition
typedef struct IntegerStack {
    int top;
    unsigned capacity;
    int* arr;
}intstack_t;

// char stack definition
typedef struct CharacterStack {
    int top;
    unsigned capacity;
    char* arr;
}charstack_t;

//Stack functions

// int stack functions
intstack_t* create_intstack(int);
void destroy_intstack(intstack_t*);
void intstack_push(intstack_t*, int);

// char stack functions
charstack_t* create_charstack(int);
void destroy_charstack(charstack_t*);
void charstack_push(charstack_t*, char);

Majority of the function declarations (and indirectly, their respective macros) have been removed, as they all essentially function the same. I'm only concerned about the push function macro as provided. The other macros are really there to show what kind of usecase I intend. Obviously the macro used in push using _Generic will not work as intstack_t or charstack_t are not primitive types.

The goal is for the user to be able to use push(stack, data) where the stack can be a variable of either type intstack_t* or charstack_t* and the statement push(stack, data) will be transformed into intstack_push(stack, data) or charstack_push(stack, data) respectively.

Chase
  • 5,315
  • 2
  • 15
  • 41
  • It may be possible, but it'll probably make your code hard to read and maintain. – Fiddling Bits Apr 14 '20 at 14:32
  • Instead of using a type, use `void *` and store a pointer to an opaque type. – Paul Ogilvie Apr 14 '20 at 14:41
  • I really don't want to use void pointers as it might make the code type unsafe and open up serious vulnerabilities. I'm fine with the code being difficult to read/maintain but I do not want it to be unsafe unless explicitly needed – Chase Apr 14 '20 at 14:43
  • You've left out the control expression in the `_Generic` invocation. I assume you meant it to be`(stack)`. But why does it matter that the types are not primitive? – rici Apr 14 '20 at 19:49
  • @rici my bad, I did not notice I had the param missing. But it won't work for "intstack_t" or "charstack_t" types because _Generic does not allow those, it only allows primitive types like `int`, `long` etc – Chase Apr 15 '20 at 05:54
  • @Chase: Says whom? https://gcc.godbolt.org/z/tujqfS. (From the C standard, 6.5.2p2: "The type name in a generic association shall specify a complete object type other than a variably modified type." Nothing about primitive types there.) – rici Apr 15 '20 at 06:00
  • Here's a more fun example: http://coliru.stacked-crooked.com/a/bffb6a1ffbd8cfcc – rici Apr 15 '20 at 06:12
  • `clang` seems to throw errors at me that the types are not legal. I don't think it's possible for `_Generic` to know the type of a struct. Even if it is, it sure as hell isn't working for me – Chase Apr 15 '20 at 06:14
  • What clang version? Godbolt is using clang-9 there, and it seems perfectly happy. In fact, I worked backwards through the clang version list back to clang-3, and it continued to be happy. Maybe you've got some error in your invocation. – rici Apr 15 '20 at 06:17
  • As that quote from the C standard indicates, you can't use an incomplete type. Which is not surprising, because you can't write a value whose type is incomplete. But you can use a pointer to an incomplete type, since you're allowed to use a value which is a pointer to an incomplete type. – rici Apr 15 '20 at 06:29

1 Answers1

2

_Generic will work with any complete type other than a Variable Length Array. And although you cannot associate incomplete types with values in an _Generic invocation, there is no problem with pointers to incomplete types.

So your push macro will work just fine, even if used in a context where instack_t and charstack_t are opaque.

Sample on coliru: http://coliru.stacked-crooked.com/a/7d9b181af2429c5e

rici
  • 234,347
  • 28
  • 237
  • 341
  • strange, it seems only visual studio clang tools throws errors, when I use clang directly from the commandline it seems to work. Thanks! – Chase Apr 15 '20 at 07:14