0

I am currently working on some code that needs to allow semi-arbitrary c++ classes to be instantiated at runtime, meaning if e.g. a user inputs "Class1", a new instance of Class1 will be instantiated.

All of this is necessarily stored in void* form because the classes do not necessarily share any base classes (and I'd like to support primitives as well, but I'm not currently doing so)

The void* are cast to the correct type upon use, but the issue arises in that I'm also allowing them to be used as a pointer to a base class.

e.g.

void* p = new Derived();
((Base*)p)->someVirtualFunction();

I need a way to cast to the base correctly. obviously if I had the type easily accessible, I could just:

void* p = (Base*)(new Derived());
((Base*)p)->someVirtualFunction();

and there wouldn't be an issue.

However I don't have the type that easily accessible.

I do have an infrastructure in place:

class Type {};

template<typename T>
class TypeImpl : public Type {};

in the actual code, these have many functions which are not relevant to the problem at hand.

I have access to a Type* which points to a TypeImpl<Base> and one which points to a TypeImpl<Derived>

I'm attempting to find a way to use these to cast the pointer correctly e.g.

class Type {
    void* cast(Type* type_of_p, void* p)=0;

    template<typename C>
    void* castTo(void* p)=0;
};

template<typename T>
class TypeImpl : public Type {
    void* cast(Type* type_of_p, void* p){
        return type_of_p->castTo<T>(p);
    }

    template<typename C>
    void* castTo(void* p){
        return ((C*) ((T*) p));
    }
};

but without virtual function templates, I can't find a way to do this.

As for other solutions, my closest thought is maybe some kind of hashmap of hashmaps of function pointers that indexes on std::type_index but I haven't been able to get that to work either.

Is this possible, and if so, how might I go about it?

EDIT TO HOPEFULLY CLARIFY:

I have a void* p that points to a Derived I need to cast it to a Base* (e.g. void* p = new Derived();)

(Base*)p or any type of cast that does not use runtime information is not valid as the vtable is misaligned.

I need something like: (Base*)(Derived*)p (or equivalent c++ casts) but I don't know which derived class it is.

I'm not sure if I'm just wording this very poorly or if there's some mistake I'm making but that's the core issue.

Phi
  • 107
  • 6
  • 3
    a `void*` is just a `void*`, even if you make a cast like in `void* p = (Base*)(new Derived());` its still a `void*`. The question in your title makes no sense. Why do you think you need to use raw void pointers in the first place? (maybe this information is somewhere in your question, but i have to admit i got lost somewhere in the middle ;) – 463035818_is_not_an_ai Aug 08 '18 at 12:00
  • btw the point where i got lost is: "I need a way to cast to the base correctly. " there is nothing special you have to do, you can simply `Base* ptr = new Derived();`... no need for any `void*`... I have the feeling that you got stuck in solving a problem where there maybe is no problem at all. Can you explain what you actually want to achieve by this wild casting? (see also [xy](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem)) – 463035818_is_not_an_ai Aug 08 '18 at 12:06
  • @user463035818 yes a `void*` is a `void*`, but I know for a fact that it points to a memory location which houses a class derived from `Base`, I just don't know which derived class in particular (or not directly). I'm using raw void pointers because I need to read text files with class names and instantiate them, but don't know what the type will be at compile time, in fact the type may not exist at compile time (it's a library) it might be a derived type that is created in the program that uses this DLL. The only type that I am aware of that can store an arbitrary class is `void*` – Phi Aug 08 '18 at 12:06
  • if the types share the same base, then you (typically) dont need to know what is their actual type. All you need to know is that they share the same base class and access them via pointers to base – 463035818_is_not_an_ai Aug 08 '18 at 12:07
  • If it was a void* that pointed to the base, this wouldn't be an issue. It points to the derived class, and casting directly to a `Base*` results in the vtable being misaligned – Phi Aug 08 '18 at 12:11
  • imho the problem is that in your question it is not clear what code is part of the problem and what is part of your solution (eg why using `void*` in the first place?). I think it needs an edit to be more clear. Maybe its just me, i mean you already got an answer... – 463035818_is_not_an_ai Aug 08 '18 at 12:14
  • 3
    If you type-erase type, it seems strange to want to retrieve erased type. You probably want to do "generic" action on it instead which can be stored with the erasure. – Jarod42 Aug 08 '18 at 12:19
  • If you put a `Derived *` in a box, you can only get a `Derived *` out of the box. Once you have the `Derived *`, you can perform whatever implicit conversion you want to get to a `Base *` – Caleth Aug 08 '18 at 12:25

