How to know which module was recompiled?

85 views
Skip to first unread message

Julien Guertault

unread,
Dec 23, 2015, 4:54:23 AM12/23/15
to Runtime-Compiled C++
Hello,

I recently discovered RCC++ and decided to try and integrate it into my codebase. I don't have a full understanding of its design yet so please bear with me.

I understand there are different ways to use it, one of them being referred to as Runtime Modifiable Classes, in which case we can be notified of code being updated by implementing IObjectFactoryListener::OnConstructorsAdded(). I quickly wrote such an implementation based on the ConsoleExample for a single dummy class, and had the system successfully detect a modification and recompile the file, after which I could update the pointer to a new instance of the updated class.

What is not clear to me though, is how to know which class was recompiled in case there are several of them.

I am interested in using dynamic recompilation for classes that perform a significant amount of work during initialization. If one of them is modified, I would like to re-instantiate and initialize only that one, and avoid reloading all others. At the moment though I don't find in the API how to know which class was recompiled. Is this possible?

Regards,

Doug Binks

unread,
Dec 23, 2015, 8:43:58 AM12/23/15
to Runtime-Compiled C++
Hi Julien,

Simple answer is that IObject has an Init function which you should use for work during initialization. This should be called with isFirstInit set to true when you want to intialize, and during a reload is called with isFirstInit set to false. Then you use the Serialize functionality to move data from old to new objects. For more see below.

The larger example SimpleTest (also referred to as the Pulse demo) shows how using mulitple classes works, and looking at the code in the function ObjectFactorySystem::ProtectedObjectSwapper::ProtectedFunc() shows how the swapping of objects is handled.

After new code is loaded, the following happens (for now not mentioning auto constructed singletons):

  1. Serialize() is called on all runtime objects (IObjects) to save out state.
  2. New objects are constructed if needed.
  3. Serialize() is called on all runtime objects to load back state.
  4. Init( isFirstInit  = false ) is called on all objects - false meaning this is not a 'first' init.
  5. Old objects are deleted, with variable _isRuntimeDelete set to true so IsRuntimeDelete() is known.
