Trying to understand how RCCPP works at a lower level

223 views
Skip to first unread message

liangdeng fan

unread,
Jul 10, 2014, 9:13:28 AM7/10/14
to runtimecompi...@googlegroups.com

Hi,
I'm trying to integrate RCCPP into my own engine, which has it's own object factories and per module interfaces, and therefore I'm trying to see and understand how much of the RuntimeObjectSystem needs to be recoded to suit my engine, and to do that, I need to understand how the underlying things work.

This is the process that I'm going through in my head right now as to how this works at a lower level. Do let me know if there's something very wrong here.

At Program Initialization/Startup:
- Keep track of all dependent build header/cpp files via the macro REGISTERCLASS(RuntimeObject01).
Example:
Macro REGISTERCLASS expands to find all associated RuntimeIncludeFiles, RuntimeSourceDependency, and RuntimeLinkLibrary. TObjectConstructorConcrete constructs itself, calling PerModuleInterface::GetInstance() in the process. Due to this, ObjectInterfacePerModuleSource.cpp is added as a RuntimeIncludeFile, which contains definitions to PerModuleInterface.


At Program Runtime:
1) File for source code changes. Assume RuntimeObject01's source is changed for the following steps.
2) Use the dependent files that we gathered earlier as arguments for the build tool, building to a temporary file.
3) Call LoadLibraryA() on the temporary file and getting the module's handle.
As a side effect, due to additional code being loaded into the program's memory space, the REGISTERCLASS macro declares objects in the global scope and the object is registered again. Thus, another different PerModuleInterface is instantiated, but in a different memory space that is not the original program's.
4) Call GetProcAddress(modulehandle, "GetPerModuleInterface"); to get the function that returns the PerModuleInterface, which provides new calls to the newly merged dll's
5) Serialize all associated objects, reconstruct all objects, then deserialize them again, and reassign any additional pointers through the callback OnConstructorsAdded().


So if I'm understanding this correctly, some conclusions can be made here:
a) Code used for static libraries should also work with this method.
b) The memory leak is caused by the fact you simply no longer reference the old PerModuleInterface structures and functions.
c) MSVC's call stack will show something like A32B.tmp!RuntimeObject01::Update(), followed by ConsoleExample.exe!ConsoleGame::MainLoop. The temp file references the dll that you built in 2).
d) Step (5) can essentially be considered as replacing ALL references to the old objects, hence (b). So if A.cpp classes has pointers to B.cpp classes and B is runtime compiled, changing B.cpp would require an update of A.cpp's pointers to B.cpp's classes.

Anyway, thanks for setting this up; this is some really good learning material to work with.


Thanks,
Deng

liangdeng fan

unread,
Jul 10, 2014, 12:01:51 PM7/10/14
to runtimecompi...@googlegroups.com
Also, the template TActual in ObjectInterfacePerModule overrides the new operator for aligned allocation. Is this essential?

Doug Binks

unread,
Jul 11, 2014, 7:36:06 AM7/11/14
to runtimecompi...@googlegroups.com
Hi,

I'm travelling at the moment so not able to give as full an answer as I'd like. There's some more information available in an article in the Game AI Pro book and I'm expanding the RCC++ wiki when I get time.

Your understanding is correct about the process. I'm hoping to make it easier in future to integrate the technique into engines which have their own factories, but this may take some time to get around to.

Note that the memory taken up by 'old' libraries and code may not be a memory leak as such since it may be needed for code which hasn't changed. Since the memory overhead isn't very large and this technique is intended for development I've not put extra effort into cleaning up but this could be fairly easily added.

As for the serialization step this is indeed how we ensure old code references new code correctly. I'm hoping to remove the requirement for this step through a simple smart RCC++ pointer for those who want to use such

The TActual new overload is required if you have objects which need alignment. TActual is somewhat of a problem and I am looking into moving code into the base RCC++ class, but there have been a few small compiler compatibility issues which I need to resolve - nothing hard but I've not been able to get much time for this at the moment. Once this is done you will then be more easily be able to override behaviour, since TActual is currently the final interface rather than a parent.

Hope that makes sense - note that as I'm travelling replies will be infrequent and brief at the moment, apologies!

liangdeng fan

unread,
Jul 13, 2014, 9:23:05 AM7/13/14
to runtimecompi...@googlegroups.com
Thanks for the help Doug!

However, I've discovered a slight slowdown with the integration of RCCPP; it feels like it's designed to enforce measures to much of the user's existing code, like inheritance from IObject, etc. The most crucial point is that Runtime .cpp files don't play very well with other pieces of non-runtime cpp files. Here's a modified version of the ConsoleExample project (add/change the files attached):

1) RuntimeObject01.cpp is modified such that it includes TestObj.h. RuntimeObject01 contains an object TestObj, and calls it's function Yolo() and another static function TestObjStatic::Update.
2) Run ConsoleExample and save RuntimeObject01.cpp.
3) RuntimeObject01 is thus rebuilt to the module ABC1.tmp. If you put a breakpoint at RuntimeObject01::Update, you will see a callstack consisting of ABC1.tmp!RuntimeObject01::Update, followed by ConsoleExample.exe!Console::MainLoop. When you step into v.Yolo() or TestObjStatic::Update, the callstack will show that these 2 functions all belong to the module ABC1.tmp and thus execute code in the newly compiled module.

