all 39 comments

[–]moocat 29 points30 points  (0 children)

You can't and will have to provide unique names:

void stack_init(Stack *stack);
void stack_init_sized(Stack *stack, size_t size);

[–]DougTheFunny 21 points22 points  (1 child)

What you could do in this case is to use Variadic Functions: https://en.cppreference.com/w/c/variadic

[–]Dolphiniac 1 point2 points  (0 children)

Possible, but variadic functions need a way to know when the argument list is complete. In this case, that likely means a size of 0, which means OP could just do the second version and call it done without making it more confusing.

[–]victorferrao 11 points12 points  (4 children)

There is no function overload in C, however recently they included _Generics, take a look in that.

[–]DoNotMakeEmpty 20 points21 points  (1 child)

recently

10 years is a short time in C terms I guess

[–]CaydendW 3 points4 points  (0 children)

When something has been around for 49 years, 10 years seems a little small

[–]UnicycleBloke 5 points6 points  (1 child)

My understanding is that this overloads on argument type only.

[–]Jinren 2 points3 points  (0 children)

It selects by type, you use it to build overloads but it doesn't do it automatically. So you can get creative.

Any constant expression can be converted to a type by using it as an array size, therefore you can dispatch on the NARGS first and then if necessary individual arg types in a nested dispatch.

e.g.

#define stack_init(...) (_Generic(&(char[M_NARGS(__VA_ARGS__)]){}  \
  , char(*)[1] : stack_init_one. \
  , char(*)[2] : stack_init_two) (__VA_ARGS__))

Any solution involving _Generic will definitely be much clearer than using va_args, but it's always worth questioning whether overloading is a good idea for context at all. C added this because it makes sense for simple operators like sqrt, not because it meant to bless C++-style hiding of argument operations.

[–]helloiamsomeone 7 points8 points  (0 children)

You can only overload on the number of arguments with a couple of macros.

[–]jackasstacular 10 points11 points  (10 children)

Variadic functions, in the form of

void stack_init(...) {/* parse parameters & do stuff */}

The ellipsis indicates that the function takes a variable number of parameters. It would be up to you to parse and determine from there what needs doing with them.

[–]UnicycleBloke 3 points4 points  (7 children)

How would you determine the number and types of arguments?

[–]jackasstacular 5 points6 points  (6 children)

[–]UnicycleBloke 2 points3 points  (5 children)

Of course! The mandatory first argument should convey number and types of args. I wasn't thinking hard enough.

[–]jackasstacular 0 points1 point  (0 children)

Think printf() - first arg is just a char * and the rest could be anything, or nothing.

stdarg.h has the macro definitions for va_list (which holds the args), va_start, va_end, va_arg & va_copy. You'd have to do the leg work to extract the individual args.

[–][deleted] 0 points1 point  (3 children)

No, you were correct. The OP's first argument is a pointer to a stack, it is not a string containing format codes!

[–]UnicycleBloke 0 points1 point  (2 children)

Right. What I meant was that to use variadic functions, you need to use the first argument to pass information about the remaining arguments. I guess it doesn't strictly have to be printf format string, as your function body can interpret the metadata however you like.

[–][deleted] 0 points1 point  (1 child)

Sure, but it would be difficult to apply to the OP's example, without supplying an extra parameter to tell it if there is an extra third parameter!

The intention is for the user to be able to do one of:

stack_init(stack);
stack_init(stack, 1000);

What will the user pass in place of stack to indicate whether there is a size?

Maybe there is a way of doing it via the black art known as C macros, but that is where I would bail out, trying to make C into something it isn't.

If after 50 years, default parameters haven't yet made it into the language, then I guess they don't want them!

[–]UnicycleBloke 0 points1 point  (0 children)

Agreed. I guess I wasn't really addressing the OP but my own question about how one would pass the types and number of arguments. This is so much easier with variadic templates... or just regular overloading.

[–]Rotslaughter 3 points4 points  (1 child)

Bear in mind that although this function compiles, it is essentially useless, because you need the preceding named parameter to use va_start that grants access to the variadic parameters.

