EEC-Developers / eec

Enhanced E Compiler - AmigaE programming
Other
12 stars 3 forks source link

Interface inheritance #15

Closed SamuraiCrow closed 7 months ago

SamuraiCrow commented 2 years ago

After looking into it extensively, interface inheritance is possible on E. It requires that the function pointers be stored in an E-list and that the number of parameters match. Because E is weakly typed, function pointers generate a warning each time they are used in EC. Also, there is so much that can go wrong in their implementation that I propose that the process of generating interfaces be automated.

To automate the process, a separate compiler can be used to generate the E source to create not only the interface itself, but also to generate preprocessor macros to simplify the implementation and usage. Of course AmigaOS 4.1 has this functionality built-in to its library structure and AROS includes OOP.library in its kernel but implementing it as a static lookup on other Amiga-likes is no problem if implemented in the EEC framework I'm working on. I'll start a companion issue in the framework repo.

SamuraiCrow commented 1 year ago

An interface can be represented in non-OS4 versions using a per-opener library that, in turn, opens a shared library as a base class. (Or at least I think so.)

SamuraiCrow commented 1 year ago

https://github.com/EEC-Developers/framework/issues/3 is the corresponding issue for the framework support for this feature.

SamuraiCrow commented 11 months ago

The fact that there may be multiple interfaces per library means that there will need to be an accompanying directory for each library that implements interfaces in OS3 and MorphOS.

libs:Libname.ifaces/main.iface libs:Libname.ifaces/debug.iface libs:Libname.library libs:Interface.library

Where Interface.library is the library that copies the jump table entries into the interface's jump table at load time. I'm not sure how legal that is under the partial memory protection on MorphOS though. The ObtainIFace call normally associated with exec.library on OS4 would be inherited from Interface.library.

Hypexed commented 8 months ago

The OS4 interfaces use some compiler magic. They solve the problem of needing a native jump table as well as being able to program it in direct C. They make use of an APICALL keyword that simply passes the interface pointer as first parameter. For OS4 newbie coders this is confusing as function compiler errors don't match the method calls because all the parameters are secretly offset by one. The interface takes the place of a library base, passed as first parameter, instead of as last parameter on 68K. It also solves the issue of a function table where a negative offset function was in place. Either way both would need a modified compiler AFAICT. Since both the OFFSET(A6) and APICALL are low level ABI conventions.

Though it would be suitable interfaces haven't been extended to make full use of dayatypes and clean them up. So OOP in the Amiga API is still the old hack it used to be. BOOPSI isn't fully OOPSI yet. :-)

SamuraiCrow commented 8 months ago

I was trying to implement Java-style interfaces as a safe supplement for single inheritance to expand. If OS4 uses magical constructs here, it might be better to implement OS3 and MorphOS first. What I've heard about OS4's library interfaces are that they use a slow addressing mode found only on PPC.

Hypexed commented 8 months ago

Once you know how it works it doesn't look so magical. The interfaces are standard C structs and methods are just function pointers. An interface is mainly a table of function pointers above some management fields. The compiler magic is a keyword that instructs it to do an API OS call which places the interface pointer as first parameter. Just like 68K compilers know an OS call needs A6 in place and jumps from it.

I'm not exactly sure what the issue is with the OS4 interface calls. But one thing I know is it used double indirection. However, I'm not sure it could be optimised better. The interface is loaded into a register, then function pointer loaded, then function called. I think the PPC calling method is more of a problem than the interfaces. Unlike 68K it can't call from a register offset. It needs to load the address into a register and call from the register. So even if the interface sits in a register any OS call needs to load the function into a call register then issue the call.

SamuraiCrow commented 8 months ago

On MorphOS, I think they used the sign bit to indicate a native call as negative and used an MMU hook to invoke the call. That way the 68k stuff wouldn't have to do anything special with the positive addresses.

SamuraiCrow commented 8 months ago

As for the jump tables on 68k, it's double indirection as well but in a less obvious way. The jump table entries are absolute addressed jump instructions themselves.

Hypexed commented 8 months ago

I know on MorphOS that the 68K API running on native PPC was handled differently on OS4. They used a memory block that holds all the 68K registers used which is then passed to an ABox function which calls the particular library function. So it's somewhat indirect. And the QBox calls would be called direct. But they've changed it so there is more direct access. IIRC that was for native code as well which called Amiga functions.

On OS4 they just used SysV ABI and ported the functions over for native code. Only from 68K are they called like on MorphOS with a register block. Where 68K jump functions call a trap that jumps to native stub routine that pulls the parameters from the block and calls native routine.

SamuraiCrow commented 8 months ago

I believe I've found another way to implement my hash table without requiring inheritance of the hash node structure. Dependency injection of the get_key() and hash_function() procedures eliminate the need to inherit from the hash_node object altogether.

SamuraiCrow commented 7 months ago

Function pointer edition of Framework is now merged. Case closed as interface inheritance is unneeded.