Operator_Convert

Operator_Convert is a very handy method to implement on a lot of classes. It not only lets you create a mechanism to make your classes convert themselves into other types but it also lets you create new instances FROM other data types.

So let’s start with the simple forms of operator_convert – the “convert TO” forms.

Suppose you have some class defined called “myClass”

Class myClass
End Class

and you want to make it easy to have this class write log messages to the DebugLog. You most certainly could write a “ToString” method as an explicit way to convert this class to a String

Class myClass
   Function ToString() as string
   End Function
End Class

and then in your code you’d use it like :

dim instance as new MyClass

// lots of intervening code

system.debuglog instance.ToString

And maybe this isn’t so bad. It IS explicit which is most times a good thing. Your code will have a certain clarity to it because of this style.

But there is another way to do much the same thing that is also effective – despite it being slightly hidden and magical like a variants ability to automatically convert itself to other data types by assignment. And this “magic” is operator_convert

In a class like our sample

Class myClass
   Function Operator_Convert() as string
   End Function
End Class

And now your usage could look like

dim instance as new MyClass

// lots of intervening code

system.debuglog instance

Since System.Debuglog takes a string as a parameter the compiler figures out that our class has a suitable Operator_convert method and will call that for us.

You can define many operator_convert functions that convert TO other datatypes and as long as they dont cause any ambiguity you will get the results you expect. You may get an ambiguous situation of you define a Convert To operator_convert for Integer, Int32 and/or Int64 since Integer is simply an alias to one of those types depending on whether you are building for 32 or 64 bit. There can be others but this is the most common.

There is another form for operator_convert – the “convert FROM” form.

In this case operator_convert behaves in many ways like a constructor called with a parameter. However one thing to remember is that Operator_convert is NOT a constructor! Its an initializer at best. If your class depends on constructors being called make sure when you use the convert FROM form you call your constructor !

For our sample class suppose we want to initialize it from a string.

Class myClass
   Function Operator_Convert() as string
   End Function

   Sub Operator_Convert(initializeWith as string)
      // if we rely on the constructor having been called 
      // then call the Constructor here !
       
      // now set our class up from whatever data is in the
      // parameter passed
   End Sub

End Class

and we could use it like

dim instance as MyClass = "123"

// lots of intervening code

system.debuglog instance

Something to note is that these conversions can appear to be hidden away from a casual reader so use them with care and consideration. Often explicit code, like the ToString example above aids a reader in understanding the code because its right there in front of them that you are asking the class to give you a string representation for some purpose.

Keep that in mind and use these with that in mind.

Enjoy !

Using interfaces (again)

A recent comment on my post about overriding prompted me to write this second post about interfaces and why you might use them. Its clear to me that I havent done a very good job of explaining why you should or would use them.

In some languages you can create classes that inherit from one other and one other only. Xojo is this way. Some others let you inherit from several superclasses but this has a set of issues that Xojo avoids by limiting things to single inheritance. Java is also this way and does not support multiple inheritance.

So what do you do in the case you have a class you’ve created and you want it to “inherit” from multiple other classes ? Suppose you have a class that needs to inherit from “InputStream” so it can be read from AND also needs to inherit from “OutputStream” so it can be written to ? Xojo has such a class – BinaryStream.

The way to achieve this is actually NOT to try & inherit from two super classes but to make the InputStream and OutputStream into interfaces and then have your class implement both interfaces. In fact this IS what BinaryStream does – it implements both the Readable and Writeable interfaces so you can read from it and write to it.

You implement the methods on each interface and then any other methods on your code that takes a Readable parameter you can pass a BinaryStream to, since BinaryStream IsA Readable. And any of your code that takes a Writeable parameter you can also pass the BinaryStream to since it IsA Writeable.

As well you can extend interfaces just like any other class.

So its perfectly legal to write a method like

Sub DoREadableStuff(extends instance as Readable)

just as you would for any other type in Xojo.

This gives you a lot of flexibility in how you use & implement interfaces.

About the only thing you might run into is what to do if you have two interfaces that you want a class to implement and they have methods that you have to implement that are named the same ?

As long as the full signature of the methods, method name and all parameters, are unique this isnt a problem as you can implement both and you will just appear to have 2 or more overloads of the same method just taking different parameters.