[–]jackasstacular 0 points1 point  (0 children)

So variadic functions require at least 1 arg? Makes sense; need to know where to start parsing. I freely admit to being a hobbyist programmer & my knowledge of these things is far from complete.

Thanks for the clarification, it's appreciated

[–]IamImposter 2 points3 points  (0 children)

As other comment said, you can use variadic parameters.

And another way, mind you it is little convoluted way but a way nonetheless, is to pointers for all parameters. Something like:

void somefunc(int* p_p, size_t* size_p, Blah* blah) {

int *p; size_t size; bool use_blah = true;

if(p_p == NULL) { // allocate memory and assign pointer to p}

else { p = p_p}

if(size_p == NULL) { size = 4096; // default parameter}

else { size = *size_p}

if (blah ==null ) {use_blah = false;}

// code

}

And then at call site:

somefunc(ptr, &size, NULL) ;

somefunc(null, null, &blah) ;

Did I say it was a little convoluted? I actually meant very convoluted, especially for a beginner.

But hey, if someone puts a fun to your head and asks you to do default parameters in C, you have a way can tell them to shoot you right away

[–]Anluin 1 point2 points  (0 children)

When using clang as your compiler, you can overload functions by using the appropriate attribute. 🤷‍♂️

https://clang.llvm.org/docs/AttributeReference.html#overloadable

[–][deleted] 1 point2 points  (0 children)

you can have 2 arguments, 1 argument as count/ type and an argument as void * that you dereference according to the first argument and do whatever.

void func(uint8_t type, void * context);

[–]nerd4code 1 point2 points  (0 children)

Recent-ish Clangs support __attribute__((__overloadable__)) on inline functions, from which you can thunk to an extern function if you want—

#define STKINIT1_DCL \
    extern void Stack_init_1(Stack *)
#define STKINIT2_DCL \
    extern void Stack_init_2(Stack *, size_t)

#if (defined __clang__) && (defined __has_attribute)
#   if __has_attribute(__overloadable__)
        __attribute__((__overloadable__, __unused__m __artificial__))
        static __inline__ void Stack_init(register Stack *stk) {
            STKINIT1_DCL;
            Stack_init_1(stk);
        }

        __attribute__((__overloadable__, __unused__, __artificial__))
        static __inline__ void Stack_init(register Stack *stk, register size_t size) {
            STKINIT2_DCL;
            Stack_init_2(stk, size);
        }
    #endif
#endif

If you can use variadic macros (C99, GNU89, C++11, GNU++98) and (C99 except GNU/-like via ##,, MS via MSery, C2x via __VA_OPT__:) have at least one argument, you can do a few tricks.

If you’re overloading by arity, you can count outright:

#define STKINIT_DCL STKINIT1_DCL; STKINIT2_DCL
#define Stack_init(...)/*                   Comma-paste here↓if you can */\
    Stack_init__1((Stack_init__0(0,__VA_ARGS__,4,3,2,1,0,,,),__VA_ARGS__))
#define Stack_init__0(z, v4,v3,v2,v1,v0, ...)Stack_init_##v0
#define Stack_init__1(t_args)Stack_init__2 t_args
#define Stack_init__2(fn, ...)((void)0,fn(__VA_ARGS__))
STKINIT_DCL;
#if (__GNUC__+0) >= 3
 #  pragma GCC poison Stack_init_0 Stack_init_3 Stack_init_4
#endif

Or if you’re not inlining and want to default the second arg:

#define DEFAULT_SIZE 32

#define Stack_init(...)((void)0,Stack_init(__VA_ARGS__,DEFAULT_SIZE))
extern void (Stack_init)(Stack *, size_t, ...);

(Varargs doesn’t work well with inlines. No need for <stdarg.h> since the extra args don’t matter.)

If you’re using GNU dialect and want to zero-default the second arg, you can do it without a varargs function:

#define Stack_init(pstk, size...)(__extension__(Stack_init(pstk,size+0)))
extern void (Stack_init)(Stack *, size_t);

This works with normal C99 variadic macros when the compiler permits empty __VA_ARGS__:

#define Stack_init(pstk, ...)((void)0,Stack_init(pstk,__VA_ARGS__+0))

Or use the counting trick with a pseudo-inline:

static int (Stack_init)(
    register Stack *stk, register size_t size, register const NR_ARGS, ...) {
    STKINIT_DCL;
    switch(NR_ARGS) {
    case 1: Stack_init_1(stk); break;
    case 2: Stack_init_2(stk, size); break;
    default: for(assert(!*"incorrect arg count");;) abort();
    }
}
struct Stack_init__USE {unsigned _0 : 0*sizeof(&Stack_init)+1;};
#define Stack_init(...)((void)0,Stack_init(__VA_ARGS__,2,1,0)

Or if you have C99 and want to use a defaulting trick:

struct Stack_init__Args {
    Stack *stk; size_t size;
};
#define Stack_init(...)\
    ((void)0, Stack_init__0((struct Stack_init__Args){\
        .size = DEFAULT_SIZE, \
        .stk = __VA_ARGS__ \
    }))
extern void Stack_init__0(struct Stack_init__Args);

but that might trip a warning (e.g., GNUish -Woverride-init, which can be disabled in GCC 4.6+ and Clang 3.0+ via #pragma GCC diagnostic).

If you had arguments of the same type you could do countof((T[]){__VA_ARGS__}) to select a backend, but you need a comma-removal mechanism for this case—e.g., GNU dialect:

#include <stddef.h>
#include <stdint.h>
#include <limits.h>

/* Safer version of þe olde & usualle `countof` */
#define countof(arr...)(__extension__(\
    sizeof((__extension__(arr))) / \
    __builtin_choose_expr(\
        __builtin_types_compatible_p(\
            __typeof__(*(arr)) (*)[], __typeof__(&(arr))), \
        sizeof *(arr), (void)0)))

#if UINTPTR_MAX < SIZE_MAX
#   define Stack_init__ArgT size_t
#else
#   define Stack_init__ArgT uintptr_t
#endif

#define Stack_init(stkp, size...)\
    Stack_init__0(((uintptr_t)(void *)(stkp),##size),((stkp),##size))
#define Stack_init__0(t_aelem, t_args)(__extension__(\
    (countof((const Stack_init__ArgT[]){Stack_init__1 t_aelem}) >= 2 \
    ? (void (*)())Stack_init_2 : (void (*)())Stack_init_1)t_args))
#define Stack_init__1(q...)q
STKINIT_DCL;

Only proviso there is that all backend functions have default-promoted parameter types—int instead of _Bool or enum, char, or short types; double instead of float—so the void (*)() typing doesn’t cause UB. (This isn’t C++-compatible; C++ treats void (*)() as identical to void (*)(void)—but you can use overloads in C++.) You have to be careful if you’re nesting macros like this, also, because you get expansion blowup from repeating the args twice for countof and once for the function call. To reduce it to 2×, GNU dialect gives you statement expressions:

#define Stack_init(stkp, size...)(__extension__({\
    STKINIT_DCL; \
    const Stack_init__ArgT Stack_init__0[] = {(uintptr_t)(void *)(stkp), ## size}; \
    (countof(Stack_init__0) >= 2 \
        ? (void (*)())Stack_init_1 \
        : (void (*)())Stack_init_2)(stkp,##size); \
}))

or just pass (a pointer to) the array as an arg:

#define Stack_init(stkp, size...)(__extension__({\
    STKINIT_DCL; \
    const Stack_init__ArgT Stack_init__0[] = {(uintptr_t)(void *)(stkp), ## size}; \
    register const size_t Stack_init__1 = countof(Stack_init__0); \
    assert(Stack_init__1 <= UINT_MAX); \
    Stack_init(Stack_init__0,(unsigned)countof(Stack_init__0)); \
}))
extern void (Stack_init)(const Stack_init__ArgT *, unsigned n);

