Abstract/Virtual destructor/method behaviour

For other topics related to the FreeBASIC project or its community.
fxm
Posts: 9304
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Abstract/Virtual destructor/method behaviour

Postby fxm » Dec 03, 2012 12:57

Extract of ABSTRACT documentation:
In order to call an abstract method, it must have been overridden and implemented by a derived data type, or else the program will abort.

Program of test to check aborting:

Code: Select all

Type Parent Extends Object
  Declare Abstract Sub abstract_regular ()
  Declare Abstract Sub abstract_empty ()
End Type

Type Child Extends Parent
  Declare Sub abstract_regular ()
End Type
Sub Child.abstract_regular ()
  Print "  Execute 'abstract_regular()' ", "in ", "    Child-type"
End Sub


Dim As Parent Ptr pc = New Child
Print "From Parent-type pointer to Child-type object:"
pc->abstract_regular()
pc->abstract_empty()
Delete pc
Sleep

Code: Select all

From Parent-type pointer to Child-type object:
  Execute 'abstract_regular()'            in                Child-type

Aborting due to runtime error 7 (null pointer access) at line 17 of d:\Documents
 and Settings\t0003830\Mes documents\FBIde0.4.6r4_fbc0.25.0\FBIDETEMP.bas::()


There is an ugly way to avoid program aborting when the abstract method was not overridden by a derived method accessible from the real object type:
- Just above the base type where abstract method is declared, a super-base type may be inserted containing a virtual method (with an error message body). A regular method (without specifier) does not prevent a program abort.
- It is the only case where an abstract method calling allows by polymorphism to go up in the inheritance hierarchy ('virtual' <- 'abstract'), as if the abstract method was not declared.

Code: Select all

Type _Object Extends Object
  Declare Virtual Sub abstract_regular ()
  Declare Virtual Sub abstract_empty ()
End Type
Virtual Sub _Object.abstract_regular ()
  Print "  ******* 'abstract_regular()' error body ", "in ", "_Object-type"
End Sub
Virtual Sub _Object.abstract_empty ()
  Print "  ******* 'abstract_empty()' error body ", "in ", "_Object-type"
End Sub


Type Parent Extends _Object
  Declare Abstract Sub abstract_regular ()
  Declare Abstract Sub abstract_empty ()
End Type

Type Child Extends Parent
  Declare Sub abstract_regular ()
End Type
Sub Child.abstract_regular ()
  Print "  Execute 'abstract_regular()' ", "in ", "    Child-type"
End Sub


Dim As Parent Ptr pc = New Child
Print "From Parent-type pointer to Child-type object:"
pc->abstract_regular()
pc->abstract_empty()
Delete pc
Sleep

Code: Select all

From Parent-type pointer to Child-type object:
  Execute 'abstract_regular()'            in                Child-type
  ******* 'abstract_empty()' error body   in            _Object-type

dkl, is that this behavior is wanted, or just a pure hazard?
Last edited by fxm on Dec 03, 2012 22:58, edited 1 time in total.
counting_pine
Site Admin
Posts: 6174
Joined: Jul 05, 2005 17:32
Location: Manchester, Lancs

Re: Abstract/Virtual destructor/method behaviour

Postby counting_pine » Dec 03, 2012 15:56

fxm wrote:Extract of ABSTRACT documentation:
In order to call an abstract method, it must have been overridden and implemented by a derived data type, or else the program will abort.

Could this problem be prevented by disallowing the creation of abstract classes at compile-time?
fxm
Posts: 9304
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Abstract/Virtual destructor/method behaviour

Postby fxm » Dec 03, 2012 17:29

counting_pine wrote:
fxm wrote:Extract of ABSTRACT documentation:
In order to call an abstract method, it must have been overridden and implemented by a derived data type, or else the program will abort.

Could this problem be prevented by disallowing the creation of abstract classes at compile-time?

No because at compile-time, the compiler knows only the pointer type and not the real type of object. Consequently it cannot check if exists an overriding body between pointer type and real type in the inheritance hierarchy.
MOD
Posts: 555
Joined: Jun 11, 2009 20:15

Re: Abstract/Virtual destructor/method behaviour

Postby MOD » Dec 03, 2012 17:33

