2

Is it possible in C(not C++) to have a fuction pointer that takes a generic value(not a pointer), with -pedantic and -wall -werror flags set.

Note: I can't change the parameter Type. The code has to support uint8_t, uint16_t, etc... types as the parameters

Goal: to solve the problem with code.

Question

Is there a way to typecast a uint8_t(and/or uint16_t) parameter to a void*(Approach1)? specifically to pass a non-pointer type value to a void* value.

Is there a way to setup a Generic Type that will work with all the different values(Approach 2)?

Last resort Is there a way to set a specific compiler Exception in the code?(this question has been answer)

Approach 1(causes a invalid conversion from uint8_t to void*)

typedef struct
{
    void (*set_func)(void*);
} SetFunction;

void setValue(uint8_t byteValue)//Not a pointer parameter
{
   byteValue++;
}

void setShortValue(uint16_t byteValue)//Not a pointer parameter
{
   byteValue++;
}

int main()
{
    uint8_t a = 123;
    uint16_t b = 321;
    SetFunction pointerFuncion;
    SetFunction pointerFuncionShort;

    //Typecast the setValue to appease compiler warning
    pointerFunction.set_func = (void(*)(void*))&setValue;
    pointerFuncionShort.set_func = (void(*)(void*))&setShortValue;


    //use the function pointer with non-pointer parameter
    // Compile ERROR thrown invalid conversion from uint8_t to void*
    pointerFunction.set_func(a);
    pointerFuncionShort.set_func(b);
}

Aprroach 2(causes a Too Many Parameters Compile Error)

 typedef struct
{
    void (*set_func)();//Blank parameter to allow multiple args
} SetFunction;

void setValue(uint8_t byteValue)//Not a pointer parameter
{
   byteValue++;
}

void setShortValue(uint16_t byteValue)//Not a pointer parameter
{
   byteValue++;
}

int main()
{
    uint8_t a = 123;
    uint16_t b = 321;

    SetFunction pointerFuncion;
    SetFunction pointerFuncionShort;

    //Typecast the setValue to appease compiler warning
    pointerFunction.set_func = (void(*)())&setValue;
    pointerFuncionShort.set_func = (void(*)())&setShortValue;

    //use the function pointer with non-pointer parameter
    pointerFunction.set_func(a);// Compile ERROR thrown "Too many Args"
    pointerFuncionShort.set_func(b);// Compile ERROR thrown "Too many Args"
}

UPDATE

To add clarity to the problem. I have 100's of functions with 1 parameter. The 1 parameter of the functions are different types. I can't change any of the functions, but I want to have 1 function pointer type(or more based on type) to any of the functions. I can change any of the types associated with the function pointer and the type to the function pointer, but not what it is pointing too.

Community
  • 1
  • 1
Ashitakalax
  • 1,437
  • 1
  • 18
  • 26
  • "not a pointer" - Why not? That's exactly how you do it; `void*`. – Ed S. May 29 '15 at 16:41
  • 1
    You should bypass the compiler's typechecking only if absolutly needed. Most times now, there are better ways. Remember: the compiler is your freiend and you apparently enable the warnings for a reason, too. – too honest for this site May 29 '15 at 17:04
  • @Ed S. The function pointer are being pointer to hundreds of existing functions, I Can't change the parameters of those functions, but I can change the structure of the function pointer – Ashitakalax May 29 '15 at 17:29
  • @Olaf your right, I have a make system that uses part of the code for production stuff(which I want really tight requirements), and I have testing(while I could disable the tight requirements for testing, I prefer to have similar coding convention in our group). – Ashitakalax May 29 '15 at 17:33
  • I'm sorry, I only understand partially what you mean. For coding conventions: I'd say if a team does not conform to them, they are pretty much useless (or there is a problem with teamwork). – too honest for this site May 29 '15 at 17:46
  • I'm over my Production team, I'm not over the testing team. It is my little way to make sure there code doesn't get sloppy. – Ashitakalax May 29 '15 at 17:58

5 Answers5

