Equivalent of classes in LiveCode

LiveCode is the premier environment for creating multi-platform solutions for all major operating systems - Windows, Mac OS X, Linux, the Web, Server environments and Mobile platforms. Brand new to LiveCode? Welcome!

Moderators: FourthWorld, heatherlaine, Klaus, kevinmiller, robinmiller

Post Reply
mwieder
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 3581
Joined: Mon Jan 22, 2007 7:36 am
Location: Berkeley, CA, US
Contact:

Re: Equivalent of classes in LiveCode

Post by mwieder » Fri Aug 21, 2020 10:58 pm

Brian- the recursive method works fine if there are missing constructors, but it requires that each constructor has the header code that passes the call down the line. If there's a constructor that doesn't have that then the chain breaks. That's why I prefer your list method.

mwieder
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 3581
Joined: Mon Jan 22, 2007 7:36 am
Location: Berkeley, CA, US
Contact:

Re: Equivalent of classes in LiveCode

Post by mwieder » Fri Aug 21, 2020 11:06 pm

More...

Code: Select all

   dispatch "setColor" to the behavior of button "CarObject2" with "Yellow"
That should be illegal. I'd issue a red card.
RedCard.jpeg
RedCard.jpeg (11.56 KiB) Viewed 6561 times
The correct invocation is

Code: Select all

   dispatch "setColor" to button "CarObject2" with "Yellow"
etc. You shouldn't be allowed to call an object's class/superclass except from within the object itself.

aetaylorBUSBnWt
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 118
Joined: Thu Sep 20, 2012 5:11 pm

Re: Equivalent of classes in LiveCode

Post by aetaylorBUSBnWt » Sat Aug 22, 2020 12:12 am

Hi,

Another question, I am thinking about the process of having multiple sets of classes for different kinds of code that should be independent of each other - not even in the same application, but you don't want to have to copy/paste stuff to use them.

To me that would mean that there is a "classes" card for each of these, not sure of the naming convention to support such.

But such would only want a single OOPengine stack.

Right now the OOPengine stack expects everything to be on a Classes card based on the constant in the OOPengine stack.

How would one support multiple class cards in a single stack or multiple stacks with a single class card per stack?

bwmilby
Posts: 438
Joined: Wed Jun 07, 2017 5:37 am
Location: Henrico, VA
Contact:

Re: Equivalent of classes in LiveCode

Post by bwmilby » Sat Aug 22, 2020 3:37 am

mwieder wrote:
Fri Aug 21, 2020 11:06 pm
More...

Code: Select all

   dispatch "setColor" to the behavior of button "CarObject2" with "Yellow"
That should be illegal. I'd issue a red card.
But this is exactly what is being done here:

Code: Select all

private command invokeConstructorsOf pObject
   # get the behavior chain for the constructors
   local tClass, tClassList
   put the behavior of pObject into tClass
   
   repeat while tClass is not empty
      put tClass & cr before tClassList
      put the behavior of tClass into tClass
   end repeat
   delete the last char of tClassList
   
   # call the class constructors in order
   repeat for each line tClass in tClassList
      dispatch the short name of tClass to tClass
   end repeat
end invokeConstructorsOf
The dispatch should be:

Code: Select all

dispatch the short name of tClass to pObject
There is no native way in LiveCode to address a handler in the parent script (behavior) of the current object and get control back if it has the same name as any handler below it. My first stab at the constructor had code that would add a parameter and do a recursive dispatch but that had the issue of not handling classes missing a definition (i.e. if you didn't include the template code to pass, the message would stop there since it didn't know about needing to skip a level). In this case it was easy to write it to not use recursion with the benefit of being able to handle missing definitions with no additional work.

All of the examples that are shown where the object (self) is added as a parameter (with...) will require additional work inside the class to maintain the data separated per object (as demonstrated by my example that was "carded" which made no effort to do so).
Brian Milby

Script Tracker https://github.com/bwmilby/scriptTracker

bwmilby
Posts: 438
Joined: Wed Jun 07, 2017 5:37 am
Location: Henrico, VA
Contact:

Re: Equivalent of classes in LiveCode

Post by bwmilby » Sat Aug 22, 2020 4:22 am

aetaylorBUSBnWt wrote:
Sat Aug 22, 2020 12:12 am
To me that would mean that there is a "classes" card for each of these, not sure of the naming convention to support such.
How would one support multiple class cards in a single stack or multiple stacks with a single class card per stack?
There is no issue having a card with the same name in multiple stacks. If you have buttons with the same name on multiple of those card you may run into trouble. In the demo, the classes card was in the same stack where it was used. If you wanted to keep the classes in a totally separate stack, then I'd need to experiment a little to determine how to make it work (if it didn't work already).