But what if they have the same parameters ?

If you just add both interfaces then you will have to methods with the same name and the same parameters and Xojo will not compile your program any more because its ambiguous which one would be used when.

And this is where a little used aspect of Xojo comes into use.

Lets set up a small example so you can follow along as that will make things much clearer.

Start a new desktop project.

To the project add

  • a class named class1
  • a class interface named interface1
  • add a method to interface1 named Connect
  • a second class interface named interface2
  • add a method to interface2 also named Connect

Now we’re going to make it so class1 implements both interface1 and interface2. Click on Class1 and make sure the Inspector is showing. Click on the Interfaces button in the Inspector. Select both Interface1 and Interface2. I often also select the “insert #pragma error” in each method option at the bottom of the pane.

Click OK and empty stubs for all the methods in Interface1 and Interface2 will be added to Class1. And note we do have 2 connect methods.

If you try to run or analyze the project you will get a “duplicate method definition” error – this is exactly as expected since we have two identical signatures (method name and parameters)

And now we’ll fix this. What we need to tell Xojo is that “this method implements the Connect method from interface1 and this other method implements the Connect method from Interface2”.

Right click on the first Connect method. And select the “Show Implements” option from the contextual menu. Notice that when you do this an extra field shows up in the Inspector.

This extra field is used for exactly what we just stated – telling Xojo which Connect method each of the Connect overloads actually implement. So for the first one we’ll put in the field Interface1.Connect. Then click the second Connect method and in the field put Interface2.Connect

You’d think we’re done at this point because those two method now say which method from which interface they implement. However, they are still exact duplicates and the compiler will still complain about that. So rename the first one Interface1Connect and the second Interface2Connect. I’ve renamed this way JUST for clarity for this example. You could rename them anything you liked that doesnt cause other errors.

And now for the proof that this works.

In our sample applications Window1.Open event (or opening for those using 2019r2 and newer) lets put in the following code



Dim i As interface1 = New Class1
break
i.Connect

Dim j As interface2 = Interface2(i)
break
j.Connect

Recall that a Class1 IsA Interface1 because it implements that interface. And that it is also an Interface2 because it also implements that interface. If you run this code now you wont get any errors BUT it is hard to see that it is doing exactly what we’ve told it to.

If you run & step INTO each call to Connect you will see that because in the first case i IsA Interface1 that stepping into i.Connect does indeed call Interface1Connect – the exact method we said implemented the Interface1.Connect method.

And when you get to the second break statement and step into j.Connect you will see that, because j IsA Interface2 that this call to Connect will end up in Class1’s Interface2Connect method – the one set said implemented Interface2.Connect.

Sorry about this week folks

I’m feeling a little under the weather and just dont feel up to writing more posts

Hopefully this will pass soon enough

FWIW IF there is a topic you’d like me to cover drop me a comment or email

Overriding

Sometimes when you create a subclass you want to your subclass to do something different than the class it inherits from. But you want to retain the API or reuse the method name because its perfectly suited.

When you do this and add a method that has the EXACT SAME signature as the one in the superclass you are overriding the method. The signature includes the method name, parameters and their types but excludes the return type.

This can be very useful if you have a class that has many specialized subclasses. For instance, the framework has several subclasses of TCP Sockets; HTTPSockets, SMTPSockets and POP3Sockets. What an HTTP Socket does when you call it Connect method may be different than what the SMTP Socket does when you call its Connect method and also different from what the POP3 Socket does when you call its connect method.

Suppose you have classes like :

Class BaseClass
	Sub Foo()
	  System.debuglog CurrentMethodName
	  
	End Sub
End Class

Class SubClassA
Inherits BaseClass
	Sub Foo()
	  System.debuglog CurrentMethodName
		  
	End Sub
End Class

Class SubclassB
Inherits BaseClass
	Sub Foo()
	  System.debuglog CurrentMethodName
		  
	End Sub
End Class

And code that uses these classes as


Dim c As BaseClass

c = New SubClassA

c.foo

c = New SubclassB

c.foo

What you will see in the debuglog is something like this on macOS.

