Function Pointers in Spin2
cgracey
Posts: 14,206
I am thinking about how to implement function pointers in Prop2 Spin.
Is it needful that function pointers can point outside of the object?
I ask because doing function pointing within an object is straightforward, but having function pointers work beyond the scope of the object is difficult - it requires two 32-bit pointers, exceeding a handy long.
Is it needful that function pointers can point outside of the object?
I ask because doing function pointing within an object is straightforward, but having function pointers work beyond the scope of the object is difficult - it requires two 32-bit pointers, exceeding a handy long.
Comments
Wouldn't that break Spin's scope rules?
An object "pointer" or reference would allow calling a public function in one object from another object. That would be kind of like a function pointer.
I would be very interested in inheritance if you can do it. Inheritance would allow one object to extend another object's capability instead of having to create another set of functions to do the same things. Today, one has to completely replicate an object to extend it, and that makes for some wasteful code. Inheritance would allow making objects that have small variations in functionality. If one could over-load a given function in an inherited object that would be even better. Constructors, etc... not necessary.
It would break the scope rules.
The thing is, when pointing to another object's PUB/PRI, you need to point to THREE things: that object's code base, that object's variable base, and that object's PUB/PRI. This would take two longs and a byte. It's not very convenient.
We can overload an object today in Spin because there is no object array bounds checking. This effectively gives us a function pointer. Basically we make an object array of one and add other objects to the list, then choose the object to deliver the function by using an out of bounds object array index.
Steve is right, IMHO there isn't any value in pointing to a function, I abhor function callbacks because they are ugly and error prone. I've seen some bad interfaces that used the callback model to handle async I/O.
I would think that having the ability to pass object references would have more value for any callback mechanism. I'm not certain this isn't permitted in SPIN now.
What would make this more useful is the ability to declare objects as local variables instead of requiring them to always be global level entities. I would refer to these objects as anonymous objects, since they only live in the scope of the stack frame.
I am willing to be convinced that function pointers are of use, but I think this could make SPIN messy and only be applicable to 1:100 programmers, perhaps even worse odds than that. I've learned that sometimes it's best to avoid adding complex features, only a very small percentage of people can understand and exploit those complex features.
I'm also interested in STRUCTs and UNIONs. A union allows you to alias a variable space, just like you can with labels in PASM, but often times you will do things like byte address a portion of a long, or bit address a portion of a byte, it makes high level language operations more readable and you focus less on manipulating bits and bytes in the code you write.
Absolutely yes! One common usage would be an output formatter object, to which you pass a pointer to a character output routine that exists yet in another object.
-Phil
The proper context for such usage would be to have an object (class) that implements a standard interface, then you pass a pointer to the object. This would go nicely with Steve's request for inheritance so you can subclass a standard formatter object. In practice you may end up only writing code for that one function, but you pass it as an object instance.
Function pointer callbacks appear more often in non-OOP languages because OOP brought order and abstracts the callback mechanism away from the programmer. It may all work very similar at the bytecode level, but you aren't forcing the programmer to use archaic mechanisms in a modern language.
Here's what can be done very simply and elegantly with one long: A simple pointer to any PUB/PRI without any variable base or code base, which would mean that the PUB/PRI can only use local variables and make no references to its own object's DAT or VAR sections, or make any PUB/PRI calls, unless they are by pointer, as well. Is that even useful, though?
Maybe we just need a special PUB/PRI pointer variable type that can be assigned and utilized, only (for a full two-long-plus-one-byte structure for tracking the whole context).
So, it would be good if an object's functions could be called, but have a mechanism for those functions to call back to the calling object for I/O operations, for example?
I think that would mean instantiating an object from within your object, then handing it a pointer, so that it could call back to your object for some required purpose. That would come down to a function pointer, wouldn't it? I mean a function pointer that sets up the context (PBASE/VBASE) as well as the PUB/PRI number for that object. In contrast, an object pointer would contain just the PBASE/VBASE, but not a specific PUB/PRI, as that would be selectable. But then how would the selection be accounted for, being just an index. I think the index built in would be more useful, don't you?
Wow, prototypal inheritance. Let's morph Spin into JavaScript:)
http://javascript.crockford.com/prototypal.html
PHP implements subclassing in it's object model, but it doesn't implement multiple-inheritance. With multiple-inheritance you can subclass an I/O class and a numbers class and string class, but then substitute your own implementations of certain functions or swap in from different objects.
Basically it's like an upside down tree, where multiple classes can come together to form one class. You can then overload functions or declare your own code with the same calling parameters.
This all gets very messy very quick, but there can be some strict rules to help make it simpler. I will refrain from giving advice on those exactly, since there are at least 2 people on this forum that are much more capable than me (David and Eric).
That's a little unfair, he is only asking for inheritance, not all the crazy stuff JavaScript has evolved into.
Yes! (See my post #7.) Although, I would prefer to pass a reference to an individual character output method, rather than to the object itself. That eliminates problems when one object uses out and another uses tx, for example. The syntax could be really simple and intuitive, too:
'No special function or object type required. It's up to the programmer, as in Spin I, to do the right thing. And forget about classes, inheritance, and polymorphisms.
Chip, please make the backend as complicated as it has to be -- even if it means an extra compiler pass -- in order to keep the frontend simple and intuitive.
Thanks,
-Phil
I want to make them BOTH simple, you know.
-Phil
Then, inside of the implementation of TextFormatter.dec you would see code like:
And VGA_Terminal would define a PUB called "out" that would handle the terminal-specific character output.
As I said originally, this could also be done (probably more cleanly) by having VGA_Terminal inherit from TextFormatter.
prt.hex(@sio, 123456)
Then you have strongly identified interfaces. The C++ STL abstracts this away somewhat with the iostream class. You can implement instances of the << and >> operators and the objects can communicate with streams, without actually requiring some underlying system be stream oriented, it's just an interface.
With inheritance you would define a standard object such as io.spin, then that object is inherited by FullDuplexSerial, which re-implements functions that match the defined calling interface.
Then Formatter can take any object that is derived from io.spin as a parameter:
PUB hex (io, number)
'do something
io.write(stringvalue)
Furthermore, you might do something like this if anonymous locally scoped objects were supported:
PUB hex (io, number) | num:numbers.spin
io.write(num.hex(number))
It just occurred to me that polymorphism would be a tough one to get right because SPIN doesn't have variable typing in function calls. Because parameters are not typed, there is no value in overloading functions. The only time this might be useful is situations where you have multiple functions of the same name, but different number of parameters.
None of this is super exotic, it just requires logic in the compiler to pick the correct functions at compile time. C++ is largely deterministic at compile time, with the exception of virtual methods.
So calling calling such function through a simple pointer means it has no idea which instances state it should be using. It could only uses it's own local variables.
Not much use is it?
So, for example, if I have three instances of FDS and pass a pointer to the "tx" method to someone, when that tx is called it has no idea what serial port it applies to.
This language design business is a bit hairy.
Thumbs up, agree 100%
LOL, it will just end up driving us to C++ and making the syntax look as much like SPIN as possible with pre-processor macros!
I agree.
Of course in that case we don't need the @, the name of the object should suffice. Just like the name of any other variable.
Then I'd want to do something like:
prt.setStream(sio)
prt.hex(123456)
Something doesn't sit right with me about the procedural syntax for passing a handle to the object. Alas, you can't really do it any other way without really tearing apart the language.
The most elegant way would be to have a constructor that is called at object instantiation time, supporting bot static objects like SPIN 1, but also supporting a syntax like:
Yes, you can do exactly the same thing using the procedural model, but it would be nice to have a single statement that has parity with other languages, though I suddenly realize there is too much ambiguity in SPIN to permit the latter syntax, so just forget my suggestion.
Agreed, having a pointer to a function, without any context, is only mildly useful and I'm sure there are other clever ways to do the same thing without having it as a mainstream language capability.
I would argue for trying to maintain an immediate syntax as much as possible, that way we are passing around objects, an @ reference in the call is ~okay~, but the compiler should be able to infer that anyway, since it knows the variable is an object.
-Phil