You can also have multiple cards in the stack with the same name, but then you can't reference them by name and know what you are going to get. In a simple test, the objects on the second copy were not identifiable by name, you would need to use the id. The OOPEngine code that uses the card name wouldn't work as currently written. Easiest thing would probably be to add some form of registration code to the OOPEngine where you would index the class definitions (could pass the long ID of each card containing classes and it would cache the long ID and short name of each class). This would be somewhat similar to including header files (in effect, but quite different in implementation).

The engine would be able to use something like "if sClasseId[pClass] is not empty then..." and then "set the behavior of tNewObject to sClasseId[pClass]" where the sClasseId array would contain the index of class name to long ID of the button implementing the class.
Brian Milby

Script Tracker https://github.com/bwmilby/scriptTracker

mwieder
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 3581
Joined: Mon Jan 22, 2007 7:36 am
Location: Berkeley, CA, US
Contact:

Re: Equivalent of classes in LiveCode

Post by mwieder » Sat Aug 22, 2020 5:14 am

Brian-

the "invokeConstructorsOf" command is part of the OOP infrastructure and is only activated on new object creation. It can't be called from anywhere else.
Objects can access handlers up their own behavior chain but otherwise there should be no direct access to class handlers. Unfortunately there's no way to close off access, so it's just an honor system.

Here's an attempt at general classes:
There's a new card "Classes" of the OOPEngine stack itself that contains a misnamed "LinkedList" class button. Misnamed because it so far only has functions for a FILO queue. But it's usable as such, and here's a *very* simple stack that instantiates a LinkedList group.
The Particles stack functions as before because it has its own local classes.

I'm not convinced that a card of the OOPEngine stack is the best place to store the general classes, but it does work. I'm thinking that probably a separate directory with script-only stacks would be a better repository and the OOPEngine stack would create its list of classes on openstack.
[Update: I did that - put the Classes folder in your Plugins folder]
Attachments
OOPMWLivecode.zip
(92.37 KiB) Downloaded 168 times

bwmilby
Posts: 438
Joined: Wed Jun 07, 2017 5:37 am
Location: Henrico, VA
Contact:

Re: Equivalent of classes in LiveCode

Post by bwmilby » Sat Aug 22, 2020 3:15 pm

Changes to linked list:

Code: Select all

local sCreateTime

command LinkedList
   <snip>
   put the milliseconds into sCreateTime
end LinkedList

function getCreateTime
   return sCreateTime
end getCreateTime
Here is my button code:

Code: Select all

on mouseUp
   local tList1, tList2
   
   put newObject("list1", "LinkedList") into tList1
   put the name of tList1 & cr after msg
   
   dispatch function "getCreateTime" to the behavior of tList1
   put the id of tList1 && "behavior" && the result & cr after msg
   
   put newObject("list2", "LinkedList") into tList2
   put the name of tList2 & cr after msg
   
   dispatch function "getCreateTime" to the behavior of tList2
   put the id of tList2 && "behavior" && the result & cr after msg
   
   dispatch function "getCreateTime" to tList1
   put the id of tList1 && the result & cr after msg
   
   dispatch function "getCreateTime" to tList2
   put the id of tList2 && the result & cr after msg
end mouseUp
This is what is in message after that:

Code: Select all

LinkedList
group "LinkedList"
1006 behavior 1598104525100
LinkedList
group "LinkedList"
1007 behavior 1598104525138
1006 
1007 
After relaunching LiveCode, I changed one line of code in OOPEngine:

Code: Select all

dispatch the short name of tClass to pObject -- instead of tClass
Message box results were:

Code: Select all

LinkedList
group "LinkedList"
1006 behavior 
LinkedList
group "LinkedList"
1007 behavior 
1006 1598104796940
1007 1598104796947
(When I tried it without relaunching LiveCode, the behavior values still had the last value from the first try.)

P.S. I think the end of the first line of the push command is missing ["next"]
Brian Milby

Script Tracker https://github.com/bwmilby/scriptTracker

mwieder
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 3581
Joined: Mon Jan 22, 2007 7:36 am
Location: Berkeley, CA, US
Contact:

Re: Equivalent of classes in LiveCode

Post by mwieder » Sat Aug 22, 2020 4:40 pm

Thanks, Brian.

Yes, there's a missing ["next"]. That was really just supposed to be a quick code stub as a proof of concept rather than a working queue class. I do have a more fully-fleshed out linked-list class that I'll have to dig out of the archives. Probably some ten years old by now.

And thanks for the change to the constructor invocation code. That does remove the remaining call into an abstract class.
And as you noted in your mouseUp code, you don't need to call into the behavior (class) code, just into the object itself. That's proper OOP architecture.