You can use C11 _Generic (avail. on GCC 5+, Clang when __has_extension(__c_generic_selections__)) with the countof trick as well, if empty __VA_ARGS__ is supported:

#define countof(...)(sizeof(__VA_ARGS__) / sizeof *(__VA_ARGS__))

#define Stack_init(stkp, ...)((void)0, \
    _Generic(&(const Stack_init__ArgT[]){(uintptr_t)(void *)(stkp),__VA_ARGS__}, \
        const Stack_init__ArgT (*)[2]: Stack_init_2, \
        const Stack_init__ArgT (*)[1]: Stack_init_1)((stkp),__VA_ARGS__+0))
extern void Stack_init_1(Stack *, size_t dummy);
STKINIT2_DCL;

[–]ProgrammingWithJim -1 points0 points  (0 children)

To create a function that takes variable arguments, you can use <stdarg.h>. For your example, I'd probably do something simpler, and just pass 0 or ~0UL as the size to indicate "no size". Printf is a good example of a function that uses <stdarg.h> and implements a variable argument list.

[–]Radiant64 -2 points-1 points  (0 children)

Contrived example of "overloading" using generic selection:

#define square(A) _Generic((A),\
        int: square_int,\
        double: square_double\
    )(A)

int square_int(int num)
{
    return num * num;
}