In other languages you're not allowed to make a instance of an object with abstract methods. This may be a good option. Also I would be happy with a simple warning.

I'm very interested in this, but due to lack of time I can't play around with it as I would like to. I really appreciate your activity on this, your test cases are informative and help to improve the compiler!
fxm
Posts: 9304
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Abstract/Virtual destructor/method behaviour

Postby fxm » Dec 03, 2012 18:53

MOD wrote:In other languages you're not allowed to make a instance of an object with abstract methods. This may be a good option. Also I would be happy with a simple warning.

- In order to forbid object construction (to avoid crash due to abstract methods called), we can define a constructor and a copy-constructor as protected.
- In order to be exhaustive, we must forbid any object construction from the 'abstract' declaration up to just before the first 'overriding' body (one or several types, empty of the method body, may be between them). See my previous test case as an example.
fxm
Posts: 9304
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Abstract/Virtual destructor/method behaviour

Postby fxm » Dec 03, 2012 19:24

If I dared, I would also suggest:
- Each time there is an abstract method defined, the compiler adds a corresponding virtual method hidden (private) in its built-in Object.
- If compilation is without option -exx, the body of this extra virtual method is empty, otherwise a run-time error message is coded by compiler.

Code: Select all

Type _Object Extends Object
  Private:
    Declare Virtual Sub abstract_empty_empty ()
    Declare Virtual Sub abstract_empty_regular ()
    Declare Virtual Sub abstract_regular_empty ()
    Declare Virtual Sub abstract_regular_regular ()
    Declare Virtual Sub abstract_virtual_empty ()
    Declare Virtual Sub abstract_virtual_regular ()
    Declare Virtual Sub abstract_virtual_virtual ()
    Declare Virtual Sub abstract_abstract_empty ()
    Declare Virtual Sub abstract_abstract_regular ()
    Declare Virtual Sub abstract_abstract_virtual ()
End Type
Virtual Sub _Object.abstract_empty_empty ()
  Print "  ***Exec 'abstract_empty_empty()' ", "Err body in ", "_Object-type"
End Sub
Virtual Sub _Object.abstract_empty_regular ()
  Print "  ***Exec 'abstract_empty_regular()' ", "Err body in ", "_Object-type"
End Sub
Virtual Sub _Object.abstract_regular_empty ()
  Print "  ***Exec 'abstract_regular_empty()' ", "Err body in ", "_Object-type"
End Sub
Virtual Sub _Object.abstract_regular_regular ()
  Print "  ***Exec 'abstract_regular_regular()' ", "Err body in ", "_Object-type"
End Sub
Virtual Sub _Object.abstract_virtual_empty ()
  Print "  ***Exec 'abstract_virtual_empty()' ", "Err body in ", "_Object-type"
End Sub
Virtual Sub _Object.abstract_virtual_regular ()
  Print "  ***Exec 'abstract_virtual_regular()' ", "Err body in ", "_Object-type"
End Sub
Virtual Sub _Object.abstract_virtual_virtual ()
  Print "  ***Exec 'abstract_virtual_virtual()' ", "Err body in ", "_Object-type"
End Sub
Virtual Sub _Object.abstract_abstract_empty ()
  Print "  ***Exec 'abstract_abstract_empty()' ", "Err body in ", "_Object-type"
End Sub
Virtual Sub _Object.abstract_abstract_regular ()
  Print "  ***Exec 'abstract_abstract_regular()' ", "Err body in ", "_Object-type"
End Sub
Virtual Sub _Object.abstract_abstract_virtual ()
  Print "  ***Exec 'abstract_abstract_virtual()' ", "Err body in ", "_Object-type"
End Sub


Type Parent Extends _Object
  Declare Abstract Sub abstract_empty_empty ()
  Declare Abstract Sub abstract_empty_regular ()
  Declare Abstract Sub abstract_regular_empty ()
  Declare Abstract Sub abstract_regular_regular ()
  Declare Abstract Sub abstract_virtual_empty ()
  Declare Abstract Sub abstract_virtual_regular ()
  Declare Abstract Sub abstract_virtual_virtual ()
  Declare Abstract Sub abstract_abstract_empty ()
  Declare Abstract Sub abstract_abstract_regular ()
  Declare Abstract Sub abstract_abstract_virtual ()
