Thread overview
Need help to compile code with traits
Feb 05, 2017
Xavier Bigand
Feb 05, 2017
Basile B.
Feb 05, 2017
Xavier Bigand
February 05, 2017
Hi,

I am trying to create an allocator that don't use the GC, and I have issues for the initialization of member before calling the constructor.
Here is my actual code :
> mixin template NogcAllocator(T)
> {
> 	static T	nogcNew(T, Args...)(Args args) @nogc
> 	{
> 		import core.stdc.stdlib : malloc;
> 		import std.traits;
>
> 		T	instance;
>
> 		instance = cast(T)malloc(__traits(classInstanceSize, T));
> 		foreach (string member; __traits(allMembers, T))
> 		{
> 			static if (isType!(__traits(getMember, T, member)))
> 				__traits(getMember, instance, member) = typeof(__traits(getMember, T, member)).init;
> 		}
>
> 		instance.__ctor(args);
> 		return instance;
> 	}
>
> 	static void	nogcDelete(T)(T instance) @nogc
> 	{
> 		import core.stdc.stdlib : free;
>
> 		instance.__dtor();
> 		free(instance);
> 	}
> }
>
> unittest
> {
> 	struct Dummy {
> 		int field1 = 10;
> 		int field2 = 11;
> 	}
>
> 	class MyClass {
> 		mixin NogcAllocator!MyClass;
>
> 		int a = 0;
> 		int[] b = [1, 2, 3];
> 		Dummy c = Dummy(4, 5);
>
> 		int d = 6;
>
> 		this() @nogc {
> 		}
>
> 		this(int val) @nogc {
> 			d = val;
> 		}
> 	}
>
> 	MyClass first = MyClass.nogcNew!MyClass();
> 	MyClass second = MyClass.nogcNew!MyClass(7);
>
> 	assert(first.a == 0);
> 	assert(first.b == [1, 2, 3]);
> 	assert(first.c.field1 == 4);
> 	assert(first.d == 6);
>
> 	assert(second.c.field1 == 4);
> 	assert(second.d == 7);
> }


And the compilation errors :
> ..\src\core\nogc_memory.d(16): Error: no property 'this' for type 'core.nogc_memory.__unittestL39_3.MyClass'
> ..\src\core\nogc_memory.d(17): Error: type Monitor is not an expression
> ..\src\core\nogc_memory.d(63): Error: template instance core.nogc_memory.__unittestL39_3.MyClass.NogcAllocator!(MyClass).nogcNew!(MyClass) error instantiating
> ..\src\core\nogc_memory.d(16): Error: no property 'this' for type 'core.nogc_memory.__unittestL39_3.MyClass'
> ..\src\core\nogc_memory.d(17): Error: type Monitor is not an expression
> ..\src\core\nogc_memory.d(64): Error: template instance core.nogc_memory.__unittestL39_3.MyClass.NogcAllocator!(MyClass).nogcNew!(MyClass, int) error instantiating