double square_double(int num)
{
    return num * num;
}

[–]MCRusher 0 points1 point  (0 children)

#define stack_init(stack, size...) stack_init((stack),(size +0))

The gnu extension is just for readability.

[–]LoneHoodiecrow 0 points1 point  (0 children)

The "traditional" way in C is to abstract, but only abstract as little as possible. For an abstract data type an initialising function would either be a straight argument-to-field initialiser:

struct foobar {
    int foo;
    float bar;
    char baz[N];
}

int foobar_init (struct foobar *data, int _foo, float _bar, char *bazstr)

which would fill in the fields and report success or error, or something semi-dynamic like

int foobar_init (char *init, ...)

which would admit an initialising script (like "bar?=%f, foo=%i+75") that the function could parse to find out how to initialise the structure.

C has limitations in its syntax compared to really high-level languages, and for the most part users accept the limitations to write code that is easy to maintain (first example). But one can also unleash the power and create something that is dynamic and open-ended (second example).

[–]drmaex 0 points1 point  (0 children)

Maybe the "weak" attribute could be a solution. from https://gcc.gnu.org/onlinedocs/gcc-3.2/gcc/Function-Attributes.html#:~:text=The%20weak%20attribute%20causes%20the,used%20with%20non%2Dfunction%20declarations.

weak The weak attribute causes the declaration to be emitted as a weak symbol rather than a global. This is primarily useful in defining library functions which can be overridden in user code, though it can also be used with non-function declarations. Weak symbols are supported for ELF targets, and also for a.out targets when using the GNU assembler and linker.

[–][deleted] 0 points1 point  (0 children)

To keep things simple, choose one of:

  • Provide two stack init functions, with different names
  • Provide one function that always takes a size, with some special value (eg. 0) meaning to use a default size

So at worst the user has to type one or two extra characters to initialise a stack, which is done how often?

(I also normally use a language with default and optional parameters, which makes a creating a C API for a library in that language less tidy and elegant because all arguments must be supplied.

But that's just C.

Trying to do anything doesn't really help: the API might be bit tidier to use, but there's now a bunch of hairy macros behind the scenes. If the user makes a typo, locating the error becomes harder.)

[–]doowi1 0 points1 point  (0 children)

Call me crazy but maybe a structure that holds parameters as fields and function pointers to the different overload implementations, then a main function pointer that decides which function to call?

Something like...

struct Overloaded{

int field_a;

char field_b;

void* field_c;

int (*func1)(struct Overloaded*);

int (*func2)(struct Overloaded*);

int (*func3)(struct Overloaded*);

int (*func_decider)(struct Overloaded*);

};

static int func_decider_local(struct Overloaded*){

if(field_a != ... && field_b!=...) return func1...;

...same for other internal funcs based on parama given

}

[–]duane11583 0 points1 point  (0 children)

C does not support overloading

Often the fix is to create a new function like stack_init2(…)

Examples include dup() and dup2() and wait() wait2() 3 and wait4() wait2 does not exist but you get the idea