3

No, it is not.

Simple answer: The called function does not know how to even fetch the argument.

Details: The function code is already fixed when it is called (executed). So it contains code to access the argument, which depends on the type of the arguemtn (e.g. for an uint32_t, a 32 bit load/soter is required, for an uint8_t an 8 bit load/store). So it cannot handle even the value fetch properly. Different from C++ and higher languages like Python, C does not have a concept of run-time type identification built-in.

However, you can pass a union to the function and handle each variant in the function seperately. That would generate all possible accesses. However, you have to specify which actual type is being passed. This is normally done by a second argument which specifies the actual type. That union could also be a struct composed of the type-identifier and the actual value. But that is just an envelope, everything is still explicit.

typedef union {
    int i;
    float f;
} MyValue;

typedef enum {
    MY_VALUE_int,
    MY_VALUE_float
} MyValueType;

void func(MyValueType type, MyValue value)
{
    switch ( type ) {
        ...
    }
}

int main(void)
{
    func(MY_VALUE_int, (MyValueType){ .i=1 });
}

The compound literal argument only works for constants, otherwise you have to assign the value to a union first (or just use that union). gcc has an extension to avoid this, so you can have a function which takes such a union, but the caller may use a simple cast, instead of a compound literal. That works for variables, too:

    func(MY_VALUE_float, (MyValueType)1.0);

An alternative would be passing a const void * and internally casting. However, that is even more risky than the union approach.

All approaches require pasing the actual type explicitly (e.g. using an enum).

C11 allows to create a macro which evaluates different expressions, according to the type of an argument using the new _Generic construct. With that the original approach can be simulated (using gcc extension, normal way is possible, but more complicated):

// use the code of the first block here

#define func_generic(val) _Generic((val), \
    int : func(MY_VALUE_int, (MyValueType)(val)), \
    int : func(MY_VALUE_int, (MyValueType)(val)) )

// and call like:
func_generic(1);
func_generic(1.0);

However, note the restriction of _Generic: No two compatible types are allowed for the type-selector (i.e. const int and int are not allowed both) for uint16_t and uint32_t this works, however.

Note that gcc (you apparently use) supports C11 using -std=c11 or std=gnu11. The latter also enables GNU-extensions.

too honest for this site
  • 12,050
  • 4
  • 30
  • 52
1

Note that both of your exampels exibit Undefined Behaviour because you call a function through a pointer of another (function) type.

I found a solution that relies on the fact that there are a limited number of function types known in advance. I think however that it is too much hassle. Just call the original function.

enum GFType {
  GF_UINT8,
  GF_UINT16 // etc
};

struct GenericFunction {
  void (*func)(void);
  GFType type;
};

void callGenericFunction(GenericFunction func, uint64_t p) // largest type
{
  switch (func.type) {
  case GF_UINT8:
    ((void (*)(uint8_t))func.func)(p);
    return;
  case GF_UINT16:
    ((void (*)(uint16_t))func.func)(p);
    return;
  default:
    assert(1); // unimplemented function type
  }
}

void setValue(uint8_t byteValue) // Not a pointer parameter
{
  byteValue++;
}

void setShortValue(uint16_t byteValue) // Not a pointer parameter
{
  byteValue++;
}

int main() {
  uint8_t a = 123;
  uint16_t b = 321;

  GenericFunction pointerFunction;
  GenericFunction pointerFunctionShort;

  pointerFunction.func = (void (*)(void))setValue;
  pointerFunction.type = GF_UINT8;

  pointerFunctionShort.func = (void (*)(void))setShortValue;
  pointerFunction.type = GF_UINT16;


  callGenericFunction(pointerFunction, a);
  callGenericFunction(pointerFunctionShort, b);

  return 1;
}

Note that

a function-pointer may be freely converted to any other function-pointer type and back again, and you will get the original pointer.

This is what we use. We can't even use void * (because it is a data pointer, not a function pointer) to store the function pointer. So I used void (*)(void) to store the function pointer. An enum tells us to what kind of function we must convert it when we need to cal it.

