A runtime compiler for libcinder.
Previous implementations have moved to msvc_old and mac_cling branches.
Runtime is a developement tool that enables fast iteration and prototyping through c++ code hot-swapping. It is lightweight, use your native compiler and is integrated with your IDE and debugger. Runtime compilation happens as seamlessly as possible using your IDE error report system and debugger tools.
One of the core principle of this library is to be able to disappear completly when you don't need it. Every pieces of Runtime are designed to become no-op when building in Release
or Debug
mode. There's a very light setup code to get things running and all of it disappear as soon as you switch to a non runtime target. This makes it easier to integrate Runtime in production without the fears and investments of big dependencies.
The easiest way to get a class
watched by the runtime compiler is to use the following macros in both your header and implementation file:
MyClass.h
#pragma once
#include "runtime/ClassWatcher.h"
class MyClass {
public:
MyClass();
RT_DECL
};
MyClass.cpp
#include "MyClass.h"
MyClass::MyClass()
{
}
RT_IMPL( MyClass );
From that point, any instance of the class MyClass
allocated on the heap will be automatically reloaded when saving the header or the implementation file. Which means that you can use any flavor of smart or raw pointer (the only exception being std::make_shared
... more on that later).
SomeOtherFile.cpp
#include "MyClass.h"
std::unique_ptr<MyClass> mPtr;
void initialize()
{
// mPtr will point to the last compiled version of the class
mPtr = std::make_unique<MyClass>();
}
Now you probably want to call MyClass
member functions from outside the class and have any future iteration of the code being use instead of the old one. To be able to do this you need to make those member functions virtual
and let your compiler handle the new indirection:
#pragma once
#include "runtime/ClassWatcher.h"
class MyClass {
RT_DECL
public:
MyClass();
virtual void draw();
};
You probably don't want to deploy code with unecessary virtual calls everywhere. To avoid too much code polution rt_virtual
can be used instead of virtual
as it becomes a no-op when building the code in Release
or Debug
:
#pragma once
#include "runtime/ClassWatcher.h"
#include "runtime/Virtual.h"
class MyClass {
RT_DECL
public:
MyClass();
rt_virtual void draw();
};
When working on a header only class RT_IMPL_INLINE
can be used instead of the RT_DECL
/ RT_IMPL
pair:
#pragma once
#include "runtime/ClassWatcher.h"
#include "runtime/Virtual.h"
class MyClass {
RT_IMPL_INLINE( MyClass );
public:
MyClass() {}
rt_virtual void draw()
{
// ...
}
};
Unfortunately std::make_shared
works differently under the hood, making a seemless integration more difficult. At the moment the only way to make a std::shared_ptr
runtime reloadable is to use the operator new
or the following wrapper :
#include "runtime/make_shared.h"
rt::make_shared<MyClass>( ... );
TODO
TODO
Build the x64
Debug_Shared
and Release_Shared
targets of Cinder.
-
Create new configuration for
Debug_Shared
andRelease_Shared
(use "copy setting from" in the configuration manager)
-
Project Properties / C++ / Code Generation
: change Runtime Library to/MDd
forDebug_Shared
and/MD
forRelease_Shared
.
-
Project Properties / C++ / Preprocessor / Preprocessor Definitions
: AddCINDER_SHARED
for bothDebug_Shared
andRelease_Shared
.
-
Project Properties / Build Events / Post-Build Event / Command Line
: Pastexcopy /y /d "PATH_TO_CINDER\lib\msw\$(PlatformTarget)\$(Configuration)\$(PlatformToolset)\cinder.dll" "$(OutDir)"
and replacePATH_TO_CINDER
by the relative path to Cinder (that you can find for example insideC++ / General / Additional Include Directories
.
Ignore the following steps if using Tinderbox.