UDT's for Properties.

Source-code only - please, don't post questions here.
Pritchard
Posts: 5425
Joined: Sep 12, 2005 20:06
Location: Ohio, USA

UDT's for Properties.

Postby Pritchard » Sep 13, 2007 2:46

1) Include the library.

2) Create a UDT with the appropriate static functions (see procUDT datatype). These functions must cast to your UDTs' type, as they will pass an any pointer to your THIS object, and will be individually assigned per object.

3) Set the type of data type you're using (DataUDT.DataKind, see the Namespace DataKind table for more info).

4) Pass your procUDT type to the SetSub of your DataUDT.

5) Break my functions. Use the operators at will! :D


Example. Here we use a custom assign (set method, but we could have used any of them!) for our Speed UDT Property. We need the DataUDT, which will hold all the data for our property, and the ProcUDT, which will hold our static function pointers:

Code: Select all

#include once "priUDT.bi"
using priUDT

Type Foo
  As Double Position
  Speed       as DataUDT
  SpeedProc   as ProcUDT
 
    '' our static functions for using speed.
      '' assignment
  Declare Static Sub SpeedprocLet( ByVal _MyObj_ as Any Ptr, ByVal _rhs_ as Any Ptr )
End Type

  '' assignment.
Sub Foo.SpeedprocLet( ByVal _MyObj_ as Any Ptr, ByVal _rhs_ as Any Ptr )
    '' cast to the proper type of pointer.
  Dim as Foo ptr FooPtr = Cast( Foo Ptr, _MyObj_ )
    '' cast rhs to the proper datatype.
  Dim as Double ptr FooDouble = Cast( Double ptr, _rhs_ )
 
    '' We're using a double :D  Assignment!
  FooPtr->Speed.DataDouble = *FooDouble
End Sub

Dim as Foo Bar

  '' Set the data kind used in our UDT
  '' We'll use this as a double.
Bar.Speed.DataKind = DataKind.OpDouble

  '' Set up some context for our procedures.
  '' They're static, so we have to give them THIS.
Bar.SpeedProc.That = @Bar
  '' Assignment.
Bar.SpeedProc.procLet = @Foo.SpeedprocLet()
'  '' Additive
'Bar.SpeedProc.procAddEquals
'  '' Subtractive
'Bar.SpeedProc.procSubEquals
'  '' Divisive
'Bar.SpeedProc.procDivEquals
'  '' Integrally Divisive
'Bar.SpeedProc.procIntDivEquals
'  '' Multiplicational
'Bar.SpeedProc.procMulEquals
'  '' Exponential
'Bar.SpeedProc.procPowEquals

  '' Set our UDT procedures and context.
Bar.Speed.SetUDT( @Bar.SpeedProc )

  '' Our value.
Dim as Double BarSpeed = 520
  '' Our assigned value.
Dim as Double BarNewSpeed

  '' assign our value.
Bar.Speed = @BarSpeed
  '' get our newly assigned value.
BarNewSpeed = Bar.Speed

  '' Print our value.
Print BarNewSpeed
sleep


priUDT.bas:

Code: Select all

#include once "priUDT.bi"
  '' cast and return our type to the appropriate kind.
  '' if __type__ is a double, we return a double.
  '' we have to cast from the proper type, however.
#macro __pri_Return_Cast__( __type__ )
  select case as const this.DataKind
    case priUDT.DataKind.OpuLong
      return cast( __type__, this.DatauLong )
    case priUDT.DataKind.OpLong
      return cast( __type__, this.DataLong )
    case priUDT.DataKind.OpuInteger
      return cast( __type__, this.DatauInteger )
    case priUDT.DataKind.OpInteger
      return cast( __type__, this.DataInteger )
    case priUDT.DataKind.OpuShort
      return cast( __type__, this.DatauShort )
    case priUDT.DataKind.OpShort
      return cast( __type__, this.DataShort )
    case priUDT.DataKind.OpuByte
      return cast( __type__, this.DatauByte )
    case priUDT.DataKind.OpByte
      return cast( __type__, this.DataByte )
    case priUDT.DataKind.OpDouble
      return cast( __type__, this.DataDouble )
    case priUDT.DataKind.OpSingle
      return cast( __type__, this.DataSingle )
  end select
