On Tuesday, 20 March 2018 at 21:27:53 UTC, 12345swordy wrote:
This is very important to me as I am very interested in using
the language for game development.
Yes I know that it's marked as "Duplicated", but I strongly
disagree as it is different enough to consider is own issue.
Alex
https://issues.dlang.org/show_bug.cgi?id=17592
Yeah that's pretty poopy. I looked into this and I think this is
fixable. Here's my proof of concept:
-----------------
import core.stdc.stdio;
import std.traits;
/*******************************************************************
* This code was copied right out of druntime and
* attributed with @nogc
*******************************************************************/
extern (C) void rt_finalize(void *data, bool det=true) @nogc;
void destroy(T)(T obj) @nogc if (is(T == class))
{
rt_finalize(cast(void*)obj);
}
/*******************************************************************
* This emplace implementation below was copied
* right out of std.conv and attributed with @nogc
*******************************************************************/
@nogc pure nothrow @safe
void testEmplaceChunk(void[] chunk, size_t typeSize, size_t
typeAlignment, string typeName)
{
assert(chunk.length >= typeSize, "emplace: Chunk size too
small.");
assert((cast(size_t) chunk.ptr) % typeAlignment == 0,
"emplace: Chunk is not aligned.");
}
T emplace(T, Args...)(T chunk, auto ref Args args) @nogc
if (is(T == class))
{
static assert(!isAbstractClass!T, T.stringof ~
" is abstract and it can't be emplaced");
// Initialize the object in its pre-ctor state
enum classSize = __traits(classInstanceSize, T);
(() @trusted => (cast(void*) chunk)[0 .. classSize] =
typeid(T).initializer[])();
static if (isInnerClass!T)
{
static assert(Args.length > 0,
"Initializing an inner class requires a pointer to
the outer class");
static assert(is(Args[0] : typeof(T.outer)),
"The first argument must be a pointer to the outer
class");
chunk.outer = args[0];
alias args1 = args[1..$];
}
else alias args1 = args;
// Call the ctor if any
static if (is(typeof(chunk.__ctor(args1))))
{
// T defines a genuine constructor accepting args
// Go the classic route: write .init first, then call ctor
chunk.__ctor(args1);
}
else
{
static assert(args1.length == 0 && !is(typeof(&T.__ctor)),
"Don't know how to initialize an object of type "
~ T.stringof ~ " with arguments " ~
typeof(args1).stringof);
}
return chunk;
}
T emplace(T, Args...)(void[] chunk, auto ref Args args) @nogc
if (is(T == class))
{
enum classSize = __traits(classInstanceSize, T);
testEmplaceChunk(chunk, classSize, classInstanceAlignment!T,
T.stringof);
return emplace!T(cast(T)(chunk.ptr), args);
}
/*******************************************************************
* This code was copied from
https://wiki.dlang.org/Memory_Management#Explicit_Class_Instance_Allocation
* and attributed with @nogc
*******************************************************************/
class TestClass
{
int x;
this(int x) @nogc
{
puts("TestClass's constructor called");
this.x = x;
}
~this() @nogc
{
puts("TestClass's destructor called");
}
}
T heapAllocate(T, Args...) (Args args) @nogc
{
import core.stdc.stdlib : malloc;
import core.memory : GC;
// get class size of class instance in bytes
auto size = __traits(classInstanceSize, T);
// allocate memory for the object
auto memory = malloc(size)[0..size];
if(!memory)
{
import core.exception : onOutOfMemoryError;
onOutOfMemoryError();
}
puts("Memory allocated");
// notify garbage collector that it should scan this memory
GC.addRange(memory.ptr, size);
// call T's constructor and emplace instance on
// newly allocated memory
return emplace!(T, Args)(memory, args);
}
void heapDeallocate(T)(T obj) @nogc
{
import core.stdc.stdlib : free;
import core.memory : GC;
// calls obj's destructor
destroy(obj);
// garbage collector should no longer scan this memory
GC.removeRange(cast(void*)obj);
// free memory occupied by object
free(cast(void*)obj);
puts("Memory deallocated");
}
void main() @nogc
{
// allocate new instance of TestClass on the heap
auto test = heapAllocate!TestClass(42);
scope(exit)
{
heapDeallocate(test);
}
printf("test.x = %d\n", test.x);
}
-------------
Step 1. Make `emplace` @nogc
So we need to attribute `std.conv.emplace` as @nogc. Based on
the code above, that looks feasible. The difficulty, though will
be writing thorough tests for it.
Step 2. Make `destroy` @nogc
`destroy` simply calls `rt_finalize` in the runtime, at least for
classes. I declared it as `@nogc` in the code above, but that's
a cheat, though I think `rt_finalize` can be made `@nogc` in the
runtime.
I'll try to do step 2. If someone wants to help with step 1,
that would be great.
Mike