I don't understand my mistake with the getMember and isType traits.
And I am curious about of what is the Monitor.
February 05, 2017
On Sunday, 5 February 2017 at 14:59:04 UTC, Xavier Bigand wrote:
> Hi,
>
> I am trying to create an allocator that don't use the GC, and I have issues for the initialization of member before calling the constructor.
> Here is my actual code :
>> mixin template NogcAllocator(T)
>> {
>> 	static T	nogcNew(T, Args...)(Args args) @nogc
>> 	{
>> 		import core.stdc.stdlib : malloc;
>> 		import std.traits;
>>
>> 		T	instance;
>>
>> 		instance = cast(T)malloc(__traits(classInstanceSize, T));
>> 		foreach (string member; __traits(allMembers, T))
>> 		{
>> 			static if (isType!(__traits(getMember, T, member)))
>> 				__traits(getMember, instance, member) = typeof(__traits(getMember, T, member)).init;
>> 		}
>>
>> 		instance.__ctor(args);
>> 		return instance;
>> 	}
>>
>> 	static void	nogcDelete(T)(T instance) @nogc
>> 	{
>> 		import core.stdc.stdlib : free;
>>
>> 		instance.__dtor();
>> 		free(instance);
>> 	}
>> }
>>
>> unittest
>> {
>> 	struct Dummy {
>> 		int field1 = 10;
>> 		int field2 = 11;
>> 	}
>>
>> 	class MyClass {
>> 		mixin NogcAllocator!MyClass;
>>
>> 		int a = 0;
>> 		int[] b = [1, 2, 3];
>> 		Dummy c = Dummy(4, 5);
>>
>> 		int d = 6;
>>
>> 		this() @nogc {
>> 		}
>>
>> 		this(int val) @nogc {
>> 			d = val;
>> 		}
>> 	}
>>
>> 	MyClass first = MyClass.nogcNew!MyClass();
>> 	MyClass second = MyClass.nogcNew!MyClass(7);
>>
>> 	assert(first.a == 0);
>> 	assert(first.b == [1, 2, 3]);
>> 	assert(first.c.field1 == 4);
>> 	assert(first.d == 6);
>>
>> 	assert(second.c.field1 == 4);
>> 	assert(second.d == 7);
>> }
>
>
> And the compilation errors :
>> ..\src\core\nogc_memory.d(16): Error: no property 'this' for type 'core.nogc_memory.__unittestL39_3.MyClass'
>> ..\src\core\nogc_memory.d(17): Error: type Monitor is not an expression
>> ..\src\core\nogc_memory.d(63): Error: template instance core.nogc_memory.__unittestL39_3.MyClass.NogcAllocator!(MyClass).nogcNew!(MyClass) error instantiating
>> ..\src\core\nogc_memory.d(16): Error: no property 'this' for type 'core.nogc_memory.__unittestL39_3.MyClass'
>> ..\src\core\nogc_memory.d(17): Error: type Monitor is not an expression
>> ..\src\core\nogc_memory.d(64): Error: template instance core.nogc_memory.__unittestL39_3.MyClass.NogcAllocator!(MyClass).nogcNew!(MyClass, int) error instantiating
>
> I don't understand my mistake with the getMember and isType traits.
> And I am curious about of what is the Monitor.

The whole thing you do to initialize could be replaced by a copy of the initializer, which is what emplace does:


static T nogcNew(T, Args...)(Args args) @nogc
{
    import core.stdc.stdlib : malloc;
    import std.traits, std.meta;

    T	instance;
    enum s = __traits(classInstanceSize, T);

    instance = cast(T) malloc(s);
    (cast(void*) instance)[0..s] = typeid(T).initializer[];

    instance.__ctor(args);
    return instance;
}


Your nogcDelete() is bug-prone & leaky

- use _xdtor, which also calls the __dtor injected by mixin.
- even if you do so, __xdtors are not inherited !! instead dtor in parent classes are called by destroy() directly.

Currently what I do to simulate inherited destructor is to mix this for each new generation.

mixin template inheritedDtor()
{

private:

    import std.traits: BaseClassesTuple;

    alias B = BaseClassesTuple!(typeof(this));
    enum hasDtor = __traits(hasMember, typeof(this), "__dtor");
    static if (hasDtor && !__traits(isSame, __traits(parent, typeof(this).__dtor), typeof(this)))
        enum inDtor = true;
    else
        enum inDtor = false;

    public void callInheritedDtor(classT = typeof(this))()
    {
        import std.meta: aliasSeqOf;
        import std.range: iota;

        foreach(i; aliasSeqOf!(iota(0, B.length)))
            static if (__traits(hasMember, B[i], "__xdtor"))
            {
                mixin("this." ~ B[i].stringof ~ ".__xdtor;");
                break;
            }
    }

    static if (!hasDtor || inDtor)
    public ~this() {callInheritedDtor();}
}

