5

I am working on a C++ plugin that will be called from C#. The API I am trying to port comes with hundreds of functions and most of them are just overloads with the-same name but with different datatypes like int, float and char.

Instead of writing the-same code but with different datatypes on both C++ and C# side over and over again, I want to use one function overload for it with a generic pointer. My goal is to use static_cast from void* to int, float and then char and use which one that is successful first.

A simple test function from the C++ side:

void calculate(void* input1, void* input2)
{
    float *i1 = static_cast<float*>(input1);
    if(i1==NULL)
    std::cout<<"Bad"<<std::endl;
    else
    std::cout<<*i1<<std::endl;
}

int main()
{
   int input1 = 5;
   int input2 = 10;

   calculate(&input1,&input2);

   return 0;
}

This is supposed to output "BAD" but it seems to be showing "7.00649e-45" which I believe is an undefined behavior. The static_cast is failing and I don't know how check if it is successful or not. Checking if it is NULL did not help in this case.

Is it possible to check if void* to Object with static_cast is successful or not? If so, how?

Note:

This is not a duplicate of this question. I don't want to find out the type of the Object from void*. I just want check if the conversion or static_cast is successful or not. That's it.

Programmer
  • 121,791
  • 22
  • 236
  • 328
  • 1
    Store the `typeid`. Or better, use `std::any` or `std::variant`. – user202729 Apr 14 '18 at 11:21
  • [Possible duplicate](https://stackoverflow.com/questions/1718412/find-out-type-of-c-void-pointer). **edit** nevermind your case is more specific than this. – user202729 Apr 14 '18 at 11:22
  • Not a duplicate. I am not trying to find the type of the object from the pointer. Just checking is the conversion is successful or not. – Programmer Apr 14 '18 at 11:23
  • [Better possible duplicate](https://stackoverflow.com/questions/33370296/run-time-checking-of-a-cast-from-a-void). – user202729 Apr 14 '18 at 11:24
  • 4
    The cast doesn’t fail, but dereferencing its result is undefined. There is no way for either you or the compiler to determine the ”original” type of a `void*`. – molbdnilo Apr 14 '18 at 11:25
  • @user202729 That is `dynamic_cast`. This is about `static_cast`. You can't do this with `dynamic_cast` – Programmer Apr 14 '18 at 11:27
  • 2
    `static_cast` means "Even though it is declared `void*`, I **know** that it is really a `float*`." So if it isn't a float* it is on your shoulders. – Eljay Apr 14 '18 at 11:28
  • Not that different. The linked question is "Run-Time Checking of a Cast from a void*" and the answer is "You can't". – user202729 Apr 14 '18 at 11:29
  • @user202729 type erasure? Then why void*? – con ko Apr 14 '18 at 13:04
  • Possible duplicate of [Find out Type of C++ Void Pointer](https://stackoverflow.com/questions/1718412/find-out-type-of-c-void-pointer) – con ko Apr 14 '18 at 13:05
  • 1
    So this `void*` is actually coming in from C# (as `System.IntPtr`)? – mnistic Apr 14 '18 at 13:09
  • @mnistic That's right. – Programmer Apr 14 '18 at 13:09
  • 1
    You may be out of luck. There is no way to somehow retrieve the type back in C++ (at least that I know of). You will have to perform type checking in C# and then invoke your C++ function so the data can be marshaled appropriately. – mnistic Apr 14 '18 at 13:15

2 Answers2

1

The use of static_cast to recover the type of the argument of your function is not appropriate because the casting will be done at compile time from void* to float*. Since a conversion chain for this types exists, the compiler will not complain and during execution, the casting itself will not fail, even if there is no guarantee on the result of it.

The same applies to the casting from int* to void* when invoking the calculate function.

Maybe for this case you could consider the use of a template function to exploit the metaprogramming feature of C++.

Here is a snipped to start with:

template <class T*>
void calculate(T* a, T* b) {
    // your implementation here
}

EDIT

If you need to export the template function only for a few types, like int, float, double etc you may consider adding this a template specialization for each type in one of the cpp files of your library that includes the header where the template funcion is declared.

Here is an example for the int* type.

   template __declspec(dllexport) calculate<int*>(int* a, int* b);
Gabriella Giordano
  • 1,188
  • 9
  • 10
  • I thought about template too but can't use it because a template function cannot be exported when building it as DLL. The final function will be called from C# like I mentioned in the question. – Programmer Apr 14 '18 at 12:53
  • Yes, use template can solve this problem easily, moreover, someone has thought std::any. But it isn't an answer, I thought. – con ko Apr 14 '18 at 13:08
  • @Constructor Did you read my comment about not being able to do that since you can't export template function in a dll plugin? `std::any` is not an option either. – Programmer Apr 14 '18 at 13:17
  • @Programmer I see, I mean people should solve your problem, not change a way to implement it. – con ko Apr 14 '18 at 13:19
  • @G.Giordano I saw your edit but that doesn't help either. `calculate(int* a, int* b)` can only take int. This is why I used `void*` which is a generic pointer. Each function has about 5 overloads and there is about 900 of them. I asked this question so that I can use `void*` and avoid declaring each one 5x and writing the-same code over and over again on the C++ and C# side. This will take so long to complete if not months. – Programmer Apr 14 '18 at 13:22
  • @Programmer now I see your problem is the size of your code base, so in this case you could evaluate another solution, instead of using a cast to check the types, add a third parameter to the signature of your functions, maybe an enum that tells you the original type of the void* pointers that you are passing by. This is not a clean c++ solution, and I would not go for it, but sometimes the only way to get things done quickly is the dirty way. Hope this helps... :) – Gabriella Giordano Apr 14 '18 at 13:37
  • Yup. That's what I originally did but wasn't satisfied due to so many conversions so that's why I asked checking `static_cast` status. Thanks for your help – Programmer Apr 14 '18 at 13:47
0

static_cast performs no type checking and cannot be used to reliably retrieve the type. Furthermore, what is passed into your function is a void* pointer, at which point type information is lost and not even dynamic_cast would help. So this would be a hard problem to solve without templates even in pure C++. The fact that the void* pointer is actually a C# System.IntPtr compounds the problem and to the best of my knowledge there is no interop layer powerful enough to retrieve the original type (even if primitive).

Problems like this one is why boilerplate code generators like cog were invented. There are a bunch of them out there and you may want to do some research to find which one best fits your needs. Cog works by inserting code generation scripts embedded right in C++ (or C#, or whatever) files and can be injected into your build process. You may find it worth the (relatively small) time investment (compared to generating the boilerplate code manually).

mnistic
  • 10,866
  • 2
  • 19
  • 33