Home > Net >  Convert void pointer to a dynamic type
Convert void pointer to a dynamic type

Time:04-21

I am trying to convert a void pointer to a dynamic type. For example, I passed double variable to test1 function which I expected the sum function will work. But I got C2664 cannot convert argument error on sum(*reinterpret_cast<myType*>(tmp)). If I using this, it will work.

double c= *reinterpret_cast<double*>(tmp); 
sum(c);

How do I fix this?

void sum(double a)
{
    cout << "sum:" << a 10 << endl;
}
  
void test1(void* tmp)
{
    typedef decltype(tmp) myType;
    sum(*reinterpret_cast<myType*>(tmp));
}

int main()
{
    double a = 87.7;
    test1(&a);

    return 0;
}

CodePudding user response:

First of all, dynamic type is really only meaningful when you're using inheritance. For example, if you have code like this:

class Base {};

class Derived : public Base {};

int main() {
    Base *b = new Derived;
}

In this case, b has a static type of Base *, but a dynamic type of Derived * (because it was declared as a pointer to a Base, but is actually pointing at an instance of Derived).

So, if you want to recover an actual dynamic type, you'll need to start by defining some base class, and some derived class(es), and convert between them. When/if you do that, you usually want to use dynamic_cast rather than reinterpret_cast to do the job (it'll automatically check the actual type, so the cast will fail if you try to cast to a type that it's not really pointing at). But in most cases, you want to define some sort of virtual function in the base class, and override it as needed in the derived classes, so you don't need to use dynamic_cast at all. Also note that dynamic_cast only works on classes that contain at least one virtual function (but if you're using public inheritance without any virtual functions, you're probably doing something wrong).

Alternatively, you can create basically a discriminated union. You'll need to store something to record what actual type is stored, and you'll use that to cast back to the correct when needed.

enum type { FLOAT, DOUBLE };

struct value { 
    type t;
    void *value;
};

float f = 1.2f
double d = 3.2;

value v { FLOAT, &f };
value y { DOUBLE, &d };

void do_call(value v) {
    if (v.type == FLOAT) { 
        use_float(*reinterpret_cast<float *>(v.value));
    else if (v.type == DOUBLE)
        use_double(*reinterpret_cast<double *>(v.value));
}

The compiler, however, won't store anything in a void * to keep track of what type of object it's really pointing at. If you want to cast back from void * to float or double as applicable, you're going to have to store something to keep track of which type that actually was.

Rather than the latter, you might also want to look into using std::variant, which handles storing the type tag automatically, and can help automate most of the type dispatching as well (e.g., with std::visit). At least as typically used, this will act more like an actual tagged union though, so it stores the actual values, rather than pointers to values that are stored elsewhere.

  • Related