FreeBASIC syntax challenge games

General discussion for topics related to the FreeBASIC project or its community.
Post Reply
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: FreeBASIC syntax challenge games

Post by dodicat »

I tested on 32 bit.
fbc: FreeBASIC Compiler - Version 1.05.0

But it works on your original challenge:

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',
'         - a 'null' reference (referring to nothing) otherwise.
'-------------------------------------- zone beginning for insertion of one line ---------------------------------------
*cast(typename ptr,cast(object,expression) is typename)
'---------------------------------------- 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
    Declare Operator Cast () As String
  End Type
  
  Operator Parent##N.Cast () As String
    Operator = "- The instance " & Iif(@This, "'IS-A'    ", "'IS-NOT-A'") & " Parent" & #N
  End Operator
  
  Type Child##N Extends Parent##N
    Declare Operator Cast () As String
  End Type
  
  Operator Child##N.Cast () As String
    Operator = "- The instance " & Iif(@This, "'IS-A'    ", "'IS-NOT-A'") & " Child" & #N
  End Operator
  
  Type GrandChild##N Extends Child##N
    Declare Operator Cast () As String
  End Type
  
  Operator GrandChild##N.Cast () As String
    Operator = "- The instance " & Iif(@This, "'IS-A'    ", "'IS-NOT-A'") & " GrandChild" & #N
  End Operator
#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)
Print dynamic_cast(Child1, c1i)
Print dynamic_cast(GrandChild1, c1i)
Print dynamic_cast(Parent2, c1i)
Print dynamic_cast(Child2, c1i)
Print dynamic_cast(GrandChild2, c1i)
Print

Print "Dereferenced Parent1 object-type pointer that refers to a Child1 instance:"
Dim As Parent1 Ptr pp1c1i = @c1i
Print dynamic_cast(Parent1, *pp1c1i)
Print dynamic_cast(Child1, *pp1c1i)
Print dynamic_cast(GrandChild1, *pp1c1i)
Print dynamic_cast(Parent2, *pp1c1i)
Print dynamic_cast(Child2, *pp1c1i)
Print dynamic_cast(GrandChild2, *pp1c1i)
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))
Print dynamic_cast(Child1, p1rc1i(c1i))
Print dynamic_cast(GrandChild1, p1rc1i(c1i))
Print dynamic_cast(Parent2, p1rc1i(c1i))
Print dynamic_cast(Child2, p1rc1i(c1i))
Print dynamic_cast(GrandChild2, p1rc1i(c1i))
Print