End Type

Type Child Extends Parent
  Declare Sub abstract_regular_empty ()
  Declare Sub abstract_regular_regular ()
  Declare Virtual Sub abstract_virtual_empty ()
  Declare Virtual Sub abstract_virtual_regular ()
  Declare Virtual Sub abstract_virtual_virtual ()
  Declare Abstract Sub abstract_abstract_empty ()
  Declare Abstract Sub abstract_abstract_regular ()
  Declare Abstract Sub abstract_abstract_virtual ()
End Type
Sub Child.abstract_regular_empty ()
  Print "  Execute 'abstract_regular_empty()' ", "Sub body in ", "    Child-type"
End Sub
Sub Child.abstract_regular_regular ()
  Print "  Execute 'abstract_regular_regular()' ", "Sub body in ", "    Child-type"
End Sub
Virtual Sub Child.abstract_virtual_empty ()
  Print "  Execute 'abstract_virtual_empty()' ", "Sub body in ", "    Child-type"
End Sub
Virtual Sub Child.abstract_virtual_regular ()
  Print "  Execute 'abstract_virtual_regular()' ", "Sub body in ", "    Child-type"
End Sub
Virtual Sub Child.abstract_virtual_virtual ()
  Print "  Execute 'abstract_virtual_virtual()' ", "Sub body in ", "    Child-type"
End Sub

Type GrandChild Extends Child
  Declare Sub abstract_empty_regular ()
  Declare Sub abstract_regular_regular ()
  Declare Sub abstract_virtual_regular ()
  Declare Virtual Sub abstract_virtual_virtual ()
  Declare Sub abstract_abstract_regular ()
  Declare Virtual Sub abstract_abstract_virtual ()
End Type
Sub GrandChild.abstract_empty_regular ()
  Print "  Execute 'abstract_empty_regular()' ", "Sub body in ", "      GrandChild-type"
End Sub
Sub GrandChild.abstract_regular_regular ()
  Print "  Execute 'abstract_regular_regular()' ", "Sub body in ", "      GrandChild-type"
End Sub
Sub GrandChild.abstract_virtual_regular ()
  Print "  Execute 'abstract_virtual_regular()' ", "Sub body in ", "      GrandChild-type"
End Sub
Virtual Sub GrandChild.abstract_virtual_virtual ()
  Print "  Execute 'abstract_virtual_virtual()' ", "Sub body in ", "      GrandChild-type"
End Sub
Sub GrandChild.abstract_abstract_regular ()
  Print "  Execute 'abstract_abstract_regular()' ", "Sub body in ", "      GrandChild-type"
End Sub
Virtual Sub GrandChild.abstract_abstract_virtual ()
  Print "  Execute 'abstract_abstract_virtual()' ", "Sub body in ", "      GrandChild-type"
End Sub


Dim As Parent Ptr pc = New Child
Print "From Parent-type pointer to Child-type object:"
pc->abstract_empty_empty()
pc->abstract_empty_regular()
pc->abstract_regular_empty()
pc->abstract_regular_regular()
pc->abstract_virtual_empty()
pc->abstract_virtual_regular()
pc->abstract_virtual_virtual()
pc->abstract_abstract_empty()
pc->abstract_abstract_regular()
pc->abstract_abstract_virtual()
Delete pc
Print

Dim As Parent Ptr pg = New GrandChild
Print "From Parent-type pointer to GrandChild-type object:"
pg->abstract_empty_empty()
pg->abstract_empty_regular()
pg->abstract_regular_empty()
pg->abstract_regular_regular()
pg->abstract_virtual_empty()
pg->abstract_virtual_regular()
pg->abstract_virtual_virtual()
pg->abstract_abstract_empty()
pg->abstract_abstract_regular()
pg->abstract_abstract_virtual()
Delete pg

Sleep

Code: Select all

