libraries:machine_code:mcl

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
libraries:machine_code:mcl [2023-12-16 23:51] – created geeklibraries:machine_code:mcl [2023-12-17 03:08] (current) – Fix broken markup geek
Line 1: Line 1:
 ====== MCL [v1][v2] ====== ====== MCL [v1][v2] ======
- 
-A new kind of machine code library for AutoHotkey. 
  
 THIS IS ALPHA SOFTWARE THIS IS ALPHA SOFTWARE
  
-Currently only supports AutoHotkey V1 U32/U64.+MCL is a new kind of machine code library for AutoHotkey, which takes a no-nonsense approach to converting compiled code to AutoHotkey embedded machine code. It requires a GCC-based cross-compiler installed under PATH (named as gcc.exe/%%g++%%.exe or x86_64-w64-mingw32-gcc.exe/x86_64-w64-mingw32-%%g++%%.exe) to run the tests, examples, or to compile any code. 
 + 
 +It was designed to work with mingw-w64 installed via [[https://www.cygwin.com/|Cygwin]], and mingw-w64 installed via [[https://www.msys2.org/|MYSYS2]], but has been tested to work with [[https://jmeubank.github.io/tdm-gcc/|TDM-GCC]] as well. 
 + 
 +Once your C or %%C++%% code has been compiled to a standalone function or class, that standalone class will have no dependency back to MCL. Scripts using your compiled code will need to include //just// the compiled code, and nothing else. You can check the releases of [[libraries:json:cjson]] for an example of what compiled code would look like in a script. 
 + 
 +MCL has a few extra features compared to other machine code tools, the big ones being: 
 + 
 +  * Support for both C and %%C++%% 
 +  * Includes some basic hand-written replacements for some of the most common standard headers 
 +  * Support for both 32 and 64 bit 
 +  * Allows very large constants (and Doubles, which GCC tends to promote to being a global constant, breaking code under other tools) 
 +  * Allows exporting one or more functions to AutoHotkey 
 +  * Allows exporting one or more global variables to AutoHotkey 
 +  * Generates a wrapper with all the DllCalls/NumGet/NumPut calls pre-bound with offsets and types 
 +  * Allows importing functions from external DLLs into the C/%%C++%% code, such as from the Windows API or any other DLL you have laying around 
 +  * Generates standalone AutoHotkey code with the compiled machine code and wrappers, so you can include compiled code without including the MCL library. 
 + 
 +Altogether, MCL has just enough features that a solid subset of standard C code can be compiled under it, and run (more or less) fine. 
 + 
 +The primary goal of MCL is to enable much more complex machine code functions/libraries without resorting to a dll. For example [[libraries:json:cjson]] which is compiled under MCL, and makes use of global variables, "exporting" multiple functions, and floating point numbers. 
 + 
 +MCL also supports a tiny subset of %%C++%% (no STL, exceptions, or RTTI). For example, see [[https://github.com/G33kDude/MCL.ahk/blob/main/Tests/CPP/Spooky.cpp|the SpookyHash test]], which uses a %%C++%% implementation of the [[https://burtleburtle.net/bob/hash/spooky.html|SpookyHash]] hashing algorithm. 
 + 
 +MCL also adds the ability for code to "import" functions from DLLs in the same way you can with ''DllCall''. This feature doesn't replace DLL files, and isn't the primary goal of the project. Just think of it as a cherry on top. 
 + 
 +And a (low level) warning for this feature: There is a bug with mingw-w64 compilers which results in the stack being misaligned, which will cause a crash the next time a Windows API (or standard library) function is called from your C/%%C++%%. This bug can be tracked [[https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54412|here]]. 
 + 
 +For an example of "importing" functions, see [[user:cloakersmoker]]'s [[https://github.com/CloakerSmoker/PDFGen.ahk|PDFGen example library]], which uses a regular C library with a few headers changed around along with an incredible number of imported standard library functions. 
 + 
 +----
  
-Requires a gcc-based Windows %%C/C++%% compiler (for examplemingw-w64)MSVC is not supported.+If you have any questions about the libraryplease feel free to join [[https://geekdude.io/discord|the Discord]] and ping [[user:geek]] or [[user:cloakersmoker]] in ''#mcl-mcode-lib''.
  
 If you run into any problems, please report them as GitHub issues. If you run into any problems, please report them as GitHub issues.
Line 22: Line 50:
 The header ''mcl.h'' provides macros for "talking" to AHK from your code, mainly, this means exporting a function to AHK, or having AHK import a function from a ''.dll'' file before loading your code. The header ''mcl.h'' provides macros for "talking" to AHK from your code, mainly, this means exporting a function to AHK, or having AHK import a function from a ''.dll'' file before loading your code.
  
-However, this header is not needed. By default, the ''__main'' function will be called if there are no exported functions, or if ''mcl.h'' is not used at all.+However, this header is not needed. By default, the ''%%__main%%'' function will be called if there are no exported functions, or if ''mcl.h'' is not used at all.
  
 These macros are as follows: These macros are as follows:
Line 56: Line 84:
 In AHK, MCL provides all functionality through the ''MCL'' class.  In AHK, MCL provides all functionality through the ''MCL'' class. 
  
-Any method which is described as "returning compiled code" returns an object that wraps the exported functions and global variables as methods and properties respectively. The object is also callable, in which case it will invoke any export named "main".+Any method which is described as "returning compiled code" returns an object that wraps the exported functions and global variables as methods and properties respectively. The object is also callable, in which case it will invoke any export named "%%__main%%".
  
 Now, for the API: Now, for the API:
Line 67: Line 95:
 For any method which takes an ''Options'' parameter, the following options can be provided to control the bitness/format of the generated code: For any method which takes an ''Options'' parameter, the following options can be provided to control the bitness/format of the generated code:
  
-  * ''MCL.Options.OutputAHKBit'' generates code which will run in `A_PtrSize * 8AHK. So, on AHK U32, this flag tells MCL to generate 32 bit code. On AHK U64, this flag tells MCL to generate 64 bit code.+  * ''MCL.Options.OutputAHKBit'' generates code which will run in ''A_PtrSize * 8'' AHK. So, on AHK U32, this flag tells MCL to generate 32 bit code. On AHK U64, this flag tells MCL to generate 64 bit code.
   * ''MCL.Options.Output32Bit'' generates 32 bit code, ignoring the bitness of the AHK executable.   * ''MCL.Options.Output32Bit'' generates 32 bit code, ignoring the bitness of the AHK executable.
   * ''MCL.Options.Output64Bit'' generates 64 bit code, ignoring the bitness of the AHK executable.   * ''MCL.Options.Output64Bit'' generates 64 bit code, ignoring the bitness of the AHK executable.
Line 88: Line 116:
 Generally, any language feature supported by standard C should work. If you find something that doesn't, please report it as an issue. Generally, any language feature supported by standard C should work. If you find something that doesn't, please report it as an issue.
  
 +===== Examples =====
 +
 +==== CPlusPlus with New and Delete ====
 +
 +<tabbox AutoHotkey v1>
 +<runner ahk>
 +#Include <MCL>
 +
 +CPP = 
 +( %
 +
 +#include <stdlib.h>
 +
 +class Point {
 +public:
 + Point(int NX, int NY) {
 + X = NX;
 + Y = NY;
 + }
 +
 +private:
 + int X;
 + int Y;
 +};
 +
 +Point* __main(int X, int Y) {
 + return new Point(X, Y);
 +}
 +
 +)
 +
 +pCode := MCL.FromCPP(CPP)
 +
 +pPoint := DllCall(pCode, "Int", 20, "Int", 30, "Ptr")
 +
 +MsgBox, % NumGet(pPoint+0, 0, "Int") ", " NumGet(pPoint+0, 4, "Int")
 +</runner>
 +<tabbox AutoHotkey v2>
 +<runner ahk2>
 +#Requires AutoHotkey v2.0
 +#include <MCL>
 +
 +lib := MCL.FromCPP("
 +(
 +#include <stdlib.h>
 +
 +class Point {
 +public:
 + Point(int NX, int NY) {
 + X = NX;
 + Y = NY;
 + }
 +
 +private:
 + int X;
 + int Y;
 +};
 +
 +MCL_EXPORT(Call, Int, x, Int, y, Ptr)
 +Point* Call(int X, int Y) {
 + return new Point(X, Y);
 +}
 +)")
 +
 +pPoint := lib(20, 30)
 +
 +MsgBox NumGet(pPoint, 0, "Int") ", " NumGet(pPoint, 4, "Int")
 +</runner>
 +</tabbox>
 +
 +==== C as Library ====
 +
 +<tabbox AutoHotkey v1>
 +<runner ahk>
 +#Include <MCL>
 +
 +C =
 +( %
 +
 +#include <mcl.h>
 +
 +MCL_EXPORT_INLINE(int, Add, (int Left, int Right)) {
 +    return Left + Right;
 +}
 +
 +MCL_EXPORT_INLINE(int, Multiply, (int Left, int Right)) {
 +    return Left * Right;
 +}
 +
 +int unused() {
 +    return 20;
 +}
 +
 +)
 +
 +Code := MCL.FromC(C)
 +
 +Added := DllCall(Code.Add, "Int", 300, "Int", -20, "Int")
 +MsgBox, % Added
 +
 +Multiplied := DllCall(Code.Multiply, "Int", Added, "Int", 2, "Int")
 +MsgBox, % Multiplied
 +</runner>
 +<tabbox AutoHotkey v2>
 +<runner ahk2>
 +#Include <MCL>
 +
 +lib := MCL.FromC("
 +(
 +#include <mcl.h>
 +
 +MCL_EXPORT(Add, Int, left, Int, right, Int)
 +int Add(int left, int right) {
 +    return left + right;
 +}
 +
 +MCL_EXPORT(Multiply, Int, left, Int, right, Int)
 +int Multiply(int left, int right) {
 +    return left * right;
 +}
 +
 +int unused() {
 +    return 20;
 +}
 +)")
 +
 +Added := lib.Add(300, -20)
 +MsgBox Added
 +
 +Multiplied := lib.Multiply(Added, 2)
 +MsgBox Multiplied
 +</runner>
 +</tabbox>
 +
 +==== C Return Struct to AHK ====
 +
 +<tabbox AutoHotkey v1>
 +<runner ahk>
 +#Include <MCL>
 +
 +C =
 +( %
 +
 +#include <stdlib.h>
 +
 +typedef struct {
 + int X;
 + int Y;
 +} Point;
 +
 +Point* __main(int a, int b) {
 + Point* P = malloc(sizeof(Point));
 +
 + P->X = a;
 + P->Y = b;
 +
 + return P;
 +}
 +
 +)
 +
 +Code := MCL.AHKFromC(C, MCL.Options.DoNotFormat) ; Compile and stringify the code, but don't format it as an AHK string literal (since we're going to load it again momentarily)
 +
 +pCode := MCL.FromString(Code)
 +
 +pPoint := DllCall(pCode, "Int", 20, "Int", 30, "CDecl Ptr")
 +
 +MsgBox, % NumGet(pPoint+0, 0, "Int") ", " NumGet(pPoint+0, 4, "Int")
 +</runner>
 +<tabbox AutoHotkey v2>
 +<runner ahk2>
 +#Include <MCL>
 +
 +lib := MCL.FromC("
 +(
 +#include <stdlib.h>
 +
 +typedef struct {
 + int X;
 + int Y;
 +} Point;
 +
 +MCL_EXPORT(Call, Int, a, Int, b, Ptr)
 +Point* Call(int a, int b) {
 + Point* P = malloc(sizeof(Point));
 +
 + P->X = a;
 + P->Y = b;
 +
 + return P;
 +}
 +)")
 +
 +pPoint := lib(20, 30)
 +
 +MsgBox NumGet(pPoint, 0, "Int") ", " NumGet(pPoint, 4, "Int")
 +</runner>
 +</tabbox>
 +
 +==== C Set Global from AHK ====
 +
 +<tabbox AutoHotkey v1>
 +<runner ahk>
 +#Include %A_ScriptDir%/../
 +#include MCL.ahk
 +
 +C =
 +( %
 +
 +#include <mcl.h>
 +
 +int SavedValue = 10;
 +MCL_EXPORT_GLOBAL(SavedValue);
 +
 +int __main(int NewValue) {
 + int Result = SavedValue;
 +
 + SavedValue = NewValue;
 +
 + return Result;
 +}
 +
 +)
 +
 +Code := MCL.FromC(C)
 +
 +NumPut(20, Code.SavedValue, 0, "Int")
 +MsgBox, % DllCall(Code.__main, "Int", 30, "CDecl Int")
 +MsgBox, % DllCall(Code.__main, "Int", 40, "CDecl Int")
 +MsgBox, % NumGet(Code.SavedValue, 0, "Int")
 +</runner>
 +<tabbox AutoHotkey v2>
 +<runner ahk2>
 +#Include <MCL>
 +
 +lib := MCL.FromC("
 +(
 +#include <mcl.h>
 +
 +MCL_EXPORT_GLOBAL(savedValue, Int)
 +int savedValue = 10;
 +
 +MCL_EXPORT(Call, Int, newValue, Int)
 +int Call(int newValue) {
 + int result = savedValue;
 + savedValue = newValue;
 + return result;
 +}
 +)")
 +
 +lib.savedValue := 20
 +MsgBox lib(30)
 +MsgBox lib(40)
 +MsgBox lib.savedValue
 +</runner>
 +</tabbox>
 +
 +==== C with Floats ====
 +
 +<tabbox AutoHotkey v1>
 +<runner ahk>
 +#Include <MCL>
 +
 +C =
 +( %
 +
 +double __main(double In) {
 + return In * 2.5;
 +}
 +
 +)
 +
 +pCode := MCL.FromC(C)
 +
 +MsgBox, % DllCall(pCode, "Double", 11.7, "Double")
 +</runner>
 +<tabbox AutoHotkey v2>
 +<runner ahk2>
 +#Include <MCL>
 +
 +lib := MCL.FromC("
 +(
 +#include <mcl.h>
 +MCL_EXPORT(Call, Double, in, Double)
 +double Call(double in) {
 + return in * 2.5;
 +}
 +)")
 +
 +MsgBox lib(11.7)
 +</runner>
 +</tabbox>
 +
 +==== C Write to File ====
 +
 +<tabbox AutoHotkey v1>
 +<runner ahk>
 +#Include <MCL>
 +
 +C =
 +( %
 +
 +#include <stdio.h>
 +
 +void __main(int Value) {
 + FILE* f = fopen("test.txt", "w");
 +
 + fputs("Hello world!\n", f);
 + fprintf(f, "The number is: %i\n", Value);
 +
 + fclose(f);
 +}
 +
 +)
 +
 +pCode := MCL.FromC(C)
 +
 +DllCall(pCode, "Int", 2931)
 +
 +MsgBox % FileOpen("test.txt", "r").Read()
 +</runner>
 +<tabbox AutoHotkey v2>
 +<runner ahk2>
 +#Include <MCL>
 +
 +lib := MCL.FromC("
 +(
 +#include <mcl.h>
 +#include <stdio.h>
 +MCL_EXPORT(Call, Int, value)
 +void Call(int value) {
 +    FILE* f = fopen("test.txt", "w");
 +
 +    fputs("Hello world!\n", f);
 +    fprintf(f, "The number is: %i\n", value);
 +
 +    fclose(f);
 +}
 +
 +)")
 +
 +lib(2931)
 +MsgBox FileRead("test.txt")
 +</runner>
 +</tabbox>
 +
 +==== Import DLL with Headers (Lua) ====
 +
 +<runner ahk2>
 +#Requires AutoHotkey v2
 +#DllLoad Z:\ahk2\lua\lua54.dll
 +#Include <MCL>
 +FileCopy "Z:\ahk2\lua\include\*.h", A_WorkingDir
 +
 +lib := MCL.FromC("
 +(
 +#include <mcl.h>
 +#include <stddef.h>
 +#include <string.h>
 +#include "lua.h"
 +#include "lauxlib.h"
 +#include "lualib.h"
 +
 +#define MCL_IMPORT_SOURCE lua54
 +
 +MCL_IMPORT_DECLARED_FROM(luaL_newstate);
 +MCL_IMPORT_DECLARED_FROM(luaL_openlibs);
 +MCL_IMPORT_DECLARED_FROM(luaL_loadstring);
 +MCL_IMPORT_DECLARED_FROM(lua_pcallk);
 +MCL_IMPORT_DECLARED_FROM(lua_close);
 +
 +lua_State *L;
 +
 +MCL_EXPORT(init, Int)
 +void init() {
 +    L = luaL_newstate();
 +    luaL_openlibs(L);
 +}
 +
 +MCL_EXPORT(dostring, AStr, str, Int)
 +int dostring(char* str) {
 +    return luaL_loadstring(L, str) || lua_pcall(L, 0, 0, 0);
 +}
 +
 +MCL_EXPORT(close, Int)
 +void close() {
 +    lua_close(L);
 +}
 +)")
 +
 +lib.init()
 +lib.dostring('print("Hello from Lua!")')
 +lib.close()
 +</runner>