bolov
  • 72,283
  • 15
  • 145
  • 224
  • I agree that it would be easier to call the function, the particular application has hundreds of functions with a simple 1 parameter value. This interface eliminates the need of writing code for each one. Thanks – Ashitakalax May 29 '15 at 16:47
  • uint64_t is not necessarily the largest type. that would actually be `long long` for ints (at least 64 bits, but possibly larger), but others might be larger (e.g. vector types, some float types bejond double). For me, this is too much casting. This is a nightmare for debugging. – too honest for this site May 29 '15 at 16:52
1

The short answer is no.

You have several problems:

1) The different functions all have to have the same signature to allow the function pointer to point to them.

2) The functions are taking their args by value which means a copy will be passed in and any actions you take on the value will not appear to have any affect outside the function call. Since you don't allow pointers I cant see any way round this.

If you are not bothered about problem 2 then you could try declaring a variadic function which will accept args of any type.

e.g.

void doSomethingWithValue(enum MyType type ...)
{
    va_list args;
    va_start( args, type);

    switch( type)
    {
        case Uint8Type:
        {
             uint8_t value = va_arg(args, uint8_t);

             //doSomething to value
        }
        break;
        .
        .
        .
    }

    va_end(args);
}

Where MyType is an enum set up to identify which type is passed in. which is used like so:

 uint8_t value = 7;
 doSomethingWithValue(Uint8Type, value);
 //note that value is still 7
David Woo
  • 749
  • 4
  • 13
1

If you can use C11, there is a way to do this using _Generic:

#include <stdio.h>
#include <inttypes.h>

#define setvalue_generic(x) _Generic((x), \
    uint8_t: setValue, \
    uint16_t: setShortValue \
    )(x)

void setValue(uint8_t byteValue)
{
    printf("setValue: %" PRIu8 "\n", byteValue);
    byteValue++;
}

void setShortValue(uint16_t byteValue)
{
    printf("setValue: %" PRIu16 "\n", byteValue);
    byteValue++;
}

int main(void)
{
    uint8_t a = 123;
    uint16_t b = 321;
    setvalue_generic(a);
    setvalue_generic(b);

    return 0;
}

Seems to work well with gcc -std=c11 -pedantic -Wextra -Wall.

Alok--
  • 724
  • 3
  • 10
0

@bolov answer is good for handling the different types, this is just a different way of handling the same issue, but with 1 parameter.

The downside to this approach is that the type in main has to be GENERAL_TYPE. In my application I can change the type of the parameter, but I can change the type of the functions that I'm pointing to.

the (void(*)(GENERAL_TYPE))& handles the function's parameter types, and the Union handles the types of all the different sizes.

Another option is to have function pointers for each type too.

typedef union generalType
{
    uint8_t byteData;
    uint16_t shortData;
    uint32_t intData;
    int     integerData;
    uint64_t longData;
    void *  voidData;
    //Add any type
} GENERAL_TYPE;

typedef struct
{
    void (*set_func)(GENERAL_TYPE);
} SetFunction;

void setValue(uint8_t byteValue)//Not a pointer parameter
{
   byteValue++;
}

void setShortValue(uint16_t byteValue)//Not a pointer parameter
{
   byteValue++;
}

int main()
{
    GENERAL_TYPE a.byteData = 123;//restricted to use GENERAL_TYPE here
    GENERAL_TYPE b.shortData = 321;
    SetFunction pointerFuncion;
    SetFunction pointerFuncionShort;

    //Typecast the setValue parameter to be a general type will 
    //Allow it to send the data of whatever type.
    pointerFunction.set_func = (void(*)(GENERAL_TYPE))&setValue;
    pointerFuncionShort.set_func = (void(*)(GENERAL_TYPE))&setShortValue;

    //use the function pointer with non-pointer parameter
    pointerFunction.set_func(a);
    pointerFuncionShort.set_func(b);
}
Ashitakalax
  • 1,437
  • 1
  • 18
  • 26