#dbugfix 17592

Mike Franklin slavo5150 at yahoo.com
Wed Mar 21 08:49:11 UTC 2018


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


More information about the Digitalmars-d mailing list