Dynamically changing the virtual pointer on runtime
Asked Answered
D

8

5

So let's say I have two classes that inherit a base class that has a pure virtual function. Both of the classes implement their own version of that function, but don't add additional member variables so they have the same size. Now sometimes, in the middle of the execution of the program, I want to convert one class to the other without copying all its data. So basically I want to make it use the virtual table of the other class. Is there a portable way of doing this?

Deepsea answered 6/11, 2011 at 3:11 Comment(0)
T
5

The portable way to do this would be to implement your own class system that actually has virtual-pointers that can be copied.

There is no such thing as a virtual-pointer in standard C++.

Teethe answered 6/11, 2011 at 3:24 Comment(1)
Could you please give an example on how to do this?Strychnic
B
5

One young colleague at Andersen Consulting (now Accenture) in Norway once approached me with a serious problem. Their applications, developed in Visual Basic, took a heck of a long time to load. He suspected that this might be because they put each class in its own DLL?

Fearing the worst, I inquired further. And yes, they also had problems with arbitrary crashes etc.

He suspected that the otherwise inexplicable crashes might be connected to their ingenious scheme for changing the type of an object at runtime, by replacing the vtable pointer?

I suggested that maybe they shouldn't really do these things. He looked skeptically at me, and ventured that they didn't have time to do things from scratch again. In fact, they were already stretching it, and there were various problems with that, like their project leader insisting that they work at the client site instead of participating at obligatory meetings. To me, that sounded like mushroom management (keep them in the dark, when a head pops up, cut it): these things often go together.

Anyway, I give you the same advice: don't.

Perhaps you can instead implement fast move operations to move data from a to b?

Or, perhaps you will discover that it's all a case of premature optimization?

Barman answered 6/11, 2011 at 3:42 Comment(1)
I agree 100%. As if development wasn't hard enough already! What happens when some contractor decides to add a couple extra data members to 'their' subclass? I thought that one reason for inheritance etc. was to avoid these sort of bodges.Bashan
L
4

Nope. As far as the language is concerened, there is no such thing as a virtual table, let alone rules about how it looks/what it contains/where it is stored.

Some form of composition is probably more appropriate to your task.

Libidinous answered 6/11, 2011 at 3:15 Comment(0)
C
3

Is there a portable way of doing this?

Absolutely not. The specifics of how virtual functions are implemented are not defined by the spec, and therefore there is no portable way to pretend one virtual class is another.

Counterwork answered 6/11, 2011 at 3:15 Comment(0)
L
3

As the other answers have said, actually changing the vtable is definitely non-portable.

However, there are several work-arounds that could let you accomplish similar semantics without actually changing the class type:

This simplest solution would be to "roll your own" inheritance with an enum describing the current implementation:

class MyClass
{
    public:
    enum DerivedType { A, B };

    private:
    DerivedType myType;

    public:
    void myVirtualFunction()
    {
        if (myType == A)
            myAFunction();
        else
            myBFunction();
    }
}

You could also use a function pointer as a public member variable, which is set to the function indicating the class type. Then you could set the function pointer to the other class's function to "change it's type"

Since you mention you want to avoid copying the data, you could keep your different classes, but have reference-counting pointers to all your member variables, so that you can create new objects of the opposite type from each other quickly.

Ludwick answered 6/11, 2011 at 3:26 Comment(0)
H
2

What about using placement new? This is maybe not quite portable, but it does exactly the required thing - replaces vtable, and nothing more. Just need to take care of constructor - use an empty one.

struct Base
{
    int someData;
    virtual int GetValue() = 0;
};

struct A : public Base
{
    int GetValue() override { return 11111; }
};

struct B : public Base
{
    int GetValue() override { return 22222; }
};

A ob;
ob.someData = 123;
auto ob2 = new (&ob) B;
auto value = ob2->GetValue();

Not mentioning obvious things like classes size, best practices, etc.

Harrisonharrod answered 22/4, 2019 at 8:32 Comment(0)
P
0

Even though this question is old, I would like to bring up a way to do this. (Not quite sure on the portability)

From what I understand you have a class B and C that inherit from some class A and only a single virtual function exists between them. (The method I present here works if B and C are not related as well.)