More troublesome, though, is the fact that I messed up the newObject code. The class name should be the first argument.
So the first line of the newObject function in the OOPEngine stack should use the class name, not the object name.

Code: Select all

   put classIDFromName(pClass) into tClassID
So the arguments in the button mouseUp code to the newObject calls should be reversed like

Code: Select all

   put newObject("LinkedList", "list1") into tList1

bwmilby
Posts: 438
Joined: Wed Jun 07, 2017 5:37 am
Location: Henrico, VA
Contact:

Re: Equivalent of classes in LiveCode

Post by bwmilby » Sun Aug 23, 2020 4:37 am

I've been tracking all of the code for OOPEngine in this thread in a Git repository on my computer. I decided to go ahead and publish it on GitHub to make it easier for others to view/compare. There are 2 branches currently. My changes (based on Mark's initial update) are in the master branch. All of Mark's other updates are in the mark branch.

My latest change is to add the concept of registering classes that I mentioned as a way to have them be stored in arbitrary locations. The only real constraint is that all buttons on the provided card should be class definitions. The other is that the class names need to be globally unique (since we are referencing them by the short name). Registering a duplicate class will throw an error. Re-registering a class (same name and ID) will not.

https://github.com/bwmilby/OOPEngine

I will add a README document to provide credit and links to the original articles and this thread.
Brian Milby

Script Tracker https://github.com/bwmilby/scriptTracker

mwieder
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 3581
Joined: Mon Jan 22, 2007 7:36 am
Location: Berkeley, CA, US
Contact:

Re: Equivalent of classes in LiveCode

Post by mwieder » Sun Aug 23, 2020 5:18 am

[I posted this as a comment in a pull request for the github repository, but reposting here for general access]

Brian- thanks for posting the git repository. However, here's where we branch in different directions. If I'm reading this properly, your registerClasses paradigm needs the classes card to be on the same stack as the application, where I'm heading in the direction of a globally-accessible repository for generic classes (e.g., my LinkedList and Queue text files). My original impetus to place them in the Plugins folder works in the development environment, but not for standalones.

I'm not sure how we'll reconcile these two different directions.

aetaylorBUSBnWt
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 118
Joined: Thu Sep 20, 2012 5:11 pm

Re: Equivalent of classes in LiveCode

Post by aetaylorBUSBnWt » Sun Aug 23, 2020 2:40 pm

I think of classes in terms of functionality.

One thing I am developing is OAuth1.0A - I need it because the vendor of the web site I am accessing has no concrete plans of upgrading at this moment, if at all.

So I want that as a separate chunk of code with its classes that I can include in my overall project.
I am trying to code it so that I can swap it out if/when the vendor changes to a newer OAuth.

There are other components that are class oriented that need to be included that I see as other other class libraries.

In all cases I would have subclasses of these class libraries that are unique to the specific application and don't need to exist outside that application.

From what I understand the proper method of doing this is that each of these class sources should be a separate stack that can then be included in an application project as needed.

bwmilby
Posts: 438
Joined: Wed Jun 07, 2017 5:37 am
Location: Henrico, VA
Contact:

Re: Equivalent of classes in LiveCode

Post by bwmilby » Mon Aug 24, 2020 1:39 pm

@Mark
I replied in the PR. I’ll put more here later this evening.

One thing I’m working on is registering stacks as classes. I wrote the code last night but didn’t get to test it. This would allow easy inclusion of relevant classes in a project. If the class needed to encapsulate some other binary objects then a normal stack could be used. If not, then a stack only script could be used. The already committed code will allow a card from a stack to be registered which will index all of the classes (buttons) on that card and make them available.
Brian Milby

Script Tracker https://github.com/bwmilby/scriptTracker

aetaylorBUSBnWt
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 118
Joined: Thu Sep 20, 2012 5:11 pm

Re: Equivalent of classes in LiveCode

Post by aetaylorBUSBnWt » Mon Aug 24, 2020 8:46 pm

Hi,

One thing I have been thinking about is the newObject call. One of its parameters is for naming the object.

I am thinking that this is a LiveCode bias that may not be used. I can't think of a time I ever want a name for an object.
I have a pointer to it.
For LiveCode, we still need a variable somewhere that contains the object that was created, independent of the name.
In order to access the object by name, the name would need to be unique, making it even more unlikely to be used.

The example application, Particle, does not name the objects created either and it creates lots of visible buttons running around the screen.

A typical C++ application would never name the objects. Classes need names.

Possibly use that parameter for specifying the source card for the Class that you want to create?