From Parent-type pointer to Child-type object:
  ***Exec 'abstract_empty_empty()'        Err body in   _Object-type
  ***Exec 'abstract_empty_regular()'      Err body in   _Object-type
  Execute 'abstract_regular_empty()'      Sub body in       Child-type
  Execute 'abstract_regular_regular()'    Sub body in       Child-type
  Execute 'abstract_virtual_empty()'      Sub body in       Child-type
  Execute 'abstract_virtual_regular()'    Sub body in       Child-type
  Execute 'abstract_virtual_virtual()'    Sub body in       Child-type
  ***Exec 'abstract_abstract_empty()'     Err body in   _Object-type
  ***Exec 'abstract_abstract_regular()'   Err body in   _Object-type
  ***Exec 'abstract_abstract_virtual()'   Err body in   _Object-type

From Parent-type pointer to GrandChild-type object:
  ***Exec 'abstract_empty_empty()'        Err body in   _Object-type
  Execute 'abstract_empty_regular()'      Sub body in         GrandChild-type
  Execute 'abstract_regular_empty()'      Sub body in       Child-type
  Execute 'abstract_regular_regular()'    Sub body in       Child-type
  Execute 'abstract_virtual_empty()'      Sub body in       Child-type
  Execute 'abstract_virtual_regular()'    Sub body in         GrandChild-type
  Execute 'abstract_virtual_virtual()'    Sub body in         GrandChild-type
  ***Exec 'abstract_abstract_empty()'     Err body in   _Object-type
  Execute 'abstract_abstract_regular()'   Sub body in         GrandChild-type
  Execute 'abstract_abstract_virtual()'   Sub body in         GrandChild-type
Last edited by fxm on Dec 04, 2012 12:44, edited 3 times in total.
fxm
Posts: 9304
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Abstract/Virtual destructor/method behaviour

Postby fxm » Dec 03, 2012 23:38

Recent changes to code: Recognize OVERRIDE attribute on method declarations.

There is a compiler error if 'Override' is put on the second regular method or more after an 'Abstract' or 'Virtual' declaration, because at present time the property of being a virtual method is not implicitly inherited.

For any user, explanation of the 'Override' attribute:
- New attribute called 'Override' that allows we to explicitly mark methods we intend to be overrides. If the method is not an override, the compiler will complain about it.
- Use of the 'Override' attribute is not required, it is highly recommended, as it will help prevent inadvertent errors (name/signature not matching).
Last edited by fxm on Dec 08, 2012 7:29, edited 4 times in total.
counting_pine
Site Admin
Posts: 6174
Joined: Jul 05, 2005 17:32
Location: Manchester, Lancs

Re: Abstract/Virtual destructor/method behaviour

Postby counting_pine » Dec 04, 2012 1:45

fxm wrote:
counting_pine wrote:Could this problem be prevented by disallowing the creation of abstract classes at compile-time?

No because at compile-time, the compiler knows only the pointer type and not the real type of object.
But the compiler does know the exact type of the object when it is created with New/Dim. This is when it should be disallowed. If I understand right, instances of abstract classes simply shouldn't be able to exist.
fxm
Posts: 9304
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Abstract/Virtual destructor/method behaviour

Postby fxm » Dec 04, 2012 7:58

counting_pine wrote:
fxm wrote:
counting_pine wrote:Could this problem be prevented by disallowing the creation of abstract classes at compile-time?

No because at compile-time, the compiler knows only the pointer type and not the real type of object.
But the compiler does know the exact type of the object when it is created with New/Dim. This is when it should be disallowed.

- Yes, you are right, it's very simple for type containing abstract method.
I was too focalized on the ulterior use of the created object, through a pointer.

- But there are others cases to take into account:
fxm wrote:In order to be exhaustive, we must forbid any object construction from the 'abstract' declaration up to just before the first 'overriding' body (one or several types, empty of the method body, may be between them). See my previous test case as an example.
Last edited by fxm on Dec 04, 2012 9:16, edited 2 times in total.
fxm
Posts: 9304
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Abstract/Virtual destructor/method behaviour

Postby fxm » Dec 04, 2012 8:45

counting_pine wrote:If I understand right, instances of abstract classes simply shouldn't be able to exist.

At present time, the specifier 'Abstract' is not applicable at the 'Type' declaration highest level.

IMHO:
Only the type containing only abstract methods and no member variables ('interface' type) should be non-instantiable.
A type containing a mix of abstract methods and, virtual/regular methods or member variables, must remain instantiable.
Last edited by fxm on May 17, 2019 9:28, edited 1 time in total.
dkl
Site Admin
Posts: 3210
Joined: Jul 28, 2005 14:45
Location: Germany

