Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Class library (OOP) - Help Thread


  • Please log in to reply
92 replies to this topic
bmcclure
  • Members
  • 774 posts
  • Last active: Jan 04 2014 10:44 PM
  • Joined: 24 Nov 2007
I like that idea. I preferred the original function names, but now I see the logic in changing them anyhow. Thanks for the reply!

animeaime
  • Members
  • 1045 posts
  • Last active: Jun 18 2011 04:44 AM
  • Joined: 04 Nov 2008
Well, in most cases, you will probably be calling the functions directly via their class name function, not the interface ones, right? The main thing I'm hoping from interfaces is to create a "standard" (like LogI, IniFileI, etc.); then, users would implement these classes making their own additions. The interfaces would be updated with additional functions as the need for such functions arise.

Since the class would implement the respective interface, you know that the core functionality remains consistent between the classes. Therefore, it would be easy to test out the different implementations and see which one does what you want.

The fact that interfaces would have the "I' says immediately that they are interfaces. I didn't use an "I" for the List interface, because I was using the Java List interface as a basis and wanted to preserve the naming. Also, with Array and Vector already devolped, there is no need for a "list" class.
As always, if you have any further questions, don't hesitate to ask.

Add OOP to your scripts via the Class Library. Check out my scripts.

bmcclure
  • Members
  • 774 posts
  • Last active: Jan 04 2014 10:44 PM
  • Joined: 24 Nov 2007
So how far do you go building functionality into the interface, as opposed to leaving the functionality for the calling class to implement?

Most of my DOM interfaces, probably because they didn't start off as interfaces, handle almost all of the processing internally, and really only require the calling class to construct the actual objects and add any non-official DOM functions. Should I be moving the functions out to the calling class and just referencing them from the interface, as is seen in Coin (save for its single static function)? Or is it acceptable to keep a large number of statics in an interface?

animeaime
  • Members
  • 1045 posts
  • Last active: Jun 18 2011 04:44 AM
  • Joined: 04 Nov 2008

So how far do you go building functionality into the interface, as opposed to leaving the functionality for the calling class to implement?

An interface is meant to be a shell. For example, the List interface defines functions which are used to manipulate lists. Then, the implementing classes (e.g. Array and Vector) implement these functions.

The only functions that should be defined in the interface are functions that are "final" (using java terminology). A final function is a function that can't be overridden. For example, the List_copy function is defined directly, because it performs the necessary task - there is no need to override it.

An interface, by design, is meant to "share" functionality. It is meant to define functions which a set of similar classes share.

Most of my DOM interfaces, probably because they didn't start off as interfaces, handle almost all of the processing internally, and really only require the calling class to construct the actual objects and add any non-official DOM functions. Should I be moving the functions out to the calling class and just referencing them from the interface?

I'm not sure I understand the question, but if you are asking if you should be creating an interface that implements the functions, I can only ask 1) what would the implementing class implement, and 2) why not just keep it as a class?

Interfaces, and inheritence, when added, have the overhead of a dynamic call and the function call to Class_call / Class_Scall. These functions must "figure out" which class to call. Did you run the two examples of the List_test (List_test1.ahk and List_test2.ahk)? If not, I would suggest doing so - this shows the added overhead in using interface functions.

Or is it acceptable to keep a large number of statics in an interface?

By statics, do you mean static functions. If so, static functions should be "rare"; at least, in my programming experience, rarely do I need a static function. For example, the getWorth function is static because a nickel, regardless, is worth 5 cents. Also, by making it a function (instead of, maybe, a constant in the constant function), it can be used as the "useFunction" paramater of the get and getAddress functions in the list interface (and implementing classes). For example, this is used in the CoinPurse object, to return the total amount of money in the CoinPurse via its getWorth function, which in turn uses the getWorth function in the coin interface.
As always, if you have any further questions, don't hesitate to ask.

Add OOP to your scripts via the Class Library. Check out my scripts.

bmcclure
  • Members
  • 774 posts
  • Last active: Jan 04 2014 10:44 PM
  • Joined: 24 Nov 2007
Thanks, that clears it up. I just need to further separate my logic from the interface and implement that in the classes.