10:09:20 PM : My Application Launched
              SubClassA.Foo
              SubclassB.Foo
10:09:27 PM : My Application Ended

Whats going on ?

Methods are, unlike properties, virtual. What that means is that the correct method to call will be determined NOT at compile time, but at runtime and based on the actual instance and its method list. This is really handy in lots of ways as I already noted.

You can, as we did in our code, declare the type as the most generic – like we did – since all subclasses are instance of the subclass AND whatever the superclasses that its inherited.

In our code above an instance of SubClassA is both a SubclassA AND a BaseClass.

There are some glitches that you can still run into. For instance this bug report notes one spot where the correct method to call is NOT based on the runtime type but the compile time type (which is really counter intuitive)

That said, overriding is very useful in a class hierarchy.

Have fun with this.

Faking named parameters

Xojo doesnt support named parameters. And I dont expect they are likely to add it any time soon. But, you can kind of fake it for yourself with a bit of work.

Xojo supports both variadic parameter lists, ones that take an unbounded list of parameter, and a declarative syntax for creating pairs as literals.

You can combine this into faking named parameters.

If you define a method as

Sub foo( paramarray namedParams as Pair )

You can call it like

Foo( "param1":1, "param2":"2" )

and the method will get an array of pairs named namedParams that is composed of pairs. Each pair in the list will, for this example, have its left property holding the name and the right property holding he parameter value.

You can iterate over the array and pull out the relevant values into local variables and the method behaves as if you support “named” parameters like :

dim param1 as integer
dim param2 as integer

for each param as pair in namedParams
  select case true
    case param.left = "param1"
      param1 = param.right.integervalue
    case param.left = "param2"
      param2 = param.right.stringvalue
  else   
     // here ypou cane do whatever you want 
     // but I chose to raise an exception
     Raise new UnsupportedOperationException
  end select
next

You could take this further and add an overload that supports positional parameters like

Sub foo( param1 as integer, param2 as string )

and if you do this you probably want to make sure the named parameter version validates that the types of values for the named versions are consistent with the positional version.

And now you can call with named params or positional params and you can decide whether internally the named one calls into the positional param one or vice versa so you dont have to write the same code twice – just put a different API on it.

Thanks !

Flat out I’ll say Thanks to Xojo for the 2.1 decision to revert the event name changes. Of all the changes that one was probably the hardest one to deal with as there’s no easy way to #if around an event definition or event handler.

Properties may still cause some grief if you start a project in 2019r2 or r2.1 since they don’t retain the “old” values” so going back to an old version can be tricky if you start in 2019r2 or 2.1. At least this can be worked around by starting projects in 2019r1.1 or older and its fairly simple to make it so every project behaves like this by using a template that overrides the normal built in one.

WOW !

All I can say is WOW !

The Eagles reuniting was the first time I saw hell freeze over

And now I’ve seen it a second time

You’ll see what I mean

All I can say is OMG THANK YOU !!!!!!!!!

Just wait. You’ll see what I mean in the not too distant future.

Downgrade 2019.R2 to 2019.R1.1

It had to happen sooner or later. But now questions about how to do this are appearing. And the answer is “it depends”.

ALWAYS use a copy or have a backup copy of your entire project JUST in case something goes wrong.

IF you have a license that permits you to save as text (Xojo Project) save your project as a text project. You then need to open the single file called projectname.xojo_project

At the very beginning of this file you will see entries like the following :

Type=Desktop
RBProjectVersion=2019.02
MinIDEVersion=20190200
OrigIDEVersion=20190200

Change the number after MinIDEVersion to 20070100 and save. You can now open the project in whatever version you want.

In an XML project you would see

<?xml version="1.0" encoding="UTF-8"?>
<RBProject version="2019r2" FormatVersion="2" MinIDEVersion="20190200">
<block type="Project" ID="0">

Again change the number following MinIDEVersion to 20070100. Make sure you keep the quotes around the value. And save.

Binary is a lot tougher because it is a proprietary format. And to edit that you WILL need a licensed version of Arbed. Once you have that you can open a binary project and select the top entry which represents the project and change the OriginalVersion to 20070100 and the ProjectSavedInVers to 2007.01 and save.

And now you can open your project in old versions again.