Prevent incompatible functions in C?

It is sometimes useful to disable function callbacks.

For example, we may have a function to duplicate some data:
struct MyStruct *my_dupe_fn(const struct MyStruct *s)

But pass it as a generic callback:
typedef void *(*MyGenericCopyCallback)(void *key);

For instance: ensure_key_in_set(my_set, my_key, (MyGenericCopyCallback)my_dupe_fn);

Since the difference between const struct MyStruct *and void *will not cause problems in this case, it will not cause any errors (at least, it is called in the function itself).

However, if later arguments are added to my_dupe_fn, this may result in an error that will not generate a compiler warning.


Is there a way to drop the function, but still show warnings if the arguments or return values ​​are of different sizes?



: , C * *, - .

+4
4

: " ", undefined .

undefined, . UB, . , undefined. , .

:

typedef void *MyCallback(void *key);    // style: avoid pointer typedefs

struct MyStruct *my_dupe_fn(const struct MyStruct *s)
{ ... }

void *my_dupe_fn_callback(void *s)
{
     return my_dupe_fn(s);
}

void generic_algorithm(MyCallback *callback)
{
    // ....
    ensure_key_in_set(my_set, my_key, callback); 
    // ....
}

// elsewhere
generic_algorithm(my_dupe_fn_callback);  

. , - , , .

+3

gcc , plan9-extensions. ( C99) .. .

, , gcc, MS- (?) . .

+2

, , - , my_dupe_fn, , .

, DRY ( ). , - , .

my_dupe_fn, , , , typedef .

, , void.

+1

, , , , C. , , , , , . . , , .

, , , . :

typedef void* GenericFunction(int argc, void** args);

, , , , :

void* MyCallback(int argc, void** args)
{
    assert(argc == 2);
    ...
    return 0;
}

, , , - :

struct Variant
{
    void* ptr;
    const char* type_name;
};

struct Variant to_variant(void* ptr, const char* type_name)
{
    struct Variant new_var;
    new_var.ptr = ptr;
    new_var.type_name = type_name;
    return new_var;
}

void* from_variant(struct Variant* var, const char* type_name)
{
     assert(strcmp(var->type_name, type_name) == 0 && "Type mismatch!");
     return var->ptr;
}

void* pop_variant(struct Variant** args, const char* type_name)
{
     struct Variant* var = *args;
     assert(var->ptr && "Trying to pop off the end of the argument stack!");
     assert(strcmp(var->type_name, type_name) == 0 && "Type mismatch!");
     ++*args;
     return var->ptr;
}

:

#define TO_VARIANT(val, type) to_variant(&val, #type);
#define FROM_VARIANT(var, type) *(type*)from_variant(&var, #type);
#define POP_VARIANT(args, type) *(type*)pop_variant(&args, #type);

typedef struct Variant* GenericFunction(struct Variant* args);

:

struct Variant* MyCallback(struct Variant* args)
{
    // `args` is null-terminated.
    int arg1 = POP_VARIANT(args, int);
    float arg2 = POP_VARIANT(args, float);
    ...
    return 0;
}

- , , MyCallback type_name.

Such a function can be useful if your codebase supports callbacks to dynamically typed scripting languages, since scripting languages ​​should not make cast types in their code (as a rule, they should be a little more secure). Type names can then be used to automatically convert arguments to native scripting language types dynamically using these fields type_name.

0
source

All Articles