aetaylorBUSBnWt
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 118
Joined: Thu Sep 20, 2012 5:11 pm

Re: Equivalent of classes in LiveCode

Post by aetaylorBUSBnWt » Mon Aug 24, 2020 10:21 pm

I have been working on the means to call any method in the superClass with the proper data and testing bits of Mark's modified OOPengine.

The following do work:

dispatch "reportBehav" to the behavior of me with target
or
dispatch "reportBehav" to the behavior of me with long id of me

From that I attempted to use the "superClass pCommand" command in OOPengine.
It does not work as I expected because it does not include the object ID, so it goes to the super class method, but uses a different object for data. (that function has since been deleted from the library.)

While it may be obvious to long time LiveCoders, some of the syntax to newbies (me for one) is mystifying.

I just discovered the subtle difference between calling functions and commands and calling a command with multiple parameters JUST FEELS WRONG! Especially since you can have commands and functions with the same name.
But then you can call a command with parenthesis IF there is only one parameter!
I also discovered that "the result" is persistent, across multiple function/command calls if no other function/command modifies it <urk>.

Anyway, when you want to call functions / commands in a superClass, I suggest the following for OOPengine in order to be precise and cause crashes if you don't provide the right parameters in the right order. There is also a consistency to the syntax.

Code: Select all

command superCommand pCommand, pObjectID
   if the behavior of pObjectID is not empty then
      // need to yank following put from final code
      put "from Stack superCommand: " & "Target: " & the target & cr & "method: " & pCommand & cr & "name ID: " & name of pObjectID & cr into field 1
      dispatch pCommand to the behavior of pObjectID with pObjectID
      return the result
   end if
   return "unhandled"   --what is the proper way to specify unhandled?
end superCommand

function superFunction pFunction, pObjectID
   if the behavior of pObjectID is not empty then
      // need to yank following put from final code
      put "from Stack superFunction: " & "Target: " & the target & cr & "method: " & pFunction & cr & "name ID: " & name of pObjectID & cr into field 1
      dispatch function pFunction to the behavior of pObjectID with pObjectID
      return the result
   end if
   return "unhandled"   --what is the proper way to specify unhandled?
end superFunction
Then calling a super Class command or function becomes:

Code: Select all

superCommand "methodName", objectID
--or
if superFunction( "methodName", objectID) then /*whatever*/ end if
The above are the simplest implementations, but do not acommodate the fact that commands and functions are very likely to have parameters. For that, after a bunch of experimenting I found a way to do this. but it complicates the receiving command/function because it needs to detect that it was called from a subclass and unpack the parameters itself.

I did attempt to use the params() and param functions to pass parameters instead of building my own list, but the end result is more complicated stuff ending up at the receiving function and then more complicated work to interpret what was received.

Here is the modified version of superFunction:

Code: Select all

function superFunction pFunction, pObjectID
   if the behavior of pObjectID is not empty then      
      local tParams
      --THIS SEEMS THE WAY TO DO THIS, IS IT REALLY?
      put empty into tParams
      if paramCount() > 2 then
         repeat with i = 3 to the paramCount
            put param(i) & comma after tParams
         end repeat
         delete last char of tParams
      end if
      dispatch function pFunction to the behavior of pObjectID with pObjectID, tParams
      return the result
   end if
   return "unhandled"
end superFunction
So an example of this would be:

Code: Select all

....
   if superFunction("reportBehav", pid, "SecondParam", "ChickenParm") then end if
....
and

Code: Select all

function reportBehav pid, p2ndParam, p3rdParam
  /*    We know we have 1 parameter, for pid. Check paramCount() against function's desired parm count.
      if less than that, need to extract from second parameter
      The following parameter code needs to be customized for every class function that has parameters
      and could be called by a subclass.
      */
      if paramCount() < 3 then
         put item 2 of p2ndParam into p3rdParam
         put item 1 of p2ndParam into p2ndParam
      end if

--rest of function's code
Thoughts?

mwieder
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 3581
Joined: Mon Jan 22, 2007 7:36 am
Location: Berkeley, CA, US
Contact:

Re: Equivalent of classes in LiveCode

Post by mwieder » Mon Aug 24, 2020 11:52 pm

Andrew-

I'm missing something here. Why

Code: Select all

superClass pClassCommand
when you can just say

Code: Select all

pClassCommand
or

Code: Select all

pClassCommand param1, param2, etc.
Isn't that part of the whole reason for subclassing?

Same thing with functions in the classes:

Code: Select all

put pClassFunction(param1, param2) into tLocalVariable
If I'm missing something obvious (wouldn't be the first time), do clue me in.

Post Reply

Return to “Getting Started with LiveCode - Experienced Developers”