class A {
public:
    virtual std::string hello() = 0;
};

class B : public A { 
public:
    virtual std::string hello() { return "B"; }
};

class C : public A {
public:
    virtual std::string hello() { return "C"; }
};

And then you want to take a B to a C then call hello and get "B".


So, there is a way to create a watered down version of the boost::any that will cast anything to anything as long as it fits:)

struct parent {};

template< typename T >
struct child : public parent {
    child(T const& t): item(t){}
    mutable T item;
};

template< typename T >
T& as(parent const & p) { return static_cast< child< T > const& >(p).item; }

Then mix it all together:

B b;
parent* p = new child< B >(b);
std::cout << as< C >(*p).hello() << std::endl;
// ==== OUTPUT ====
// B

Can see the code in action here.


To go a step further we can create a function that converts from one type to another without giving a gnat's rear-end about what goes on between them.

template< typename TO, typename FROM >
TO& convert(FROM const& from) {
    parent* p = new child< FROM >(from);
    return as< TO >(p);
};

This can be ran here.

(Realized that I missed the inheritance in these example code links, but after reading the question I think that is what was actually desired. So, to see the test without inheritance go here)


Some other code that I started playing with that I thought might help some as well...

#include <iostream>
#include <string>

class B {
public:
    virtual char hello() {return 'B';}
};

class C {
public:
    virtual int hello() {return 65;}
};

struct parent {};

template< typename T >
struct child : public parent {
    child(T const& t): item(t){}
    mutable T item;
};

template< typename T >
T& as(parent const & p) { return static_cast< child< T > const& >(p).item; }

template< typename TO, typename FROM >
TO& convert(FROM const& from) {
    parent* p = new child< FROM >(from);
    return as< TO >(*p);
};

int main()
{
    B b;
    std::cout << convert< C, B >(b).hello() << std::endl;
    C c;
    std::cout << convert< B, C >(c).hello() << std::endl;
}
// ==== OUTPUT ====
// 66
// A

Figured out how to make it all within the convert function:

template< typename TO, typename FROM >
TO& convert(FROM const& from) {
    struct parent {};

    struct child : public parent {
        child(FROM const& t): item(t){}
        mutable FROM item;
    };

    struct sibling : public parent {
        sibling(TO const& t): item(t){}
        mutable TO item;
    };

    parent* p = new child(from);
    return static_cast< sibling const& >(*p).item;
};
Pustule answered 1/12, 2015 at 20:39 Comment(0)
P
0

As others have already pointed out: C++ is not meant to be used this way. Why? C++ is based on the idea type describes the static aspects of behaviour, and thus should not be manipulated. This allows to do a lot of checks at compile time, since everything about a type is known beforehand.

PImpl pattern

However, there is a well known, portable and generally accepted design pattern, which can give you the desired behaviour: pointer-to-implementation ("PImpl").

  • have a interface class as front-end; this exposes the functions your clients should invoke
  • this class holds a private pointer to an implementation class, and forwards each call from the front-end functions to appropriate functions on the implementation class.

For your desired use case, you could provide an additional API, which makes the front-end object switch its internal implementation object, leading to a dynamic behaviour change.

Since the "PImpl" pointer is private, you're free to pull off all kinds of clever tricks, and you can do so completely within the implementation of the front-end class (in the *.cpp file). You could place them into an inline buffer (with placement new), or you could hold them as singletons in a separate manager, or you could use a pool of instances and a block allocation scheme -- whatever you need to meet specific performance goals.

In the most simplest form, the PImpl is just a smart pointer with single ownership, and the implementation objects are maintained on heap.

class Interface
{
public:
  virtual ~Interface() {}
  
  virtual void doIt()  =0;
};

class ImplA : public Interface
{
  void doIt()  override  { /* impl A */ }
};

class ImplB : public Interface
{
  void doIt()  override  { /* impl B */ }
};



class FrontEnd
{
  std::unique_ptr<Interface> pimpl_;
  
public:
  FrontEnd()
    : pimpl_{new ImplA()}
  { }

  void doIt()    { pimpl_->doIt(); }
  
  void switchB() { pimpl_.reset(new ImplB();) }
  
};
Provincial answered 25/9, 2022 at 17:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.