Problems using malloc in D language: why writeln call the destructor twice in this example

Viewed 82

I am trying to write a D wrapper for a C library (libmpdec) that stores its data using the standard C malloc function. But the are some nasty bugs in my programs that I don't know how to solve. So I have written the following test example, trying to understand this. The idea is to create structure holding a pointer to a memory area allocated using malloc in the constructor and that contains a zero-terminated C string, and free the area using the destructor. Also I can print the string using printf. The problem arises when I try to implement a method toString() so that I can use the standard D function writeln. For some reason that I don't understand the destructor seems to be called twice! (one after writeln) and so a segmentation fault occurs.

import std.stdio;
import core.stdc.stdlib;
import std.string;
import core.stdc.string;

struct Prueba { 

  char* pointer;
  string name;

  this(string given_name)
  {
    writeln("calling the constructor");
    pointer= cast (char*) malloc(char.sizeof*10);
  name=given_name;

    char* p= pointer;
    *p= 'a';
    p++;
    *p= 'b';
    p++;
    *p= '\n';
    p++;
    *p= '\0';
  }

  ~this()
  {
    writeln("\n calling the destructor");
    free(pointer);
  }

  void print()
  {
    printf("Using printf %s \n",pointer); 
  }

  
  string toString()
  {
    ulong len=strlen(pointer);
    return cast(string) pointer[0..len];      
   }

}


void main()
{
  writeln("version 1");
  Prueba p=Prueba("a");
  writeln("using writeln ",p);

  p.print();
}

But if I store the result in a string varible like

string s=p.toString();
 writeln("using writeln ",s);

The program just works! I cannot figure out why!

You can see both versions of my test program at

https://github.com/pdenapo/example_programs_in_D/tree/master/using_malloc

Many thanks for any help!

Update: It seems that writeln plays no role here. And I can get the same result with something like

void probando(Prueba q)
{
    q.print();
}

    
probando(p);

The problem seems to be that a copy of p is created when calling a function.

1 Answers

In cases like this, it's often a good idea to see if it's the same instance being destroyed. Adding &this to the writeln calls, I get this output:

version 1                            
calling the constructor at 6FBB70F960
Instance on stack: 6FBB70F960        
using writeln ab                     
                                     
 calling the destructor at 6FBB70F820
                                     
 calling the destructor at 6FBB70F7F0

As we can see, the pointers are different, so there's two instances.

D structs are value types, and so are copied and moved. When you call a function taking a class parameter, a pointer is what's actually being passed, and it basically says 'the class instance you're looking for is over there'. With structs a copy is created, and suddenly you have two independent objects living their separate lives.

Of course, that's not what you want - Prueba isn't actually a copyable type, since having two copies will result in two calls to the destructor, and thus double freeing. To mark it as non-copyable, simply add @disable this(this); to disable the postblit, and the compiler will helpfully throw error messages at you when a copy would be created.

This will cause a compiler error on the writeln line, and you will have to manually call toString, e.g.: writeln("using writeln ", p.toString());

Note that a non-copyable struct may be passed to functions as ref, since that doesn't create a new copy. We can't really modify writeln to do that, but it's worth knowing for your own functions.

Related