#endmacro
Namespace priUDT

    '' Set up our UDT.  Accepts any pointer.
    '' Casts to a ProcUDT type.
  Sub DataUDT.SetUDT( ByVal _MyObj_ as Any ptr )
      '' cast our type
    Dim as priUDT.ProcUDT ptr _ThatObj_ = cast( priUDT.ProcUDT ptr, _MyObj_ )
      '' assign our values and procedures.
    this.DataProc = *_ThatObj_
  End Sub
 
    '' Assignment Operators:
      '' Assignment
  Operator DataUDT.Let ( ByVal _rhs_ as Any Ptr )
    this.DataProc.ProcLet( this.DataProc.That, _rhs_ )
  End Operator
 
      '' Additive
  Operator DataUDT.+= ( ByVal _rhs_ as Any Ptr )
    this.DataProc.ProcAddEquals( this.DataProc.That, _rhs_ )
  End Operator
 
      '' Subtractive
  Operator DataUDT.-= ( ByVal _rhs_ as Any Ptr )
    this.DataProc.ProcSubEquals( this.DataProc.That, _rhs_ )
  End Operator
 
      '' Divisive
  Operator DataUDT./= ( ByVal _rhs_ as Any Ptr )
    this.DataProc.ProcDivEquals( this.DataProc.That, _rhs_ )
  End Operator
 
      '' Integrally Divisive
  Operator DataUDT.\= ( ByVal _rhs_ as Any Ptr )
    this.DataProc.ProcIntDivEquals( this.DataProc.That, _rhs_ )
  End Operator
 
      '' Multiplicational
  Operator DataUDT.*= ( ByVal _rhs_ as Any Ptr )
    this.DataProc.ProcMulEquals( this.DataProc.That, _rhs_ )
  End Operator
 
      '' Exponential
  Operator DataUDT.^= ( ByVal _rhs_ as Any Ptr )
    this.DataProc.ProcPowEquals( this.DataProc.That, _rhs_ )
  End Operator
 
    '' Casting.
  Operator DataUDT.Cast() as uLong
      '' horray macros.
    __pri_Return_Cast__( uLong )
  End Operator
 
  Operator DataUDT.Cast() as Long
    __pri_Return_Cast__( Long )
  End Operator
 
  Operator DataUDT.Cast() as uInteger
    __pri_Return_Cast__( uInteger )
  End Operator
 
  Operator DataUDT.Cast() as Integer
    __pri_Return_Cast__( Integer )
  End Operator
 
  Operator DataUDT.Cast() as uShort
    __pri_Return_Cast__( uShort )
  End Operator
 
  Operator DataUDT.Cast() as Short
    __pri_Return_Cast__( Short )
  End Operator
 
  Operator DataUDT.Cast() as uByte
    __pri_Return_Cast__( uByte )
  End Operator
 
  Operator DataUDT.Cast() as Byte
    __pri_Return_Cast__( Byte )
  End Operator
 
  Operator DataUDT.Cast() as Double
    __pri_Return_Cast__( Double )
  End Operator
 
  Operator DataUDT.Cast() as Single
    __pri_Return_Cast__( Single )
  End Operator

End Namespace


priUDT.bi:

Code: Select all