The reason I'm using interfaces at all for the DOM is to follow the DOM specification... I want to create interfaces, and a set of default implementing classes, but allow anyone to create their own implementing classes however they see fit, as well.

Some of the interfaces truly need to be interfaces to function properly, I believe. Most of them directly correspond to real objects, however, and are just made into interfaces to separate the definition of the functions from the actual implementation of them.

I'm seeing the use in moving almost all of my logic outside of the interface and leaving it a shell, as it should be. Thanks again for helping clear that up.

bmcclure
  • Members
  • 774 posts
  • Last active: Jan 04 2014 10:44 PM
  • Joined: 24 Nov 2007
What about the possibility of supporting just the function name (with no Class name and underscore) for the FunctionName parameter of Class_call?

That would make it more useful to be able to call other classes without knowing the class name.

For instance, my DOM class needs to call a certain function within the class of an object it is passed in one of its methods.

Rather than calling Class_getClassName and then manually creating the function string, I'd like to be able to just do something like:
Class_call("canSet", Object, param1, param2, etc...)

Currently this will throw an error. Instead of a check to make sure there is an underscore in the name, perhaps there should just be a function to remove everything before the underscore in the function name to normalize it, and then figure out which class to call and append the class name and underscore again?

animeaime
  • Members
  • 1045 posts
  • Last active: Jun 18 2011 04:44 AM
  • Joined: 04 Nov 2008

perhaps there should just be a function to remove everything before the underscore in the function name to normalize it, and then figure out which class to call and append the class name and underscore again?

The reason it throws an error is that Class_call and Class_Scall have checks to verify that the specified class object implements the class specified by the function name. That's why interface functions pass A_ThisFunc for the first parameter. Since the function name is File_Func and File is the name of the interface, everything up to the first "_" is the interface name. Then, it verifies the specified object / class implements the interface.

However, I can write another function, without this check - how about "invoke" (as seen in the COM library).

