-3

Context:

I have a piece of code that compiles and runs fine when compiled as C, and I am trying to port it to C++ to be able to use it in future C++ programs.

I'm using this structure data type in my program.

 typedef struct MessageDigest{
      int32_t digestLen;
      void* ctx;
      int (*Init)(void* c);
      int (*update)(void* c, const void* data, size_t len);
      int (*Final)(uint8_t* md, void* c);
   }MessageDigest;

In my main function I'm affecting the structure's identifiers:

    MessageDigest* newMD;
    /*Other code*/
    newMd->Init=&SHA1_Init; //The problem is here

I get this error :

 invalid conversion from ‘int (*)(SHA_CTX*) {aka int (*)
 (SHAstate_st*)}’ to ‘int (*)(void*)’ [-fpermissive]

I tried with static_cast<> but it's not working

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
Ahmed Mkadem
  • 65
  • 2
  • 9
  • Show the definition of `SHA1_Init` – David May 11 '17 at 12:50
  • 2
    Please provide a [mcve]. – Passer By May 11 '17 at 12:50
  • The message is self-explanatory. `int (*)(SHA_CTX*)` is not the same as `int (*)(void*)` and you cannot assign one to the other. – n. m. could be an AI May 11 '17 at 12:51
  • Gotta ask... why are you using function pointers like that in C++? Is it related to some legacy C library or something? – hyde May 11 '17 at 12:57
  • @n.m. Actually it's working in C but I want it in C++, when I changed the compiler from gcc to g++ the only error I got is this conversion. @David it's defined in openssl/sha.h : `int SHA1_Init(SHA_CTX *c);` I found the declaration here https://wiki.openssl.org/index.php/Manual:Sha(3) – Ahmed Mkadem May 11 '17 at 13:01
  • @hyde it is perfectly fine to use function pointers, even in Cpp... – MABVT May 11 '17 at 13:01
  • @n.m. s comment is correct. You have different function signatures, one taking a pointer to SHA_CTX, another a pointer to void. – MABVT May 11 '17 at 13:01
  • @MABVT Doing type-unsafe things (those void pointer arguments) is never "fine" in modern C++. It may be "best" or at least "less bad than alternatives" in some situations, but I'd never call it "fine". – hyde May 11 '17 at 13:05
  • 2
    From a language implementation point of view it is perfectly fine to use it in CPP, that is all I said. That it is neither considered "modern" nor type-safe is written on another piece of paper. The "auto" keyword is also modern, it is type-safe, and still I consider it bad as it reduces the awareness of the modern cpp coder by leaving "all type deduction to the compiler" and giving a sh** on what is effectively be done. Proper use of the void* requires the developer to know what he's doing at least. But that's philosophical :D @hyde – MABVT May 11 '17 at 13:08
  • Anyway, porting C to C++ should not be simply trying to compile C as C++... – Serge Ballesta May 12 '17 at 06:41

1 Answers1

4

The error is clear about the problem. You declare

int  SHA1_Init(SHAstate_st*);

As its parameter is declared to be a SHAstate_st*, is it not compatible with the function pointer declaration which expects the parameter to be a void *.

You have 2 ways here. The short one is to use reinterpret_cast. As we know that for any current compiler, a pointer to void or to struct is a mere address, it should work but IMHO it invokes Undefined Behaviour with a strict reading of standard:

newMd->Init=reinterpret_cast<int (*)(void *)(&SHA1_Init); // should work but not really standard compliant

The correct one is to wrap SHA1_Init.

int SHA1_Init_void(void *p) {
    return SHA1_Init(static_cast<SHAstate_st *>(p));
}

You can then correcly use it:

newMd->Init = &SHA1_init_void;

But anyway, you are taking it the wrong way: you are essentialy trying to compile C code in C++ which often leads to many little errors like this one. They are indeed different languages, and have particular semantics on corner cases. And it generally leads to poor code not using all the power of C++ language, mainly type safety guarantee at compile time.

Typically, having pointer to functions taking void * parameters in a C structure could often be better expressed by method overriding in C++. It will certainly involve more initial work, but IMHO you should considere rewriting the higher level of your code in modern C++ (with classes, constructors, methods and derivation, optionaly templates) and calling lower level C functions. You will get more maintainable code and have a gain in the future. Mixing C and C++ translation units in one single project is perfectly fine, and you just have to enclose the C related include files in extern "C" { ... } blocks from your C++ source files. The common usage is to put that directly in the .h files protected with a #ifdef __cplusplus and is demonstrated in this other SO question: Combining C++ and C - how does #ifdef __cplusplus work?

Community
  • 1
  • 1
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252