1 Answers1

1

First, I would suggest using C++ casts, rather than plain old C casts, such as static_cast, reinterpret_cast, dynamic_cast and const_cast. They are safer than the C type casts, since they do only one thing.

Since you mention that you have a pointer to an object of the base/derived class you are trying to cast to, you can use decltype.

decltype is a C++11 specifier that yields the type of the variable passed as argument.

So, you can use something like reinterpret_cast<decltype(base_pointer)>(value).

For further information, have a look at the following resources:

https://en.cppreference.com/w/cpp/language/decltype

Cast a value using decltype, is it possible?

When should static_cast, dynamic_cast, const_cast and reinterpret_cast be used?


The discussions in the comments pointed that the initial question asked for a runtime approach, while decltype is compile time. Still, the above might be relevant, so I will add some thoughts on runtime approaches.

The C++ template system and type deduction mechanisms run at compile time. If you want to handle a dynamic type system at runtime (i.e. the user input determining the types used), you have to use something like a factory method.

However, it might be worth considering if there is a better approach to this.

Paul92
  • 8,827
  • 1
  • 23
  • 37
  • 1
    I'm not sure if `reinterpret_cast` is at all what I want, it seems that I likely need `dynamic_cast`. but neither of those solve the issue. the C style casts work fine, and none of the C++ ones use runtime information as far as I am aware? decltype seems promising, I'll look into it and see if it will let me do what I need somehow – Phi Aug 08 '18 at 12:16
  • From the description of the expected usage, I think the OP wants something different: *"if e.g. a user inputs "Class1", a new instance of Class1 will be instantiated."*. Seems to me that the type resolution needs to be done at runtime, so I don't think `decltype` will help here. – GPhilo Aug 08 '18 at 12:17
  • @GPhilo, The core problem is that casting a `Derived*` to a `Base*` changes the actual integer value of the pointer, but casting a `Derived*` to a `void*` to a `Base*` does not. I just can't seem to get all the required information into a function at the same time. – Phi Aug 08 '18 at 12:19
  • 1
    @Phi tbh, given your expected use I'd consider moving away from void pointers altogether and explore options like C++17 `std::any` (or its boost equivalent that you can us with older C++ versions). The problem with `void*` is that they don't carry along any information about what type they originally were, more powerful types may give you what you need. – GPhilo Aug 08 '18 at 12:23
  • @Phi `dynamic_cast` can change the "value" of the pointer: the resulting pointer is guaranteed to point to the most derived object. If there is multiple inheritance involved, if say, you cast from a pointer to the second base class of the actual object to `void*`, you will change the pointer "value". – rubenvb Aug 08 '18 at 12:27
  • @rubenvb that's actually really useful. is there a way to do a void* cast that instead gives the memory address of the "least derived" object? (I realize that doesn't always have a meaningful interpretation with multiple inheritance) – Phi Aug 08 '18 at 12:32
  • @GPhilo, you're probably right. It's just frustrating to throw away such a large section of code that does everything else I need perfectly. I'll look into std::any, I don't think there's a way for me to finagle decltype into making this work. – Phi Aug 08 '18 at 12:35
  • Well if you're feeling brave you can always write your own "MyVoidPtr" class that keeps track of what type it was when created/assigned and internally stores to void*. Then where you need to know the type, you get it from your class's instance – GPhilo Aug 08 '18 at 12:37
  • I'm gonna accept this answer because I don't think a perfect solution exists. I have a giant mess of template factory methods, destruction methods and a bunch more, everything else works great because for creation, destruction/deallocation, and most other things I only need the type of the object itself. for casting I need the type of the object and the type to cast to, and the only way I can see to get it is a virtual function template, which isn't a thing in c++. Thanks for the help! <3 – Phi Aug 08 '18 at 12:39
  • @Phi Not if you don't know the type beforehand, because as you say, there's no definitive answer to that one so it would make little sense to pour that into a core language feature such as `dynamic_cast`. But if you know the base type you want to cast to, `dynamic_cast` will do this job nicely. I'm unsure if `static_cast` will be sufficient (I would suppose so, but I supposed that for casting the other way down the inheritance chain and was surprised multiple inheritance got in the way so I won't state any false truths). – rubenvb Aug 08 '18 at 12:39
  • @GPhilo I have that type of information, but its as e.g. a MyVoidPtr_Impl and I only have a pointer to the parent class. if I could call MyVoidPtr->Cast(), we'd be gucci, but I don't know a way to do that – Phi Aug 08 '18 at 12:40