0

First of all, thank you to whoever is reading and to whom you can respond.

This part of code, is part of an interface library gui, totally written in c.

But I'm tempted to compile these written files in c, with a c ++ compiler to be able to integrate into a program I'm doing with wxwidgets.

Within the c files, I have several function calls that have as arguments, pointers to other functions.

 typedef struct {
      void (*fchd_ptr)(bool);   
      uint16_t xpos;         
      uint16_t ypos;          
        ...
        ...
}BUTTON_t;

void BT_SetHandler(BUTTON_t* ptr, void* fchd_ptr)
{
    if (ptr == NULL)
        return;

    ptr->fchd_ptr =  fchd_ptr;  // OK in C, ERROR In C++
}

When I try to compile in C ++, I get this error.

ptr-> fkt_ptr = fkt_ptr;
// error: invalid conversion from 'void *' to 'void (*) (bool)' [-fpermissive]

If I cast an explicit cast.

ptr-> fkt_ptr = (bool *) fkt_ptr;
// error: can not convert 'bool *' to 'void (*) (bool)' in assignment

Any help, and explanation how to solve it will be welcome.

Hong Ooi
  • 56,353
  • 13
  • 134
  • 187
ivan braga
  • 13
  • 2
  • 4
    How to solve it is to add fuel to the fire and add a bunch of explicit casts everywhere. This is very fragile, and type-unsafe. An even better solution is to rewrite the whole thing in typesafe C++. – Sam Varshavchik May 09 '18 at 23:57
  • 2
    C++ has stronger typing rules with respect to what you can do with a `void*` It demands that you acknowledge that you're doing something dodgy with a cast. – user4581301 May 09 '18 at 23:58
  • 2
    Are you really explicitly casting to `bool*`? Seems like you should cast to `(void (*)(bool))` instead... – hlt May 10 '18 at 00:02
  • Sam Varshavchik, I know that if everything was in C ++ it would be right, but I try to reuse the code in c, because what I'm trying to do is a RAD tool, ie drag and drop. So, I need the c code, as this will be used by the final application in c. – ivan braga May 10 '18 at 00:08
  • 2
    You should compile the C files in C. (Maybe you are unaware that compiled C files can be linked into a C++ project) – M.M May 10 '18 at 00:12
  • hit, I applied (void (*) (bool)), and the error stopped. You could explain (void (*) (bool)), so I understand what happens. – ivan braga May 10 '18 at 00:14
  • `void (*fchd_ptr)(bool)` declares a pointer to a function that accepts a `bool` parameter and returns `void` – Ripi2 May 10 '18 at 00:17
  • M.M, I was going in that direction, compile in c and link. – ivan braga May 10 '18 at 00:21

2 Answers2

1

Your explicit cast is wrong. You cast to bool * (pointer to object of type bool), when you really should be casting to void (*)(bool) (pointer to function taking a bool and returning nothing).1

If you replace that line by

ptr->fchd_ptr = (void (*)(bool)) fchd_ptr;

it will compile in both C and C++.


If possible, however, you should try to compile your C code in C only - while we might get this one thing working, the two languages are not always fully compatible, and you may well run into trouble down the road. Generally, it is possible to compile C code separately from the C++ parts. If you use extern "C" in your header files, you can simply link the C++ objects and the C objects together, or keep the C part in a separate library (static or dynamic).

Additionally, I would recommend reworking the C API so that it really takes a function pointer all the way through if that is at all possible. Aliasing through void * is ugly and can lead to some really nasty bugs and crashes if you pass in the wrong pointer type.


1 Function pointers are generally very different from object pointers, and it is implementation-defined whether you are even allowed to cast between the two. Here, you want a pointer to a function, not a pointer to an object (like bool* would be a pointer to an object of type bool) so that you can call it later. You can see in your code sample that void (*fchd_ptr)(bool) is the declaration of the variable that you assign to, which makes void (*)(bool) its type. If you find the notation somewhat confusing, cdecl.org is a great way to deal with those convoluted array, pointer and function pointer names that come from C.

hlt
  • 6,219
  • 3
  • 23
  • 43
  • 1
    *recommend reworking the C API so that it really takes a function pointer* -- this will avoid the need of a cast for assignment. – jxh May 10 '18 at 00:32
  • jxh, Could you give an example? How would you do with the code example above? – ivan braga May 10 '18 at 00:37
  • 1
    Basically the idea is to replace `void *fchd_ptr` in the function signature with `void (*fchd_ptr)(bool)` - the same function pointer type that you need later. That lets you avoid the cast, and the errors that can happen with `void *` – hlt May 10 '18 at 00:39
1

If you change the signature of the function to accept a function pointer that matches the type expected by ptr->fchd_ptr. Then, you would not need to use a cast to complete the assignment.

Based on your question, this would be:

void BT_SetHandler(BUTTON_t* ptr, void (*fchd_ptr)(bool))

However, if you are not sure what the type is for ptr->fchd_ptr, you can make the compiler figure it out for you. Previously, this could only be done with a template function and it would derive a type based on what was passed into the function call at compile time. However, C++.11 provdes decltype, which allows you to enforce the type you want for the function parameter, rather than letting the compiler deduce one from callers of the function.

void BT_SetHandler(BUTTON_t* ptr, decltype(ptr->fchd_ptr) fchd_ptr)

This is fine so long as you are intent on compiling your code with a C++ compiler.

jxh
  • 69,070
  • 8
  • 110
  • 193