| |
| Posted by vit in reply to Bienlein | PermalinkReply |
|
vit
Posted in reply to Bienlein
| On Thursday, 19 August 2021 at 08:25:23 UTC, Bienlein wrote:
> On Thursday, 19 August 2021 at 07:30:38 UTC, Bienlein wrote:
> Hello,
I allocate some instance of class C manually and then free the memory again:
class C {
int num;
~this() {
writeln("~this");
}
}
void foo() // @nogc
{
auto mem = cast(C)malloc(__traits(classInstanceSize, C));
auto c = emplace!(C)(mem);
c.num = 789;
destroy(c);
free(cast(void*) c);
c = null;
}
int main()
{
foo();
}
The code above works well as the destructor of c in class C is called by destroy. Problem is that destroy cannot be used once function foo is annotated with @nogc. There seems to be no way round it.
What I want is to keep the function foo annotated with @nogc, but still have the destructor of C be called before free is called. Is there a way to call the destructor through meta programming or some kind of reflection so that I can create some generic function that calls the destructor and then free for any kind of class?
Thanks, Bienlein
Oops, I just realized that you can also not call emplace when @nogc is present. Well that is at least consistent with not either being able to call destroy ;-).
So, I guess this means that you can forget about manually allocating and freeing some instance of a class and using @nogc as well. That's a pitty, @nogc was a good idea.
Try this:
import std;
import core.stdc.stdlib : malloc, free;
class C {
int num;
~this() @nogc{
debug writeln("~this");
}
}
void foo() @nogc
{
auto mem = cast(C)malloc(__traits(classInstanceSize, C));
auto c = emplace!(C)(mem);
c.num = 789;
destruct(c);
free(cast(void*) c);
c = null;
}
void main()
{
foo();
}
//https://github.com/atilaneves/automem/blob/master/source/automem/utils.d
void destruct(T)(T obj) if (is(T == class)) {
(cast(_finalizeType!T) &rt_finalize)(() @trusted { return cast(void*) obj; }());
}
private extern(C){
void rt_finalize(void* p, bool det = true) @trusted @nogc pure nothrow;
template _finalizeType(T) {
import std.traits: Unqual;
static if (is(Unqual!T == Object)) {
alias _finalizeType = typeof(&rt_finalize);
} else {
import std.traits : BaseClassesTuple;
import std.meta : AliasSeq;
alias _finalizeType = typeof(function void(void* p, bool det = true) {
// generate a body that calls all the destructors in the chain,
// compiler should infer the intersection of attributes
foreach (B; AliasSeq!(T, BaseClassesTuple!T)) {
// __dtor, i.e. B.~this
static if (__traits(hasMember, B, "__dtor"))
() { B obj; obj.__dtor; } ();
// __xdtor, i.e. dtors for all RAII members
static if (__traits(hasMember, B, "__xdtor"))
() { B obj; obj.__xdtor; } ();
}
});
}
}
}
|