FreeBASIC syntax challenge games

General discussion for topics related to the FreeBASIC project or its community.
Post Reply
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: FreeBASIC syntax challenge games

Post by fxm »

The principle is the right one for the constructor declaration: Use as default parameter value a null pointer dereferenced.

- But already your constructor code can be much simplified:

Code: Select all

Constructor UDT (Byref u As UDT)
  Print iif(@u,ccs,dcs)
End Constructor
- My solution is still simpler because instead of use a static member pointer, mine uses just a temporary pointer:

Code: Select all

'--------------------------------- zone beginning of modification -----------------------------------
Type UDT
  Public:
    Declare Constructor (Byref As UDT = *Cast(UDT Ptr, 0))  ' or: '...(Byref As UDT = Peek(UDT, 0))'
  Private:
    Const As String dcs = "  Instance created by default-construction."
    Const As String ccs = "  Instance created by copy-construction."
    Dim As Integer dummy
End Type

Constructor UDT (Byref u As UDT)
  Print Iif(@u, ccs, dcs)  ' iif structure with strings is valid since FBC 0.90.0
End Constructor
'----------------------------------- zone ending of modification ------------------------------------

Print "Begin of challenge:"
Dim As UDT u1 = UDT()    ' To highlight the default-constructor's call, explicit call but can be reduced to: 'Dim As UDT u1'
Dim As UDT u2 = UDT(u1)  ' To highlight the copy-constructor's call, explicit call but can be reduced to: 'Dim As UDT u2 = u1'
Print "End of challenge."

Sleep
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: FreeBASIC syntax challenge games

Post by fxm »

Passing by reference a dereferenced null-pointer as a parameter to a procedure is allowed and runs as long as such a parameter is not explicitly used in the body of the user procedure:
- Because under the hood, passing by reference is encoded by the compiler as passing by value a pointer (null in that case). So, as long as such a parameter is not used in the body of the user procedure, none implicit dereferencing attempt of the compiler occurs.

Instead, passing by value a dereferenced null-pointer as a parameter to a procedure always crashes, even if such a parameter is not used in the body of the user procedure:
- Because the null-pointer must be necessarily dereferenced to get a copy from the parameter to be passed.
dafhi
Posts: 1641
Joined: Jun 04, 2005 9:51

Re: FreeBASIC syntax challenge games

Post by dafhi »

I couldn't figure this one out

i tried something like

Code: Select all

as type<UDT> = NULL
with the resulting error message
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: FreeBASIC syntax challenge games

Post by dodicat »

You can also have
const as udt ptr p=0
Declare Constructor (Byref As UDT=*p)

In fact, anything which allows you to write Byref As UDT = (something).
And the constructor body can check whether that something exists, (checking for a null pointer for instance).
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: FreeBASIC syntax challenge games

Post by fxm »

Enigma #20, force 1 (a simple challenge, just for highlighting a small new added feature)

Some times, it could be useful to define an inheritance structure that accepts to work both through:
- with polymorphic way by using base pointers, even for a derived Type having a polymorphic function which returns an object that is more derived object than the one of the base Type (this is perfectly legal, since the pointer is automatically downcast to a base pointer),
- and with not polymorphic way by using classic dedicated derived-type pointers.

A common use of this (but by no means the only one) is a clone() method.
The usual polymorphism usage defines an overriding method with the same signature than the base method:

Code: Select all

Type Parent Extends Object
  Declare Virtual Function clone () As Parent Ptr
End Type
Virtual Function Parent.clone () As Parent Ptr
  Print "Instance of Parent cloned"
  Return New Parent(This)
End Function

'--------------- zone beginning for modification ---------------
Type Child Extends Parent
  Declare Virtual Function clone () As Parent Ptr Override
End Type
Virtual Function Child.clone () As Parent Ptr
  Print "Instance of Child cloned"
  Return New Child(This)
End Function
'----------------- zone ending for modification ----------------
- This perfectly works when the user code uses polymorphism:

Code: Select all

Dim As Parent Ptr ppc1 = New Child
Dim As Parent Ptr ppc2 = ppc1->clone()

