🔒 Locked
[C++] Class instance relocation
Advertisement
Original Post
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
Enigma
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):
=> 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):
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:
Enigma
#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
Thank you for the many ideas in there. I guess I can also implement virtual functions in a platform-independent way (since I'm allowed to use virtual functions on objects that don't interact with the garbage collector), for instance having the vtable be a derived class. The performance loss might be acceptable.
I'm currently checking whether the compilers I use place the vptr at the beginning of the segment or not.
I'm currently checking whether the compilers I use place the vptr at the beginning of the segment or not.
Topic Locked
This topic has been locked by a moderator. New replies are not allowed.
Sign in to reply to this topic.
Advertisement