-
Notifications
You must be signed in to change notification settings - Fork 32
Working in a @nogc environment
Probably the largest hurdle to cross when learning Dplug is getting used to working without the runtime enabled. It may be confusing or even a little frustrating at first, but once you learn a few key tricks, it's a breeze. Here are some important things to remember when writing Plug-ins with Dplug.
Dplug does not use the -betterC
switch, but instead lets the runtime be disabled.
This allows us to get back most features of the language, except for:
- Heap closures
- Module constructors and destructors:
static this()
,static ~this()
shared static this()
andshared static ~this()
- TLS variables
- Built-in associative arrays (replace them with
dplug.core.map
)
No runtime enabled implies no GC, however the GC's code is still here: if a GC allocation happens, the program will either crash or spinlock endlessly. @nogc
effectively helps prevent that, and is used everywhere, along with nothrow
eventually.
Slices can be allocated using malloc
/free
, and the functions mallocSlice
, mallocSliceNoInit
, and freeSlice
provide wrappers to do just that.
Example:
float[] myBuf = mallocSliceNoInit(128);
// later
freeSlice(myBuf);
Another even more handy way to allocate @nogc
arrays is to use reallocBuffer
. This function also takes an alignment.
Example:
float[] myAudioBuf;
myAudioBuf.reallocBuffer(frames, 16); // takes optional alignment (default = 1)
// later
myAudioBuf.reallocBuffer(0, 16); // must repeat alignment
-
Dynamic array: You'll find a
Vec!T
indplug.core.alignedbuffer
. -
Map: You'll find a
Map!T
b-tree based map indplug.core.map
. -
Set: You'll find a
Set!T
dynamic set indplug.core.map
.
Dplug generally uses malloc
throughout, however some constructs use alignedMalloc
, alignedRealloc
and alignedFree
.
Don't mix and match memory allocated with malloc
and memory allocated with alignedMalloc
: some additional bytes are used.
Since there is no runtime enabled (hence no GC) the keyword new
cannot be used to allocate memory for classes. Instead, classes must be allocated using auto mallocNew(T, Args...)(Args args)
.
Once you are finished using the class, you may deallocate it using void destroyFree(T)(T p)
Example:
class Foo
{
public:
this(int i)
{
f = i;
}
private:
int f;
}
// Create a new object of type foo and pass 3 to its constructor.
Foo foo = mallocNew!Foo(3);
// Done using the class so the memory can be freed.
foo.destroyFree();
See: https://p0nce.github.io/d-idioms/#@nogc-Array-Literals:-Breaking-the-Limits
If you need an exception in runtime-free D, use the following construct:
Example:
try
{
if (error)
throw mallocNew!Exception("An error message without format() call");
}
catch(Exception e)
{
// do something with e
e.destroyFree();
}
- Thread-Local Storage (TLS) can't be used without the D runtime
- Global constructors/destructors (
static this
andstatic ~this
)
Example:
// At top-level
module mymodule;
int myTLSvar; // NO, can't have TLS variables
shared static this() // NO, can't have static constructors
{
doSomething();
}
One disadvantage of using @nogc is that much of the standard library is off limits. If you are unsure of what you can and can't use, you can check the documentation here Phobos Documentation, if the function is nothrow @nogc
, then it can be used. Most of the core
modules can be used, and everything in core.stdc
is nothrow @nogc
.
Note that template functions have their attributes inferred most of the time: https://p0nce.github.io/d-idioms/#Automatic-attribute-inference-for-function-templates
Beware of the C locale!
In a plugin, the host could change the C locale. It is therefore a trap to call printf
/scanf
/strtod
/atof
and friends directly!
See dplug.core.string
for alternatives to parse numbers.
When printing numbers, there is no solution yet.
An exception to these rules is when using CTFE(Compile-Time Function Evaluation). Since the D runtime is available during compile time, it is possible to use every feature using the GC: new
, heap closures, array literals, GC slices, etc.
Quite ironically, compile-time code doesn't have the restrictions discussed in this very article. It might well be easier to parse something at compile-time than runtime in Dplug!
Another exception is host applications - using dplug:host
- will have the runtime enabled.
In general, nothrow @nogc
code can be used both by regular code using the runtime and nothrow @nogc
code.
Note: Efforts to enable back the runtime have always failed unfortunately. See: https://github.com/AuburnSounds/Dplug/issues/292