Sleep
Delete Cptr(Child Ptr, ppc2)
Delete Cptr(Child Ptr, ppc1)
Instance of Child cloned
- But that no longer works with a classic user code (without polymorphism usage):

Code: Select all

Dim As Child Ptr pcc1 = New Child
Dim As Child Ptr pcc2 = pcc1->clone()

Sleep
Delete pcc2
Delete pcc1
error 180: Invalid assignment/conversion in 'Dim As Child Ptr pcc2 = pcc1->clone()'
Challenge:
Modify the Child Type definition only by using the feature added since fbc rev 1.02.0 allowing to improve this above inheritance structure for accepting to work both through polymorphic way (by using base pointers) and not (by using dedicated derived-type pointers):

Code: Select all

Dim As Parent Ptr ppc1 = New Child
Dim As Parent Ptr ppc2 = ppc1->clone()

Dim As Child Ptr pcc1 = New Child
Dim As Child Ptr pcc2 = pcc1->clone()

Sleep
Delete pcc2
Delete pcc1
Delete Cptr(Child Ptr, ppc2)
Delete Cptr(Child Ptr, ppc1)
Instance of Child cloned
Instance of Child cloned
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: FreeBASIC syntax challenge games

Post by fxm »

For a little help:
- see the commit [4a68ba]: Relax procptr type checks
- In addition, I documented all this in the FBWiki in July
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: FreeBASIC syntax challenge games

Post by dodicat »

making :
Type Child Extends Parent
Declare Virtual Function clone () As Parent Ptr Override
End Type

into
Type Child Extends Parent
Declare Virtual Function clone () As child Ptr Override
End Type

does the job.
But I don't think that is the required method.
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: FreeBASIC syntax challenge games

Post by fxm »

Yes, it's perfect!
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: FreeBASIC syntax challenge games

Post by fxm »