'' Using  a  UDT   instead  of   PROPERTIES  for  get/set,  and  even a  full
'' list operators!  Templates might have been useful here?  Virtual functions?
'' I'd have found an easier solution SOMEWHERE in OOP -_-;;
#inclib "priUDT"
Namespace priUDT

  Namespace DataKind
    Const as Integer OpuLong = 0
    Const as Integer OpLong = 1
    Const as Integer OpuInteger = 2
    Const as Integer OpInteger = 3
    Const as Integer OpuShort = 4
    Const as Integer OpShort = 5
    Const as Integer OpuByte = 6
    Const as Integer OpByte = 7
    Const as Integer OpDouble = 8
    Const as Integer OpSingle = 9
  End Namespace

    '' Sub to use for operators.
    '' MyObj to get our value.
    '' RHS for the operator value
  Type OpProc as Sub (      _
    ByVal _MyObj_ as Any Ptr, _
    ByVal _rhs_ as Any Ptr    _
  )
 
    ' Hold all of the static procedures along with our object.
    '
    '
    ' !!!THESE ARE THE PROCEDURES THAT YOU SET UP FOR EACH OBJECT!!!
    '
    '
  Type ProcUDT
      '' the object we would normally use as THIS.
      '' You're going to have to properly cast this in your functions.
      '' THAT is passed as _MyObj_ in the op proc,
      '' while the normal operator rhs is passed afterwards.
    That            as Any Ptr
      '' Assignment Operators:
        '' Assignment
    procLet as OpProc
        '' Additive
    procAddEquals as OpProc
        '' Subtractive
    procSubEquals as OpProc
        '' Divisive
    procDivEquals as OpProc
        '' Integrally Divisive
    procIntDivEquals as OpProc
        '' Multiplicational
    procMulEquals as OpProc
        '' Exponential
    procPowEquals as OpProc
  End Type
 
    '' Hold any sort of value you want.
    '' Use it like a property, but with operators.
  Type DataUDT
      '' Data procedures.
    DataProc        as ProcUDT
      '' Data Kind.
    DataKind        as Integer
      '' Data value.
    Union
      DatauLong     as uLong
      DataLong      as Long
      DatauInteger  as uInteger
      DataInteger   as Integer
      DatauShort    as uShort
      DataShort     as Short
      DatauByte     as uByte
      DataByte      as Byte
      DataDouble    as Double
      DataSingle    as Single
    End Union
   
      '' Set up our UDT.  Accepts any pointer.  Casts to the one we need.
    Declare Sub SetUDT( ByVal _MyObj_ as Any ptr )
   
      '' Assignment Operators:
        '' Assignment
    Declare Operator Let ( ByVal _rhs_ as Any Ptr )
        '' Additive
    Declare Operator += ( ByVal _rhs_ as Any Ptr )
        '' Subtractive
    Declare Operator -= ( ByVal _rhs_ as Any Ptr )
        '' Divisive
    Declare Operator /= ( ByVal _rhs_ as Any Ptr )
        '' Integrally Divisive
    Declare Operator \= ( ByVal _rhs_ as Any Ptr )
        '' Multiplicational
    Declare Operator *= ( ByVal _rhs_ as Any Ptr )
        '' Exponential
    Declare Operator ^= ( ByVal _rhs_ as Any Ptr )
   
      '' Casting.
    Declare Operator Cast() as uLong
    Declare Operator Cast() as Long
    Declare Operator Cast() as uInteger
    Declare Operator Cast() as Integer
    Declare Operator Cast() as uShort
    Declare Operator Cast() as Short
    Declare Operator Cast() as uByte
    Declare Operator Cast() as Byte
    Declare Operator Cast() as Double
    Declare Operator Cast() as Single
       
  End Type
 
End Namespace


If anyone has an actual use for this, I can work on it further, on the weekends. It's nice because you're not just limited to GET/SET of a property now, but instead you have all of the operators available. This really isn't a complete version. The next step would be a lot of debugging, and a bit much time for something I don't see anyone using.

How it works?

First, you have to remember that you can point to static functions. Good.

We have a list of operators within our ProcUDT data type. These operators simply point to a static sub. These are the ones you code yourself. You have to create each of your procedures as static, and then point the procedures.

Next, we have to set up our context. Assign all the sub pointers within procUDT to the static subs you have made. Let the procUDT also know what your THIS is, because we'll pass that later on. Static procs don't have a THIS. Set up your dataUDT and let it know the type of data you'll be filling in. All basic data types are supported (and we need to know for casting), except for string conversions.

Now we actually call our operator. What happens is that the DataUDT class has all the operators overloaded already - let, +=, -=, etc. We cast the rhs to the proper data type. We then call your custom overloaded operator for that particular operator. MyUDTProperty.Let = MyCustomLet

Code: Select all

  Operator DataUDT.Let ( Byval _rhs_ As Any Ptr )
    this.DataProc.ProcLet( this.DataProc.That, _rhs_ )
  End Operator
See? We just call yet another sub. That's the static one that you created.


Voila! :D

Return to “Tips and Tricks”

Who is online

Users browsing this forum: No registered users and 2 guests