Class_invoke(Object, "canSet", param1, param2, etc...)
This would call the canSet function in the class (as specified by Object). It would still make use of the reserved value ("C1@5s", internally, because it needs to know how many parameters are passed, so that only that number of parameters are passed in the dynamic call.


Since interfaces are just starting, should I reverse the parameters (object, then paramater) to have them match the format used by COM_Invoke (for consistency)? This would apply to the newly added functions Class_invoke and Class_Sinvoke and the existing Class_call and Class_Scall. This will break scripts that use Class_call and Class_Scall, but since interfaces just began, if the change is made, now is better than later. Also, it only affects the interfaces themself - not the calling of an interface function. So, it would be very easy to modify.
As always, if you have any further questions, don't hesitate to ask.

Add OOP to your scripts via the Class Library. Check out my scripts.

bmcclure
  • Members
  • 774 posts
  • Last active: Jan 04 2014 10:44 PM
  • Joined: 24 Nov 2007
I've been debating whether or not I think that the order of the parameters of Class_call should be changed, actually, and now that you bring it up, it seems like a good idea. It follows the normal OOP order of object.function(parameters).

I'm using Class_call in about 25 interfaces right now, but it would be a simple search and replace operation in each of them.

animeaime
  • Members
  • 1045 posts
  • Last active: Jun 18 2011 04:44 AM
  • Joined: 04 Nov 2008
Ok. sorry about that. I modified Class_call and Class_Scall to have ClassObject then FunctionName as the inputted parameters. Also, I added Class_invoke and Class_Sinvoke. The error messages match the ones from Class_call; "-3" is the error level (using the return from Class_callError) if the passed object isn't a class object (e.g. 0 or the empty string was passed).

For all four functions, you can call Class_callError to see if the call resulted in an error, and the error (if any, or 0 otherwise) will be returned. I've update the page on Interfaces to reflect the changed parameter order. I'll add a page for Class_invoke / Class_Sinvoke shortly.

The List and Coin interface were updated to use the new syntax. I used the below RegEx to swap the two parameters. This will swap the first two parameters for all Class_call and Class_Scall functions. It assumes that "A_ThisFunc" is the first parameter, which it should be if you followed the format used in the List and Coin interface.

;spacing (around parameters) and name for the second parameter are maintained.
;This RegEx works for both Class_call and Class_Scall
Find: (Class_S?call)\((\s*)(A_ThisFunc)(\s*),(\s*)(\w+)(\s*)(\)|,)
Replace: $1($2$6$4,$5$3$7$8

Download the updated zip.
As always, if you have any further questions, don't hesitate to ask.

Add OOP to your scripts via the Class Library. Check out my scripts.

bmcclure
  • Members
  • 774 posts
  • Last active: Jan 04 2014 10:44 PM
  • Joined: 24 Nov 2007
Modified the replacement value for backslashes instead of dollar signs, and the regex worked perfectly in EmEditor on all of my DOM interfaces at once :) Thanks for that.

And thanks also for invoke. I haven't been able to test yet, but I'm using it as required already in the DOM.

bmcclure
  • Members
  • 774 posts
  • Last active: Jan 04 2014 10:44 PM
  • Joined: 24 Nov 2007
Is it possible for invoke to accept the function name as a string instead of a ByRef?

To get a parameter of an object from an external class, I'd like to be able to pass Class_invoke(Object, "getSomeParam"), for instance. Right now it throws an error because it's looking for a variable reference instead.

animeaime
  • Members
  • 1045 posts
  • Last active: Jun 18 2011 04:44 AM
  • Joined: 04 Nov 2008

Is it possible for invoke to accept the function name as a string instead of a ByRef?

The function name isn't ByRef - the parameters are, though. Is this what you meant?

The parameters are ByRef so that if you call a function that has any ByRef parameters, calling the function via Class_invoke will work as expected.

I can change this, so that the parameters are no longer ByRef; however, this would mean Call_invoke won't work correctly when calling a function with ByRef parameters. However, it will allow calling Class_invoke by passing a value, instead of only allowing variable references.

This is the function's signature

Class_invoke(ClassObject, FunctionName, ByRef arg1 = "C1@5s", ByRef arg2 = "C1@5s", ByRef arg3 = "C1@5s", ByRef arg4 = "C1@5s", ByRef arg5 = "C1@5s", ByRef arg6 = "C1@5s")

As always, if you have any further questions, don't hesitate to ask.

Add OOP to your scripts via the Class Library. Check out my scripts.

bmcclure
  • Members
  • 774 posts
  • Last active: Jan 04 2014 10:44 PM
  • Joined: 24 Nov 2007
Sorry, I noticed that too; I must have misread the error message from AHK.

Yes, I was referring to the parameters. More often than not I'll be passing variables to Class_invoke for its parameters, but it would be nice not to have to.

You bring up a good point, though--it would also be expected to work when passing a byref, so changing it might not be the best option either.

I can't think of any graceful way to get around it; I could just assign the values to variables and use them with Class_invoke how it is now, or perhaps a second function could be created without byrefs, maybe Class_sinvoke (for string invoke) or something?

Now that I understand its purpose, it does seem like a good idea to leave the invoke function using ByRefs as you've designed it to do.

animeaime
  • Members
  • 1045 posts
  • Last active: Jun 18 2011 04:44 AM
  • Joined: 04 Nov 2008
Well Class_Sinvoke is taken - (Static) invoke. However, as a programmer, I would call the version you are talking about ByValue, whereas the current method is ByRef. So, maybe Class_Vinvoke (for byValue) and Class_SVinvoke for the static version would be good names. However, might I make a suggestion. You can store the values in a temporary variable and pass it.

Example
Class_invoke(Object, Function, arg1 := "Parameter 1", arg2 := 0xC001D06)


If this solution isn't satisfactory, I can add the forementioned Class_Vinvoke and Class_SVinvoke functions. However, if any parameters need to be ByRef, Class_invoke / Class_Sinvoke would be the one you would need to call; you could use the above solution for the parameters that are values.
As always, if you have any further questions, don't hesitate to ask.

Add OOP to your scripts via the Class Library. Check out my scripts.

bmcclure
  • Members
  • 774 posts
  • Last active: Jan 04 2014 10:44 PM
  • Joined: 24 Nov 2007
I think you're right, turning them into variables for the function call will probably be the easiest way to go, and will avoid the need to create additional functionality for Class.

It would be great if Class could automagically determine if a parameter should be a byref and pass it accordingly, but I don't think it's possible currently.