Explanation (about enigma #20):

Feature added in fbc rev 1.02.0 (the second change corrected as following should be the true wording in file changelog.txt):
- Relaxed type checking for procedure pointers and virtual method overrides: Procedures with different signatures can now be treated equal if the signatures are compatible. For example: assignments between a function pointer returning an Integer and one returning a Long won't cause a "suspicious pointer assignment" warning on 32bit anymore.
- Covariant parameters and function results (feature request #289)
- Contravariant byref parameters and covariant byref function results (feature request #289)

Definition:
- Covariance allows you to use a less generic (more derived) type than the one originally specified.
- Contravariance allows you to use a more generic (less derived) type than the one originally specified.
- Invariance requires you to use exactly the same type than the one originally specified.

Remark:
- Contravariant byref (or by pointer) parameters and covariant byref (or by pointer) results work for assigning procedures to procedure pointers.
- Only the covariant byref (or by pointer) results works for overriding virtual functions, none contravariant parameters is allowed (invariant parameters).
- On the other hand, covariant parameters and contravariant results can not normally be allowed because they are not type safe.

Related documentation pages updated:
- Glossary
- Pointers to Procedures
- VIRTUAL


Example of assigning to a function pointer a function with a contravariant parameter and a covariant result:

Code: Select all

Type A
    Dim As Integer I
End type

Type B Extends A
    Dim As Integer J
End Type

Function f (Byref a0 As A) As B Ptr
    Print "Instance of B created"
    Return New B(a0)
End Function

Dim As Function (Byref As B) As A Ptr pf = @f

Dim As B b0
Dim As A Ptr pab = pf(b0)

Sleep
Delete Cptr(B Ptr, pab)
  • Compiler warning message before the rev 1.02.0 of fbc:
    • warning 4(1): Suspicious pointer assignment
Example of overriding virtual function with covariant return:
(Solution to enigma #20)

Code: Select all

Type Parent Extends Object
  Declare Virtual Function clone () As Parent Ptr
End Type
Virtual Function Parent.clone () As Parent Ptr
  Print "Instance of Parent cloned"
  Return New Parent(This)
End Function

Type Child Extends Parent
'--------------- zone beginning for modification ---------------
  'Declare Virtual Function clone () As Parent Ptr Override
  Declare Virtual Function clone () As Child Ptr Override
End Type
'Virtual Function Child.clone () As Parent Ptr
Virtual Function Child.clone () As Child Ptr
  Print "Instance of Child cloned"
  Return New Child(This)
End Function
'----------------- zone ending for modification ----------------

Dim As Parent Ptr ppc1 = New Child
Dim As Parent Ptr ppc2 = ppc1->clone()

Dim As Child Ptr pcc1 = New Child
Dim As Child Ptr pcc2 = pcc1->clone()

Sleep
Delete pcc2
Delete pcc1
Delete Cptr(Child Ptr, ppc2)
Delete Cptr(Child Ptr, ppc1)
  • Compiler error message before the rev 1.02.0 of fbc:
    • error 224: Override has different return type than overridden method
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: FreeBASIC syntax challenge games

Post by fxm »

Since there are surely some "fans" of this challenge on holiday (only dodicat has posted about the previous), I will wait the early September to propose another.
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: FreeBASIC syntax challenge games

Post by fxm »

Enigma #21, force 3
Simulation of a 'dynamic cast' operator, by using a macro.
(may be interesting for those who want use a kind of polymorphism but not through virtual member procedures, and who do not prefer to call directly the operator 'IS' whose use is not intuitive)

Challenge:
By only defining one code line in the macro body (a somewhat complex line inside the dedicated zone for line insertion ), make so that program works and correctly displays the types/super-types ('IS-A' typename) or not ('IS-NOT-A' typename) of the created instance (referred in 3 different ways).
The main specifications of the macro are commented out in the macro header of test code below.

Test code compatible with fbc version >= 0.90.0:

Code: Select all

#macro dynamic_cast (typename, expression)
' macro to simulate a 'dynamic cast' operator:
'     - 'typename' and 'expression' must be directly or indirectly derived from 'Object' using 'Extends',
'       but not necessarily on the same inheritance branch
'     - A 'typename' reference is always provided:
'         - a non 'null' reference referring to 'expression', if 'expression' is compatible to 'typename'
'           (@reference = @expression)
'         - a 'null' reference referring to nothing, otherwise
'           (@reference = 0)
'-------------------------------------- zone beginning for insertion of one line ---------------------------------------

'---------------------------------------- zone ending for insertion of one line ----------------------------------------
#endmacro


#macro family(N)  '' for testing purpose, macro to define an inheritance branch structure : family # N
  Type Parent##N Extends Object
    Public:
      Declare Property testIfIsA () As String
    Private:
      Dim   As String sy = "- The instance 'IS-A'     Parent" & #N
      Const As String sn = "- The instance 'IS-NOT-A' Parent" & #N
  End Type
 
  Property Parent##N.testIfIsA () As String
    Property = Iif(@This, This.sy, Parent##N.sn)
  End Property
 
  Type Child##N Extends Parent##N
    Public:
      Declare Property testIfIsA () As String
    Private:
      Dim   As String sy = "- The instance 'IS-A'     Child" & #N
      Const As String sn = "- The instance 'IS-NOT-A' Child" & #N
  End Type
 
  Property Child##N.testIfIsA () As String
    Property = Iif(@This, This.sy, Child##N.sn)
  End Property
 
  Type GrandChild##N Extends Child##N
    Public:
      Declare Property testIfIsA () As String
    Private:
      Dim   As String sy = "- The instance 'IS-A'     GrandChild" & #N
      Const As String sn = "- The instance 'IS-NOT-A' GrandChild" & #N
  End Type
 
  Property GrandChild##N.testIfIsA () As String
    Property = Iif(@This, This.sy, GrandChild##N.sn)
  End Property
#endmacro


family(1)  '' define an inheritance branch : family # 1

family(2)  '' define an inheritance branch : family # 2

Print "Simple Child1 instance variable:"
Dim As Child1 c1i
Print dynamic_cast(Parent1    , c1i).testIfIsA
Print dynamic_cast(Child1     , c1i).testIfIsA
Print dynamic_cast(GrandChild1, c1i).testIfIsA
Print dynamic_cast(Parent2    , c1i).testIfIsA
Print dynamic_cast(Child2     , c1i).testIfIsA
Print dynamic_cast(GrandChild2, c1i).testIfIsA
Print

Print "Dereferenced Parent1 object-type pointer that refers to a Child1 instance:"
Dim As Parent1 Ptr pp1c1i = @c1i
Print dynamic_cast(Parent1    , *pp1c1i).testIfIsA
Print dynamic_cast(Child1     , *pp1c1i).testIfIsA
Print dynamic_cast(GrandChild1, *pp1c1i).testIfIsA
Print dynamic_cast(Parent2    , *pp1c1i).testIfIsA
Print dynamic_cast(Child2     , *pp1c1i).testIfIsA
Print dynamic_cast(GrandChild2, *pp1c1i).testIfIsA
Print

Print "Parent1 object-type reference that refers to a Child1 instance:"
Function p1rc1i (Byref c1 As Child1) Byref As Parent1
  Return c1
End Function
Print dynamic_cast(Parent1    , p1rc1i(c1i)).testIfIsA
Print dynamic_cast(Child1     , p1rc1i(c1i)).testIfIsA
Print dynamic_cast(GrandChild1, p1rc1i(c1i)).testIfIsA
Print dynamic_cast(Parent2    , p1rc1i(c1i)).testIfIsA
Print dynamic_cast(Child2     , p1rc1i(c1i)).testIfIsA
Print dynamic_cast(GrandChild2, p1rc1i(c1i)).testIfIsA
Print

Sleep
Execution result to be obtained:

Code: Select all

Simple Child1 instance variable:
- The instance 'IS-A'     Parent1
- The instance 'IS-A'     Child1
- The instance 'IS-NOT-A' GrandChild1
- The instance 'IS-NOT-A' Parent2
- The instance 'IS-NOT-A' Child2
- The instance 'IS-NOT-A' GrandChild2

Dereferenced Parent1 object-type pointer that refers to a Child1 instance:
- The instance 'IS-A'     Parent1
- The instance 'IS-A'     Child1
- The instance 'IS-NOT-A' GrandChild1
- The instance 'IS-NOT-A' Parent2
- The instance 'IS-NOT-A' Child2
- The instance 'IS-NOT-A' GrandChild2

Parent1 object-type reference that refers to a Child1 instance:
- The instance 'IS-A'     Parent1
- The instance 'IS-A'     Child1
- The instance 'IS-NOT-A' GrandChild1
- The instance 'IS-NOT-A' Parent2
- The instance 'IS-NOT-A' Child2
- The instance 'IS-NOT-A' GrandChild2
[edit]
Test program modified in order to impose that the macro returns an UDT reference.
Last edited by fxm on Sep 07, 2017 9:32, edited 2 times in total.
sancho2
Posts: 547
Joined: May 17, 2015 6:41

Re: FreeBASIC syntax challenge games

Post by sancho2 »

Ok 2 days fighting with this one. I think I have the solution, but I don't fully understand what is happening.

Code: Select all

IIf(Cast(object, (expression)) Is typename, Cast(String, typename), Cast(String, *Cast(typename Ptr, 0)))
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: FreeBASIC syntax challenge games

Post by fxm »

Indeed, it's not the expected solution because the "dynamic_cast" macro is expected to return a well cast UDT reference and not a string as your above solution (you use the fact that in the proposed test program, all UDTs have a "cast() as string" member operator).
Therefore, I modified my test program ("cast() as string" member operator replaced by a "property() as string" member) in order to impose that the macro returns the expected UDT reference.

In your proposed "iif" structure, the condition to test is right, but you call the "cast" operator of typename, either on a local typename instance, or on a "null" typename instance.
Your proposal is equivalent to:
IIf(Cast(object, expression) Is typename, Cast(String, Type<typename>()), Cast(String, *Cast(typename Ptr, 0)))

In your proposed "iif" structure, only the expressions if true and false must be modified to arrive at the right solution.
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: FreeBASIC syntax challenge games

Post by dodicat »

Before you re-did everything to property

*cast(typename ptr,cast(object,expression) is typename)
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: FreeBASIC syntax challenge games

Post by fxm »

Even with my old test program (with cast), I don't see how it can work because the "cast(object,expression) is typename" term returns either -1 or 0!

With my both test programs (with cast and with property), I get:
- In 32-bit & gas:
Aborting due to runtime error 12 ("segmentation violation" signal)
- In 64-bit:
error 28: Expected pointer
Post Reply