When a dtor is implemented it has to call "callInheritedDtor()" at end of the dtor implementation.
February 05, 2017
Le 05/02/2017 à 18:32, Basile B. a écrit :
> On Sunday, 5 February 2017 at 14:59:04 UTC, Xavier Bigand wrote:
>> Hi,
>>
>> I am trying to create an allocator that don't use the GC, and I have
>> issues for the initialization of member before calling the constructor.
>> Here is my actual code :
>>> mixin template NogcAllocator(T)
>>> {
>>>     static T    nogcNew(T, Args...)(Args args) @nogc
>>>     {
>>>         import core.stdc.stdlib : malloc;
>>>         import std.traits;
>>>
>>>         T    instance;
>>>
>>>         instance = cast(T)malloc(__traits(classInstanceSize, T));
>>>         foreach (string member; __traits(allMembers, T))
>>>         {
>>>             static if (isType!(__traits(getMember, T, member)))
>>>                 __traits(getMember, instance, member) =
>>> typeof(__traits(getMember, T, member)).init;
>>>         }
>>>
>>>         instance.__ctor(args);
>>>         return instance;
>>>     }
>>>
>>>     static void    nogcDelete(T)(T instance) @nogc
>>>     {
>>>         import core.stdc.stdlib : free;
>>>
>>>         instance.__dtor();
>>>         free(instance);
>>>     }
>>> }
>>>
>>> unittest
>>> {
>>>     struct Dummy {
>>>         int field1 = 10;
>>>         int field2 = 11;
>>>     }
>>>
>>>     class MyClass {
>>>         mixin NogcAllocator!MyClass;
>>>
>>>         int a = 0;
>>>         int[] b = [1, 2, 3];
>>>         Dummy c = Dummy(4, 5);
>>>
>>>         int d = 6;
>>>
>>>         this() @nogc {
>>>         }
>>>
>>>         this(int val) @nogc {
>>>             d = val;
>>>         }
>>>     }
>>>
>>>     MyClass first = MyClass.nogcNew!MyClass();
>>>     MyClass second = MyClass.nogcNew!MyClass(7);
>>>
>>>     assert(first.a == 0);
>>>     assert(first.b == [1, 2, 3]);
>>>     assert(first.c.field1 == 4);
>>>     assert(first.d == 6);
>>>
>>>     assert(second.c.field1 == 4);
>>>     assert(second.d == 7);
>>> }
>>
>>
>> And the compilation errors :
>>> ..\src\core\nogc_memory.d(16): Error: no property 'this' for type
>>> 'core.nogc_memory.__unittestL39_3.MyClass'
>>> ..\src\core\nogc_memory.d(17): Error: type Monitor is not an expression
>>> ..\src\core\nogc_memory.d(63): Error: template instance
>>> core.nogc_memory.__unittestL39_3.MyClass.NogcAllocator!(MyClass).nogcNew!(MyClass)
>>> error instantiating
>>> ..\src\core\nogc_memory.d(16): Error: no property 'this' for type
>>> 'core.nogc_memory.__unittestL39_3.MyClass'
>>> ..\src\core\nogc_memory.d(17): Error: type Monitor is not an expression
>>> ..\src\core\nogc_memory.d(64): Error: template instance
>>> core.nogc_memory.__unittestL39_3.MyClass.NogcAllocator!(MyClass).nogcNew!(MyClass,
>>> int) error instantiating
>>
>> I don't understand my mistake with the getMember and isType traits.
>> And I am curious about of what is the Monitor.
>
> The whole thing you do to initialize could be replaced by a copy of the
> initializer, which is what emplace does:
>
>
> static T nogcNew(T, Args...)(Args args) @nogc
> {
>     import core.stdc.stdlib : malloc;
>     import std.traits, std.meta;
>
>     T    instance;
>     enum s = __traits(classInstanceSize, T);
>
>     instance = cast(T) malloc(s);
>     (cast(void*) instance)[0..s] = typeid(T).initializer[];
>
>     instance.__ctor(args);
>     return instance;
> }
>

Nice, thank you for that, it is much elegant ;-)

>
> Your nogcDelete() is bug-prone & leaky

Certainly I didn't think a lot about it for the moment.

>
> - use _xdtor, which also calls the __dtor injected by mixin.
> - even if you do so, __xdtors are not inherited !! instead dtor in
> parent classes are called by destroy() directly.
>
> Currently what I do to simulate inherited destructor is to mix this for
> each new generation.
>
> mixin template inheritedDtor()
> {
>
> private:
>
>     import std.traits: BaseClassesTuple;
>
>     alias B = BaseClassesTuple!(typeof(this));
>     enum hasDtor = __traits(hasMember, typeof(this), "__dtor");
>     static if (hasDtor && !__traits(isSame, __traits(parent,
> typeof(this).__dtor), typeof(this)))
>         enum inDtor = true;
>     else
>         enum inDtor = false;
>
>     public void callInheritedDtor(classT = typeof(this))()
>     {
>         import std.meta: aliasSeqOf;
>         import std.range: iota;
>
>         foreach(i; aliasSeqOf!(iota(0, B.length)))
>             static if (__traits(hasMember, B[i], "__xdtor"))
>             {
>                 mixin("this." ~ B[i].stringof ~ ".__xdtor;");
>                 break;
>             }
>     }
>
>     static if (!hasDtor || inDtor)
>     public ~this() {callInheritedDtor();}
> }
>
> When a dtor is implemented it has to call "callInheritedDtor()" at end
> of the dtor implementation.

Thank you a lot for this great help.