I'm going to try to make a step by step guide to using Extension Builder and wrapping foreign code libraries.
This IS NOT a beginners guide to Extension Builder, you should learn the basics of Extension builder syntax before moving on to this. There are existing tutorials for that already. I'm concentrating on Foreign Function binding for now, since there really isn't any good tutorials available for this task.
I feel like I finally know this stuff well enough to teach most of the basics of it to others. I've used it plenty 'as-is' and I've used it to wrap C, C++ and Objective C libraries and make some 'native' widgets. I don't have a lot of experience wrapping Java stuff with it (I did do a Java Android extension many years ago, before switching to iPhone), nor do I have experience wrapping javascript stuff with it (which there aren't many examples of wrapping JS that are in the public sphere).
Before you begin It would be helpful if you have at least some knowledge of others programming languages (specially the ones you need to tap into) and the 'variable types' that are commonly used with them.
Unlike the xTalk Scripting language, Extension Builder is 'strongly typed' (although it does have some flexibility), which means EVERY variable you use MUST be declared before you can use the variable. When you declare a variable you are telling the compiler what type of data to expect that will be stored in that variable so that the appropriate amount of memory may be reserved for storing the contents of the variable.
Variable types may be one of several alien concepts to you if your've only ever used scripting languages. Popular scripting languages and xTalk(s) tend to be 'loosely typed', and so that you don't normally have to declare a variable type at all. Instead the script interpreter engine will infer a variable's type from the context that it's being used in. So if you have a number 2 in some variable and a string of the text character "2" in another variable you can still add them together and the script engine will automatically convert the string "2" into a number 2 and add them together resulting in the number 4. That's not the case with 'strongly typed' languages. The previous example would have resulted in an error because you can't add a number to a text character. Unlike xTalk scripting, Extension Builder is NOT a loosely typed language!
Extension Builder has its own generic equivalents for common variable types for types like Boolean, String, Integer, etc, These types may be equivalent to a 'C' variable type, but you can also use some variable types that are specific to a foreign language such as 'C'. For C language these types look like 'CInt' which is a C signed Integer that can hold an integer number between -127 and 127, which requires a single byte of memory to store its value, or it may look like 'CUInt' which is unsigned integer that can hold an integer number between 0 and 255, which also requires a single byte of memory to store its value. You can also create custom 'foreign' variable types and 'alias types' for other types, but I won't get too into that for now.
_____________________________________________________________________________________________________
I'll start by breaking down a C API foreign function binding string example from a thread 'over there':
private -- means this handler will be invisible outside of the context of the current Extension module.private __safe foreign handler _ReadBackAnalogOut (out rBuffer as CInt) returns nothing binds to "c:K8055D>ReadBackAnalogOut!stdcall"
__safe -- there is safe and unsafe context, I'm a bit sketchy on this. The docs say: Foreign handlers are always considered unsafe, and thus may only be called from unsafe context - i.e. from within an unsafe handler, or unsafe statement block. The _safe keyword seems to be undocumented. I've only seen it used with C++ APIs. The bottom line is foreign handlers that have this keyword tells Extension Builder its OK to be called outside of the 'unsafe/end unsafe' structure.
foreign handler -- as opposed to a regular handler, this let's us know we are binding to some external code
_ReadBackAnalogOut -- the name we'll be using to call this foreign handler, naming conventions here are up to your own discretion
(out rBuffer as CInt) -- inside the parens are declaring what the the foreign handler is expecting to receive (in) or send back (out) or both (inout) when this external code is being called. In this case it's declaring that the foreign code will return a parameter that should be a C signed integer (as CInt) but you might also be able to use LCB's type 'as Number' or any other variable type that's compatible with a C signed int. When I say compatible what I mean is the data can fit, byte size wise, into the type you're declaring. So make sure you're using the correct type. A unsigned C integer (as CUint) can fit a signed int, but will give you the wrong number range. rBuffer is an arbitrarily named variable but the lead 'r' is a commonly used convention for indicating it's a variable that's being return.
returns nothing -- the foreign function will return nothing (null), at least not in the normal way, in the case of this handler the variable you pass for 'rBuffer' is will be filled (out) with the result of the foreign code. With many APIs the return value here will simply be 'returns CBool' (or Boolean) that indicates success or failure of the call.
binds to -- Always needed, just indicates the next quoted string is the actual binding string for the foreign handler, that gives libFFI some clues about the code being called.
"c:K8055D>ReadBackAnalogOut!stdcall" --
The binding string starts with "c:" which says the library or API we want is C based. It can be one of c: (which covert C++ too), java: or objc: (and maybe others? js:? And I see mention of Pascal in the docs).
Followed by K8055D>, so K8055D is the library or API name to look for, followed by '>'. The actual foreign code library can be included in platform_cpu specifically named folders in your extension project folder, or the foreign code library may be an API (or .Framework for MacOS) part of the OS or in user installed common shared library folder in the OS. I'm not sure of the exact order that memory or directories are searched for libraries / function symbols. Like if there were two copies of the same library available, I'm not sure if it would load the included copy first or another one in the system one first.
ReadBackAnalogOut -- ReadBackAnalogOut is the name of the actual function we want to use from the foreign code library.
!stdcall is the calling convention, with C on windows can be one of default, stdcall, thiscall, fastcall, cdecl, pascal, or register. All but default are Win32-only. I've only had to change this once so far, most of the time you probably don't need to worry about it. On Java / Android this can be one of instance, static, or nonvirtual. I haven't done much with Android in some time so I don't know anything about those calling conventions.
Now in some regular handler you then call like _ReadBackAnalogOut( tMyCint ), tMyInt would be an integer variable you've already declared in the calling handler with 'variable tMyInt as CInt' or variable tMyInt as Integer'.
A lot of times the the returned data or object may need further processing to get the data into a format that's useful for our scripting language. For example ObjC's NSColor Red Blue Green components might be floating point numbers between 0.00000 and 1.00000, so we would multiply them by 255 to get to be 8 bit RGB values we can use with our scripts language.
The binding strings look a bit different depending on if you're binding to c:, java:, or objc: foreign code. You can, and in the case of Apple probably need to, mix c: and objc: APIs together in the same module (C based CoreAudio AND Objective C based AVAudio APIs for example)
The foreign code is only resolved when it's called so you won't know if you got the foreign binding strings correct until you actually try to use that foreign handler.
Besides knowing how to bind to the foreign code, you will probably need to learn about the API you're trying to use too, although if you're like me you might stumble through learning about an API WHILE you're trying to write bindings for it. And of course there's bound to be some 'gotchas' along the way too that will test your spirit! Like for examples dealing with returned data that's in a different byte order, or maybe you get back a Pointer to data or an Object that exists elsewhere and need to parse the block of data a pointer points to (variable foreign type declarations is a whole other thing to know) or need to query an object for its relevant properties.
Once you start to get the feel of it, it does get a little easier. Like I've said, I've used ChatGPT to type out a lot of binding strings for me nowadays, and most of the time it even makes valid binding strings. If a stupid AI can get the hang of it then I know you can too! If xTalk is ever going to be on par with languages like Lua or Python, then I think it needs to have plenty of libraries available so people can use it to do a wide variety of things.
There's some undocumented things that I want to cover on on here too. Like binding the functions in the Scripting Engine itself, it's 'Foundation' stuff.
Here are all of the related reference docs from 'All Guides', although there isn't a lot of plain English:
https://github.com/OpenXTalk-org/OpenXT ... -naming.md
https://github.com/OpenXTalk-org/OpenXT ... form-id.md
https://github.com/OpenXTalk-org/OpenXT ... cedence.md
https://github.com/OpenXTalk-org/OpenXT ... ference.md
https://github.com/OpenXTalk-org/OpenXT ... 20Guide.md
https://github.com/OpenXTalk-org/OpenXT ... g%20OXT.md
https://github.com/OpenXTalk-org/OpenXT ... ference.md