Sleep 
I was surprised also.
0 can be regarded as a pointer with no error.
But so also can -1 (as is the result when cast(object,expression) is actually a typename
fxm
Moderator
Posts: 12106
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: FreeBASIC syntax challenge games

Post by fxm »

Yes you are right.
It's my fault because I have not completely returned to my original test program (I kept the data fields "sy" and "sn"!).

With fbc 64-bit, I get a compiler error because "IS" returns a "LONG" ant that's not compatible with a 64-bit pointer.
We must first convert it to an "INTEGER":
*cast(typename ptr ,cast(integer, cast(object, expression) is typename))
Last edited by fxm on Sep 04, 2017 11:58, edited 1 time in total.
fxm
Moderator
Posts: 12106
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: FreeBASIC syntax challenge games

Post by fxm »

Known behavior:

Code: Select all

Type UDT Extends object
  Declare Sub s ()
End Type

Sub UDT.s ()
  Print "UDT.s()", @This
End Sub

(*Cast(UDT Ptr, 123456789)).s()

Sleep

Code: Select all

UDT.s()       123456789
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: FreeBASIC syntax challenge games

Post by dodicat »

Just cint suffices
*cast(typename ptr,cint(cast(object,expression) is typename))
But both ways cause a seg fault in your property version.
fxm
Moderator
Posts: 12106
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: FreeBASIC syntax challenge games

Post by fxm »

The macro to define must verify the specification defined in its header (see below):

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
That is why I modified my test program to check all these requirements.
Last edited by fxm on Sep 07, 2017 9:32, edited 1 time in total.
fxm
Moderator
Posts: 12106
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: FreeBASIC syntax challenge games

Post by fxm »

Warning when using "IIF" (useful for the challenge #21)

As specified in documentation, "IIF" works with expressions of type:
- numeric value, which can be integer, floating point number or pointer,
- or string value,
- or UDT value.
and returns a copy of the expression.

More precisely, contrary to one might expect when using the "*" operator for assignment, "IIF" does not work when attempting assignment with dereferenced pointers referring to objects (and this without compile error):

Code: Select all

Iif( condition, *pointer_if_true, *pointer_if_false )
returns only a (temporary) copy of the object.
In order to get a true (temporary) reference, the following syntax must be used instead:

Code: Select all

( *Iif( condition, pointer_if_true, pointer_if_false ) )
Example:

Code: Select all

Type UDT
  Dim As Integer I
End Type

Dim As UDT Ptr p1 = New UDT(1)
Dim As UDT Ptr p2 = New UDT(2)

Dim As Integer C = 0

Iif(C, *p1, *p2).I = 3  '' does not work, although without compile error
Print p1->I, p2->I

(*Iif(C, p1, p2)).I = 3  '' does work
Print p1->I, p2->I

Sleep
Delete p1
Delete p2
Obviously, same behavior if using references to objects (with also no compile error when attempting assignment):

Code: Select all

Iif( condition, reference_if_true, reference_if_false )
returns only a (temporary) copy of the object.
In order to get a true (temporary) reference, the following syntax must be used instead:

Code: Select all

( *Iif( condition, @reference_if_true, @reference_if_false ) )
Example:

Code: Select all

Type UDT
  Dim As Integer I
End Type

Dim Byref As UDT r1 = *New UDT(1)
Dim Byref As UDT r2 = *New UDT(2)

Dim As Integer C = 0

Iif(C, r1, r2).I = 3  '' does not work, although without compile error
Print r1.I, r2.I

(*Iif(C, @r1, @r2)).I = 3  '' does work
Print r1.I, r2.I

Sleep
Delete @r1
Delete @r2
For simpler predefine types as "INTEGER" or even references to "INTEGER", attempting an assignment always results into a compile error:

Code: Select all

Dim As Integer I1 = 1, I2 = 2

Dim As Integer C

'Iif(C, I1, I2) = 3  '' does not compile
*Iif(C, @I1, @I2) = 3  '' does work
Print I1, I2

Dim Byref As Integer R1 = I1, R2 = I2
'Iif(C, R1, R2) = 4  '' does not compile
*Iif(C, @R1, @R2) = 4  '' does work
Print I1, I2

Sleep
Last edited by fxm on Sep 06, 2017 11:16, edited 16 times in total.
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: FreeBASIC syntax challenge games

Post by MrSwiss »

@fxm,

for the sake of completeness, you might want to add: Booleans ...
fxm
Moderator
Posts: 12106
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: FreeBASIC syntax challenge games

Post by fxm »

Documentation updated:
KeyPgIif → fxm [Added the Boolean as expression type]
fxm
Moderator
Posts: 12106
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: FreeBASIC syntax challenge games

Post by fxm »

Enigma #21 wrote:
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)
.....
I think the following sentence extracted from the documentation on "Extends" is useful for solving the enigma #21 (for defining the "dynamic_cast" macro):
KeyPgExtends wrote:
.....
Note: UDT pointer can only be cast to pointer types of "widened compatibility" (up or even down in the inheritance hierarchy), or to Any Ptr. Otherwise, cast to Any Ptr first (or to Object Ptr first if both types are directly or indirectly derived from Object).
.....
fxm
Moderator
Posts: 12106
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: FreeBASIC syntax challenge games

Post by fxm »

A big supplementary help for enigma #21

Referring to my previous posts, the solution syntax form for the "dynamic_cast (typename, expression)" macro is:

Code: Select all

( *Iif( condition(expression, typename), typename_pointeur_if_true, typename_pointeur_if_false ) )
and not:

Code: Select all

Iif( condition(expression, typename), *typename_pointeur_if_true, *typename_pointeur_if_false )
A beginner way could be to test from the trivial syntax:

Code: Select all

( *Iif( expression Is typename, Cptr( typename Ptr, @(expression ) ), Cptr( typename Ptr, 0 ) ) )
We get the compile error:
error 297: Types have no hierarchical relation
=> we must correct (1) the term:

Code: Select all

expression Is typename
After the right correction, we get another compile error:
error 300: Casting derived UDT pointer from unrelated UDT pointer type
=> we must also correct (2) the term:

Code: Select all

Cptr( typename Ptr, @(expression ) )
(1) expression's type must be a parent type of typename

(2) UDT pointer can only be cast to pointer types of "widened compatibility" (up or even down in the inheritance hierarchy), or to Any Ptr. Otherwise, cast to Any Ptr first (or to Object Ptr first if both types are directly or indirectly derived from Object).
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: FreeBASIC syntax challenge games

Post by dodicat »

You have made it too easy now.
A sticking point was the iif and *iif, but I see the remarks now, from earlier.
Also I was really trying to avoid using a cast to 0 pointer, but I don't think this can be avoided.

Casting both instances of expression to object in your beginner way does the trick.

Finding a real use for this dynamic_cast macro might be a bigger challenge.
fxm
Moderator
Posts: 12106
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: FreeBASIC syntax challenge games

Post by fxm »

dodicat wrote:A sticking point was the iif and *iif, but I see the remarks now, from earlier.
Yet, I tried to make a crescendo help.
dodicat wrote:Casting both instances of expression to object in your beginner way does the trick.
Yes, my own solution is:

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 ----------------------------------------
  (*Iif(Cast(Object, expression) Is typename, Cptr(typename Ptr, @Cast(Object, expression)), Cptr(typename Ptr, 0)))
'----------------------------------------- zone ending for insertion of one line -----------------------------------------
#endmacro
dodicat wrote:Also I was really trying to avoid using a cast to 0 pointer, but I don't think this can be avoided.
The following solution is allowed by the compiler (without any message reported), but I'm not sure to well understand why:

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 ----------------------------------------
  (*Iif(Cast(Object, expression) Is typename, Cptr(typename Ptr, @Cast(Object, expression)), 0))
'----------------------------------------- zone ending for insertion of one line -----------------------------------------
#endmacro
(implicit conversion by compiler?)


[edit]
- Yes, converting the specific value "0" into any type of pointer is implicit.
Last edited by fxm on Sep 11, 2017 11:42, edited 5 times in total.
fxm
Moderator
Posts: 12106
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: FreeBASIC syntax challenge games

Post by fxm »

By the way, I want to explain why I wrap the macro body with parentheses:

Remenber that expressions like:

Code: Select all

*ptr.field
or

Code: Select all

*(ptr).field
should always give a compiler error due to the operators precedence: '.' operator has a higher priority then the '*' operator.
There is already a bug report about that: #811 *(ptr).field should give an error
The only right syntax is:

Code: Select all

(*ptr).field
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: FreeBASIC syntax challenge games

Post by dodicat »

I think there is something amiss here.
Your code:

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 ---------------------------------------

 *(iif(     cbool(cast(object,expression) Is typename) and true        ,@typename, 0))
'---------------------------------------- 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 cbool(cast(object,c1i) Is child1)
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 
a straight true:
*(iif( true ,@typename, 0))
gives all the IS-A
a straight false in the above, gives all the IS-NOT-A
But in the code snippet, the iif will not react to cbool(cast(object,expression) Is typename) being true.
fxm
Moderator
Posts: 12106
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: FreeBASIC syntax challenge games

Post by fxm »

This behavior is due to that we discussed with counting_pine about 'IIF(condition, expression_if_true, expression_if_false)':
- When condition is a constant (therefore known at compile-time), 'IIF' provides the expression itself. 'IIF' can be modeled like a macro returning the expression.
- When condition is a variable (therefore known only at run-time), 'IIF' provides a copy of the expression. 'IIF' can be modeled like a function returning a copy of the expression.

If expression is the address of a temporary object (for example, '@Parent1()'), when the provided copy is used by user, the temporary object is already destructed.

Example for testing (using the 'Parent1' type):

Code: Select all

Dim As Integer c = -1

Print (*Iif(true, @Parent1(), 0)).testIfIsA

Print (*Iif(c, @Parent1(), 0)).testIfIsA
Print

#macro m()
  @Parent1()
#endmacro
Print (*(m())).testIfIsA

Function f () As Parent1 Ptr
  Return @Parent1()
End Function
Print (*f()).testIfIsA

Sleep
Post Reply