At the moment there is no way of knowing in step 1 whether the object being serialized is going to be swapped for new code. This could be added (it would make sense to do so, so if you file a request on github I can get round to this in a week or so.

To avoid serializing out large amounts of state, I frequently just serialize out a pointer to large data structures (don't swap 10Mb, just swap a 4 or 8 byte pointer to the 10Mb). This does require a virtual dtor for that data since pointers may to to memory in the heap of another dll's allocator.

You can know if you if your object is new from 3 on by serializing the pointer to the object and then checking against your own (as mentioned above an API for this would be a good idea).

I won't be able to do much for a week or so on RCC++, but I would like improve the documentation and functionality in this area to make it easier to do what you're asking.

Cheers,

Doug.

Julien Guertault

unread,
Jan 24, 2016, 12:23:22 AM1/24/16
to Runtime-Compiled C++
Hello Doug,

In fine it was a lot simpler than I thought: comparing the old and new instance address upon compilation seems to be all I needed.

Based on your sample code I wrote a small wrapper that looks like this (in case this is useful to anyone):

bool RCCHelper::UpdateCompiledCode(const InterfaceID& interfaceId, void** instance, const ObjectId& objectId) const
{
   
assert(instance != NULL && *instance != NULL);

   
const void* previousInstance = *instance;
   
IObject* pObj = m_runtimeObjectSystem->GetObjectFactorySystem()->GetObject(objectId);
    pObj
->GetInterface(interfaceId, (void**)instance);
   
if (*instance == NULL)
   
{
       
delete pObj;
        m_logger
->LogError("No interface found for %s.", pObj->GetTypeName());
   
}
   
else if (previousInstance != *instance)
   
{
       
m_logger->LogInfo("%s's code has changed.", pObj->GetTypeName());
       
return true;
   
}
   
return false;
}

I could serialize the instance pointer like you suggest, but in my case I could get away without it so...

Cheers,

Doug Binks

unread,
Jan 24, 2016, 1:43:43 PM1/24/16
to Runtime-Compiled C++
Glad you found a solution!

--
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.

Arnold Lágler

unread,
May 8, 2017, 12:30:57 PM5/8/17
to Runtime-Compiled C++
This does require a virtual dtor for that data since pointers may to to memory in the heap of another dll's allocator.

Can you please elaborate on this? I suppose you meant "point to" instead of "to to". I've read that this could only be a problem if we compile the dll with a different version of Visual Studio than the exe.
If not, don't they use the same allocator? (Not expert on c++ BTW so sorry it this is a noob question - the whole topic of "pointers accross" dlls confuses me a great deal). 
Thanks in advance,
Arnold

Doug Binks

unread,
May 8, 2017, 1:03:14 PM5/8/17
to Runtime-Compiled C++
Yes, I meant "This does require a virtual dtor for that data since pointers may point to memory in the heap of another dll's allocator."

You might be able to get away with using a non-virtual dtor if you use the same CRT as the one RCC++ compiles with (/MD - multithread-specific and DLL-specific version of the run-time library. ).

Arnold Lágler

unread,
May 9, 2017, 4:43:15 AM5/9/17
to Runtime-Compiled C++
Thanks for the quick reply. I'm still a bit confused though. Is having a virtual destructor without a base class safe? Is it not subject to optimization? 
This post suggests that it is enough to have to virtual dtor and explains gives some explanation. Also, I have read in this thread that virtual dtros can be inlined. Can that cause some problems?
Also, I have seen that at the end of ObjectInterfacPerModule.h new and delete oparators are overridden to "have correct alignment". Isn't this an alternative.
I mean I don't have an exact question but rather how do all these issues com together?
Sorry that I couldn't ask a more specific question. Perhaps it is enough to have some safe guidelines but even better to understand the WHY.

Doug Binks

unread,
May 9, 2017, 5:09:05 AM5/9/17
to Runtime-Compiled C++
Having a destructor virtual doesn't pose any safety issues if the class has no parent.

The dtor is not subject to optimization if called through a pointer, & (in C++11) you haven't declared the virtual function as final in the type of the pointer.

Whilst the new and delete class operators have been overridden in ObjectInterfacePerModule.h, this only works for  derived types and will as this code is part of each module/dll it can still result in the wrong delete call being used.

RCC++ is designed for use whilst developing, so it should be safe to experiment. If you get crashes after a recompile when deleting an object consider a virtual dtor.

Arnold Lágler

unread,
May 9, 2017, 5:27:37 AM5/9/17
to Runtime-Compiled C++
Whilst the new and delete class operators have been overridden in ObjectInterfacePerModule.h, this only works for  derived types and will as this code is part of each module/dll it can still result in the wrong delete call being used.

I meant using a similar technique for the aforementioned class that has a 10M array in it. Wouldn't that work too?

Doug Binks

unread,
May 9, 2017, 5:37:53 AM5/9/17
to Runtime-Compiled C++
The only reliable techniques here are to use a virtual destructor or to store a pointer to a delete function which is in the same module the constructor was called from and use that. Of the two, in C++ the virtual destructor is the easiest and best approach for large objects.

Arnold Lágler

unread,
May 9, 2017, 5:39:17 AM5/9/17
to Runtime-Compiled C++
Thanks!

Doug Binks <do...@enkisoftware.com> ezt írta (időpont: 2017. máj. 9., K, 11:37):
The only reliable techniques here are to use a virtual destructor or to store a pointer to a delete function which is in the same module the constructor was called from and use that. Of the two, in C++ the virtual destructor is the easiest and best approach for large objects.

--
You received this message because you are subscribed to a topic in the Google Groups "Runtime-Compiled C++" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/runtimecompiledcplusplus/R_Ta1Lf4qoE/unsubscribe.
To unsubscribe from this group and all its topics, send an email to runtimecompiledcpl...@googlegroups.com.

Arnold Lágler

unread,
May 9, 2017, 5:39:49 AM5/9/17
to Runtime-Compiled C++
Thank you!
Reply all
Reply to author
Forward
0 new messages