#1163 c++ like destructor

vladakk Thu 29 Jul 2010

A few programmers of us who are doing c++ and now work in Java have a big problem, because Java does not and will not implement the c++ like destructor logic. Therefore, we began to study the Fantom and think that this language has the greatest chance for doing it! Our basic question is: Is there a plan for the destructor implementation in the Fantom language?

We are ready to help in any kind of implementation logic as long as suitable plugin for Fantom will be operational. (We tried NB, Eclipse, IntelliJ, ...)

Regards from Belgrade, Serbia Vladimir Kokovic, Dragan Ciric

alexlamsl Thu 29 Jul 2010

Would you mind explaining the difference between your proposed destructor and java.lang.Object.finalize?

vladakk Thu 29 Jul 2010

The destructor method fulfills the opposite to constructor method functionality. It is automatically called when its scope of existence has finished (for example, if it was defined as a local object within a function and the function ends)

Regards from Belgrade, Serbia, Vladimir Kokovic, Dragan Ciric

jodastephen Thu 29 Jul 2010

So what you want is the compiler to auto-generate a call to the destructor method as the variable goes out of scope?

// Fantom code
{
  person := Person()
  // do something
}

// compiled to equivalent Java code (compilation actually goes to fcode then bytecode)
{
  Person person = new Person()
  // do something
  person.destructor()
}

Personally, I've never needed such a concept, so wouldn't be entusiastic at adding it. However, I'd love to hear your use case for why it is so necessary.

vladakk Thu 29 Jul 2010

Yes, your code is correct.

Consider this c++ class:

class vkSQLbeginTrans {

protected:
  bool commited;
public:
  vkSQLbeginTrans( bool commitNeeded = true) : commited( !commitNeeded ) {}
  ~vkSQLbeginTrans() {
    if ( !commited && ::vkSQLcon && ((long)::vkSQLcon != -1) && ::vkSQLcon->getCon() )
      vkSQLodbc::rollbackAll();
  }
  void commit() {
    if ( !commited && ::vkSQLcon && ((long)::vkSQLcon != -1) && ::vkSQLcon->getCon() ) {
      vkSQLodbc::commitAll();
      commited = true; 
    }
  }

};

and usage of this class:

{

vkSQLbeginTrans myBET;
// do something
// if some exception occurred then destructor will call rollback !!!
myBET.commit();

}

This is rock solid and simple code!

Regards from Belgrade, Serbia, Vladimir Kokovic, Dragan Ciric

katox Thu 29 Jul 2010

In Fantom (as in Java) there are no local objects (stack allocated). Everything is allocated on heap (equivalent of Fred* p = new Fred(); /* ...*/ delete p;). Even in C++ there would be no automatic destructor call at the end of the declaring scope in such a case (it can be done only manually and that would cause double delete bug later).

In Fantom (and in Java) all objects might live well outside declaration scope and cease to exist only when the garbage collector found no references on them. That is the point when finalize() would be called in Java. However this might not ever happen (unless the app is shut down) so it is not that useful and that's why Fantom Obj type has no such method.

If you added automatic destructor calls at the end of the declaring scope you have to be sure that constructed objects couldn't be referenced later. Otherwise this classic C++ situation could happen (in Fantom syntax):

class A {
   Fred[] freds := Fred[,]

   Void foo()
   {
     fred := Fred() // construct a local variable
     fred.use()
     freds.add(fred)

     fred.destructor() // this would be the automatic destructor call
     // fred is now uninitialized
   }

   Void bar()
   {
     fred := freds.get(0) // retrieve a reference
     fred.use // BOOOM! Used an uninitialized object!
   }

   static Void main() {
     a := A()
     a.foo
     b.foo // oops!
   }
}

Checking for possible reference leaks to any objects created would be incredibly expensive. With GC in mind it is always better to utilize try-catch-finally or some sort of eventing to achieve the same effect.

andy Thu 29 Jul 2010

You probably want some similar to C#'s IDisposable and using statements:

using (res = SomeResource())
{
  // do stuff with res
} // res.dispose automatically called at end of scope

I believe dispose is called also in the event of an exception - so its basically just syntax sugar for a try-catch-finally - so there is still a responsibility on the developer to use it correctly.

vladakk Thu 29 Jul 2010

We are not talking about object destroy. We need only automatic call for some method at scope end.

Regards from Belgrade, Serbia, Vladimir Kokovic, Dragan Ciric

helium Thu 29 Jul 2010

C++ like destructors don't work well in a language that has closures (like Fantom) as any variable could possibly get captured and thus its life would be extended.

To solve your transaction problem you can use first class functions to your advantage. You have a beginTransaction function that takes another function as argument. The function starts a transaction, executes the passed function inside a try-catch block and commits the transaction when no exception was thrown and aborts it otherwise (alternately you could let the passed in function return a success/failure value depending on which the transaction gets committed or aborted respectively).

beginTransaction |->| {
    ... // the transactional code
}

This would have the same effect as your C++ code

{
    vkSQLbeginTrans foo;
    ... // the transactional code
    foo.commit();
}

but looks cleaner and is more concise.

tactics Thu 29 Jul 2010

The destructor method fulfills the opposite to constructor method functionality. It is automatically called when its scope of existence has finished (for example, if it was defined as a local object within a function and the function ends)

This is called escape analysis.

If I remember correctly, there's no efficient procedure for doing this. The best you can get are approximations, meaning you can't guarantee the destructor is always called.

brian Fri 30 Jul 2010

Just to re-iterate what I think has already been summed up:

  • C++ destructors as used for memory management don't really apply to JVM which has GC
  • C++ notion of scope based on stack allocated variables doesn't really apply to the JVM since everything is heap allocated and doesn't apply to Fantom really well since features like closures don't cleanly fit into stack scope
  • the the Java world, code to force cleanup of a resource is best done in a finally block
  • in Fantom we can hide the setup/cleanup behind a method really elegantly with closures

vladakk Mon 2 Aug 2010

After all, can we ask the question: Is there a chance that the compiler generate a call for some method at the moment of variable scope end?

helium Mon 2 Aug 2010

Where exactly does the scope of a variable end that got captured by a closure?

Perhaps you want something like the 'scope' keyword from D?

vladakk Mon 2 Aug 2010

Thank you helium, we have completely forgotten about the D language. In our opinion, Fantom could accept both ideas from the D language!

brian Mon 2 Aug 2010

The core feature to handle these cases is try/finally blocks.

The scope feature in D looks cool (don't remember seeing that before when perusing D).

Essentially use of closures, D scope, C# using blocks are all syntax sugar for making finally easier to read since it tends to obscure code. What we have said in the past was that if we didn't have "control block closures" that we would consider adding something like C# using blocks (or maybe D like scopes). The final result of the closure design was it-blocks which sort of can be used by control blocks, but we might still want to consider other sugar (probably not for 1.0, but for 1.1).

Login or Signup to reply.