Re: Abstract/Virtual destructor/method behaviour

Postby dkl » Dec 04, 2012 17:31

Well, in case a Virtual is overridden by an Abstract, currently nothing will actually happen, as in C++, it's useless.

We should probably disallow abstract classes at DIM/NEW, effectively ensuring that abstract methods will always be implemented, because there will only be instances of non-abstract subclasses. Even if the UDT has a mix of abstract and non-abstract methods, if we allowed it with DIM/NEW then it could never make use of its Abstracts in which case adding them to that UDT was pointless. I think if Abstracts are there, they are intended to be used by the class but implemented by a subclass, thus abstract classes should not be allowed with DIM/NEW.
Gonzo
Posts: 722
Joined: Dec 11, 2005 22:46

Re: Abstract/Virtual destructor/method behaviour

Postby Gonzo » Dec 04, 2012 18:02

I agree.. i didn't understand the specifics but abstract has only one usage anyways
An abstract super-class can also implement an interface without actually implementing it
Thus children must implement that interface to work

it can get quite complicated for the compiler.. is this supported?

Note:
in C# you are forced to add the interface prototypes to the abstract class (which i suppose is ok, and can give better code overview)
counting_pine
Site Admin
Posts: 6174
Joined: Jul 05, 2005 17:32
Location: Manchester, Lancs

Re: Abstract/Virtual destructor/method behaviour

Postby counting_pine » Dec 04, 2012 18:16

fxm wrote:At present time, the specifier 'Abstract' is not applicable at the 'Type' declaration highest level.
I would say that means (at present time) a class is an abstract class iff it contains unimplemented abstract methods.
fxm
Posts: 9304
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Abstract/Virtual destructor/method behaviour

Postby fxm » Dec 04, 2012 19:02

dkl wrote:Well, in case a Virtual is overridden by an Abstract, currently nothing will actually happen, as in C++, it's useless.

But why, when regular (normal) is overridden by an Abstract, the behavior is different?

Code: Select all

Type _Object Extends Object
  Declare Virtual Sub virtual_abstract ()
  Declare Sub regular_abstract ()
End Type
Virtual Sub _Object.virtual_abstract ()
  Print "  ******* 'virtual_abstract() in _Object-type"
End Sub
Sub _Object.regular_abstract ()
  Print "  ******* 'regular_abstract() in _Object-type"
End Sub

Type Parent Extends _Object
  Declare Abstract Sub virtual_abstract ()
  Declare Abstract Sub regular_abstract ()
End Type


Dim As Parent Ptr pp = New Parent
pp->virtual_abstract()
pp->regular_abstract()

Code: Select all

  ******* 'virtual_abstract() in _Object-type

Aborting due to runtime error 7 (null pointer access) at line 20 of d:\Documents
 and Settings\t0003830\Mes documents\FBIde0.4.6r4_fbc0.25.0\FBIDETEMP.bas::()
fxm
Posts: 9304
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Abstract/Virtual destructor/method behaviour

Postby fxm » Dec 04, 2012 19:24

dkl wrote:We should probably disallow abstract classes at DIM/NEW, effectively ensuring that abstract methods will always be implemented, because there will only be instances of non-abstract subclasses. Even if the UDT has a mix of abstract and non-abstract methods, if we allowed it with DIM/NEW then it could never make use of its Abstracts in which case adding them to that UDT was pointless. I think if Abstracts are there, they are intended to be used by the class but implemented by a subclass, thus abstract classes should not be allowed with DIM/NEW.

Code: Select all

Type UDT0 Extends Object
  Declare Abstract Sub f1 ()
  Declare Abstract Sub f2 ()
End Type

Type UDT1 Extends UDT0
  Declare Sub f1 ()
End Type
Sub UDT1.f1 ()
End Sub

Type UDT2 Extends UDT1
  Declare Sub f2 ()
End Type
Sub UDT2.f2 ()
End Sub

And in this case above, we must also disallow UDT1 with DIM/NEW?

Return to “Community Discussion”

Who is online

Users browsing this forum: Juergen Kuehlwein and 2 guests