Jump to content
  • Advertisement
Sign in to follow this  
ToohrVyk

[C++] Class instance relocation

This topic is 4847 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I have an instance of a class which contains no user-defined pointers, and is a POD in all ways except one: it uses virtual functions. Can the instance be relocated by a memory-wise copy function (such as malloc), or is there a way to make this relocation legal?

Share this post


Link to post
Share on other sites
Advertisement
Because I'm not in charge of the relocation code (which is inside a garbage collector), and said relocation code is C code which is not aware of such C++ considerations.

Share this post


Link to post
Share on other sites
Fair enough. On x86 you should be safe to just memcpy it because every compiler I know of handles virtual function via an absolute pointer to the vtable, stored as the first member. For a bit of extra reassurance you could check the COM specification, since I believe this is required by COM. If it is, then you can pretty much count on it for all COM-compliant compilers, even if you're not using COM. I sincerely doubt that any of this is guaranteed by the C++ standard though, so you may sacrifice some portability, in particular I can say nothing about non x86 platforms, since I have almost no experience with them.

Enigma

Share this post


Link to post
Share on other sites
And sadly, this code is meant to run on PocketPC, Mac OS X, Symbian OS, and several handheld consoles...

I think I'm going to have to do something hackish...

Share this post


Link to post
Share on other sites
I think you are looking for "placement new"

=> a quick google turned up the following:

Standard C++ also supports placement new operator, which constructs an object on a pre-allocated buffer. This is useful when building a memory pool, a garbage collector or simply when performance and exception safety are paramount (there's no danger of allocation failure since the memory has already been allocated, and constructing an object on a pre-allocated buffer takes less time):

Share this post


Link to post
Share on other sites
I am already using placement new to create my objects on buffers that I get from the garbage collector (which is, in fact, a heap optimizer or something similar). But this doesn't allow the garbage collector to move my instances around.

Share this post


Link to post
Share on other sites
As long as you have no pointers actually pointing to that object, it should be fine. Emphasis on 'should'. Just a simply memcpy should do the trick nicely. I'm using this extensively with my virtual-to-virtual address mapping system.

Share this post


Link to post
Share on other sites
I agree, it seems to work on x86. But once the code is done, I will have to compile for a dozen platforms, and I'd rather not find out THEN that something is amiss.

Share this post


Link to post
Share on other sites
About your only alternative (bar dropping the virtual functions) would be to implement your own virtual function support. It's doable, but the maintenance overhead is significantly higher and you should expect to lose some performance. You would also need to thoroughly investigate your target compilers on each platform and provide different code paths (via #defines) where virtual function implementations differ. Here's an example I hacked together for x86. It works (althought I hesitate to use such a strong word as "works") under Borland 5.6.4, gcc 3.3.1 and VC++ 7.1. It's also ugly as heck:
#include <iostream>
#include <iterator>
#include <boost/lexical_cast.hpp>

template < typename MemberFunctionPointer >
std::size_t getFunctionAddress(MemberFunctionPointer functionPointer)
{
return *reinterpret_cast< std::size_t * >(&functionPointer);
}

class Base
{

public:

Base();
Base(Base const & base);
int virtualFunction();
Base & operator=(Base const & base);

protected:

Base(std::size_t * vtablePointer);
std::size_t * vtablePointer_;

int doVirtualFunction();

private:

static std::size_t vtable[1];

};

class Mid
:
public Base
{

public:

Mid(int i);
Mid(Mid const & mid);
std::string virtualFunction2(int i);
Mid & operator=(Mid const & mid);

protected:

Mid(int i, std::size_t * vtablePointer);

private:

std::string doVirtualFunction2(int i);
static std::size_t vtable[2];

int i_;

};

class Derived
:
public Mid
{

public:

Derived();

private:

int doVirtualFunction();
std::string doVirtualFunction2(int i);

static std::size_t vtable[2];

};

Base::Base()
:
vtablePointer_(reinterpret_cast< std::size_t * >(&Base::vtable))
{
}

Base::Base(std::size_t * vtablePointer)
:
vtablePointer_(vtablePointer)
{
}

Base::Base(Base const & base)
:
vtablePointer_(reinterpret_cast< std::size_t * >(&Base::vtable))
{
}

int Base::virtualFunction()
{
std::size_t function = vtablePointer_[0];
return (this->*(*reinterpret_cast< int (Base::* *)() >(&function)))();
}

Base & Base::operator=(Base const & base)
{
vtablePointer_ = reinterpret_cast< std::size_t * >(&Base::vtable);
return *this;
}

int Base::doVirtualFunction()
{
return 7;
}

std::size_t Base::vtable[1] = {getFunctionAddress(&Base::doVirtualFunction)};

Mid::Mid(int i)
:
Base(reinterpret_cast< std::size_t * >(&Mid::vtable)),
i_(i)
{
}

Mid::Mid(int i, std::size_t * vtablePointer)
:
Base(vtablePointer),
i_(i)
{
}

Mid::Mid(Mid const & mid)
:
Base(reinterpret_cast< std::size_t * >(&Mid::vtable)),
i_(mid.i_)
{
}

std::string Mid::virtualFunction2(int i)
{
std::size_t function = vtablePointer_[1];
#if defined(_MSC_VER)
return (this->*(*reinterpret_cast< std::string (Mid::* *)(int) >(&function)))(i);
#else
return (*reinterpret_cast< std::string (* *)(Mid *, int) >(&function))(this, i);
#endif
}

Mid & Mid::operator=(Mid const & mid)
{
vtablePointer_ = reinterpret_cast< std::size_t * >(&Mid::vtable);
i_ = mid.i_;
return *this;
}

std::string Mid::doVirtualFunction2(int i)
{
return "Mid: " + boost::lexical_cast< std::string >(i) + " with member variable: " + boost::lexical_cast< std::string >(i_);
}

std::size_t Mid::vtable[2] = {getFunctionAddress(&Base::doVirtualFunction), getFunctionAddress(&Mid::doVirtualFunction2)};

Derived::Derived()
:
Mid(15, reinterpret_cast< std::size_t * >(&Derived::vtable))
{
}

int Derived::doVirtualFunction()
{
return 42;
}

std::string Derived::doVirtualFunction2(int i)
{
return "Derived: " + boost::lexical_cast< std::string >(i);
}

std::size_t Derived::vtable[2] = {getFunctionAddress(&Derived::doVirtualFunction), getFunctionAddress(&Derived::doVirtualFunction2)};

int main()
{
Base b;
Mid m(12);
Derived d;
Base * bp = &b;
std::cout << bp->virtualFunction() << '\n';
bp = &m;
std::cout << bp->virtualFunction() << '\n';
bp = &d;
std::cout << bp->virtualFunction() << '\n';
Mid * mp = &m;
std::cout << mp->virtualFunction2(99) << '\n';
mp = &d;
std::cout << mp->virtualFunction2(99) << '\n';
std::cout << b.virtualFunction() << '\n';
std::cout << m.virtualFunction() << '\n';
std::cout << m.virtualFunction2(99) << '\n';
std::cout << d.virtualFunction() << '\n';
std::cout << d.virtualFunction2(99) << '\n';
Mid m2 = d;
std::cout << m2.virtualFunction() << '\n';
std::cout << m2.virtualFunction2(99) << '\n';
}

Enigma

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!