Ideally, these 2 functions should be able to be linked into ConsoleExample's modules, not in the temporary file that was built. Is this a limitation of the compiling process not being able to relink to functions in the old module? Or would it be possible to change it?

Again, to re-iterate the point of not playing well with non-runtime compiled pieces of code, let me present an example. If TestObj had class members, I would have to make sure that it is serializable and registered as Runtime Compilable. Likewise, if TestObjStatic was a singleton and had class members, I would have to use REGISTERSINGLETON and make sure that it is serializable. All this is done so that the new module has the exact same things after recompilation. This causes a kind of chain-reaction and also causes all kinds of changes to other areas in an existing code base just to get one source file to be runtime compiled code, not to mention constraints on implementing new code.


TestObj.cpp
TestObj.h
RuntimeObject01.cpp

Doug Binks

unread,
Jul 19, 2014, 6:04:06 AM7/19/14
to Runtime-Compiled C++
The runtime source dependencies are indeed compiled into the new module. If this is a problem then you can use one of the two other techniques: a) pass interfaces to the runtime code via a SystemTable, see the Pulse example for details; b) put the shared code in a dynamically linked library and link to it with the RUNTIME_COMPILER_LINKLIBRARY approach https://github.com/RuntimeCompiledCPlusPlus/RuntimeCompiledCPlusPlus/wiki/Using-libraries-from-runtime-modifiable-classes .

The SystemTable approach requires virtual interfaces and is the usual method for passing interfaces to a C++ codebases you control, whereas the dynamically linked library approach is good for large C libs and external libraries such as OpenGL. The source dependency approach is best for small pieces of code where a library or virtual interface is overkill.

By 'slight slowdown with the integration' I assume you mean that integrating RCC++ is going more slowly than you'd expected.

If you're looking for an easier approach, there are a couple of commercial Visual Studio plugins based around the ideas of RCC++ now available (Alcantarea and Recode) which I've listed on the wiki https://github.com/RuntimeCompiledCPlusPlus/RuntimeCompiledCPlusPlus/wiki/Alternatives . Recode is in use by several large games development studios and extremely easy to use on most code-bases.

Our RCC++ implementation is designed to be as standard C++ as possible, with only a few assumptions made about externals to the standard - that a compiler is available which can create a shared/dynamic library and that the OS can runtime link these. This makes RCC++ fairly portable, as it's not dependent on a particular tool chain (though currently I've only implemented Windows, OS X and Linux backends). The code pre-dates C++11, and with C++11 support patchy on Windows we don't anticipate updating to remove support for C++03.

This creates some limitations, which you're seeing. As we get more experience with RCC++ methods, I expect to see improvements making this easier - but if integrating RCC++ into a complex code-base is a fairly demanding experience for someone in-experienced with RCC++, though there are a fair number of success stories as well as a number of people who've rolled their own. Documentation is still a bit of a work in progress, but I'm iterating on it as and when I can.





--
You received this message because you are subscribed to the Google Groups "Runtime-Compiled C++" group.
To unsubscribe from this group and stop receiving emails from it, send an email to runtimecompiledcpl...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Doug Binks

unread,
Jul 19, 2014, 8:09:26 AM7/19/14
to runtimecompi...@googlegroups.com

Daniel Moodie

unread,
Jul 1, 2015, 1:48:17 PM7/1/15
to runtimecompi...@googlegroups.com
Hi all,

I've made a RCC::smart_ptr class that may be useful.
Use of this IObject interface and the smart pointer requires the use of a modified ObjectFactorySystem.cpp that explicitly serializes the notifiers and calls updateNotifiers.
I created a separate IObject::SerializeNotifiers function because I didn't want to have to trust the end developers to remember to call IObject::serialize to make sure the notifiers are updated.

This allows for the use of a RCC::smart_ptr<YourObject> much like a boost::shared_ptr or std::shared_ptr.  As well, there is a weak_ptr object that can be used for pointing to an object without affecting the reference count.  

Doug Binks

unread,
Jul 1, 2015, 3:04:08 PM7/1/15
to Runtime-Compiled C++
Thanks Daniel - I'm sure a lot of people will find that useful. You might want to create a new thread for this.

I've been considering a smarter way to enable IObject pointer exchange, but I'm not fond  of reference counting nor adding to the footprint of IObject if possible - however there are likely a lot of people who would make the trade off for easier coding.

--

Daniel Moodie

unread,
Jul 2, 2015, 4:15:50 PM7/2/15
to runtimecompi...@googlegroups.com
My apologies, I just saw you mention something along those lines previously in this thread.  I'll make a separate thread detailing it.
To unsubscribe from this group and stop receiving emails from it, send an email to runtimecompiledcplusplus+unsub...@googlegroups.com.

Doug Binks

unread,
Jul 2, 2015, 5:02:41 PM7/2/15
to Runtime-Compiled C++

No need to apologise, this is cool stuff to hear about and deserves it's own thread!

To unsubscribe from this group and stop receiving emails from it, send an email to runtimecompiledcpl...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "Runtime-Compiled C++" group.
To unsubscribe from this group and stop receiving emails from it, send an email to runtimecompiledcpl...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages