{"id":22283465,"url":"https://github.com/y-less/indirection","last_synced_at":"2026-01-06T13:05:39.382Z","repository":{"id":61831926,"uuid":"110391527","full_name":"Y-Less/indirection","owner":"Y-Less","description":"PAWN function pointers.","archived":false,"fork":false,"pushed_at":"2023-11-12T22:04:16.000Z","size":217,"stargazers_count":12,"open_issues_count":3,"forks_count":5,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-01-30T17:39:22.309Z","etag":null,"topics":["pawn","pawn-package","sa-mp","sa-mp-development","samp"],"latest_commit_sha":null,"homepage":null,"language":"Pawn","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Y-Less.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-11-12T00:25:32.000Z","updated_at":"2025-01-09T23:19:20.000Z","dependencies_parsed_at":"2023-11-12T23:20:48.776Z","dependency_job_id":"68fbc814-cefb-4387-afac-3757915d4010","html_url":"https://github.com/Y-Less/indirection","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Y-Less%2Findirection","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Y-Less%2Findirection/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Y-Less%2Findirection/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Y-Less%2Findirection/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Y-Less","download_url":"https://codeload.github.com/Y-Less/indirection/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245535405,"owners_count":20631293,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["pawn","pawn-package","sa-mp","sa-mp-development","samp"],"created_at":"2024-12-03T16:40:30.955Z","updated_at":"2026-01-06T13:05:39.331Z","avatar_url":"https://github.com/Y-Less.png","language":"Pawn","funding_links":[],"categories":[],"sub_categories":[],"readme":"# `indirection.inc`\n\n[![sampctl](https://shields.southcla.ws/badge/sampctl-indirection-2f2f2f.svg?style=for-the-badge)](https://github.com/Y-Less/indirection)\n\nIndirection is a system for calling function pointers in a generic and type-safe way.  Instead of `CallLocalFunction`, `Call`, `defer`, `Callback_Call`, or any other method, this gives one common interface which can be extended by library authors; utilising tags for compile-time parameters:\n\n```pawn\nCaller(Func:func\u003ciisi\u003e, playerid)\n{\n\t@.func(playerid, _:func, \"Hello World\", random(5));\n}\n\nCallee(playerid, func, string[], rand)\n{\n}\n\nmain()\n{\n\tCaller(\u0026Callee, 42);\n}\n```\n\n## Installation\n\nSimply install to your project:\n\n```bash\nsampctl package install Y-Less/indirection\n```\n\nInclude in your code and begin using the library:\n\n```pawn\n#include \u003cindirection\u003e\n```\n\n## Usage\n\n### Basics\n\nIf the library supports it, you can call a function that takes another function using `\u0026`:\n\n```pawn\nFuncThatWantsACallback(\u0026MyCallback);\n```\n\nYou can also use y_inline:\n\n```pawn\nFuncThatWantsACallback(using inline MyInline);\n```\n\nOr you can use `addressof` from amx_assembly:\n\n```pawn\nFuncThatWantsACallback(addressof(MyCallback));\n```\n\nFunctions that take other functions are type-safe, so a function that expects a callback that expects two integers will have a type of:\n\n```pawn\nFuncThatWantsACallback(Func:callback\u003cii\u003e)\n{\n}\n```\n\nHere `ii` is the standard notation for two integer parameters.  If you try and pass a function that does not take two integer parameters, the compiler will give a `warning 202: number of arguments does not match definition`:\n\n```pawn\nCaller(Func:func\u003ciisi\u003e, playerid)\n{\n\t@.func(playerid, _:func, \"Hello World\", random(5));\n}\n\nCallee(playerid, func, string[])\n{\n}\n\nmain()\n{\n\tCaller(\u0026Callee, 42); // `Callee` is not `iisi`, but just `iis`.\n}\n```\n\nFunction pointers are just normal variables, and so can be passed around between functions as normal:\n\n```pawn\nCaller(Func:func\u003ciisi\u003e, playerid)\n{\n\t@.func(playerid, _:func, \"Hello World\", random(5));\n}\n\nIntermediate(Func:func\u003ciisi\u003e)\n{\n\tCaller(func, 42);\n}\n\nCallee(playerid, func, string[], rand)\n{\n}\n\nmain()\n{\n\tIntermediate(\u0026Callee);\n}\n```\n\nThe available \"types\" are: `i` - any integer, `f` - floats, `a` - arrays, `s` - strings, `v` - references (`\u0026var`), `x` - varargs (`...`, must be last), `tTag:` - tagged with `Tag:`.\n\n### Metadata\n\nYou can attach a single piece of metadata to a function.  This is arbitrary data that you can access via the function handle:\n\n```pawn\nIndirect_SetMeta(func, 5);\nprintf(\"%d\", Indirect_GetMeta(func)); // Prints 5\n```\n\nThis allows you to associate things like handles, for example a timer handle:\n\n```\nstock SetTimerCallback(Func:func\u003c\u003e, time, bool:repeat)\n{\n\tIndirect_Claim(func);\n\tnew timer = SetTimerEx(\"@y_inlineTimerInvoke\", time, repeat, \"ii\", _:func, !repeat);\n\tIndirect_SetMeta(func, timer);\n\treturn _:func;\n}\n```\n\nThere is also a second type of metadata, which you can pass at call time instead to `@` to configure how the call is made:\n\n```pawn\n@.timer[1000, true](playerid);\n```\n\nAssuming `timer` is a handle to some timer function, the `1000` and `true` would configure how the timer is called, while the `playerid` is a parameter to the timer function itself.  This separates information about how the function is called from information passed to the function.  `INDIRECTION_META_DATA_SIZE` defines how much metadata can be stored and defaults to `8`; it is stored in the variable `INDIRECTION_META` and the amount is in `INDIRECTION_COUNT`.\n\n### Callbacks\n\nThere is special handling in-built for callbacks that start with `On`.  This is almost identical to `call` from y_als (indeed it is designed to replace it):\n\n```pawn\n@.\u0026OnPlayerConnect(playerid);\n```\n\nAs with most calls this tries to use `CALL@` for both the address and parameter types, but with some special handling to not use the lastest hook if there are any.  These macros are pre-defined for all standard SA:MP callbacks in y_als, but can be custom defined for any other callback too:\n\n```pawn\n#define CALL@OnCallbackExample%8() OnCallbackExample%8(0, 0.0, \"\", 0)\n```\n\n## Library Writers\n\nA parameter that takes a function pointer has the tag `Func:`, and is followed by the type specifier for the function.  For example `OnPlayerCommandText` would be `Func:func\u003cis\u003e` and `OnUnoccupiedVehicleUpdate` would be `Func:func\u003ciiiffffff\u003e`.  In a function signature this would look like:\n\n```pawn\nMyFunction(playerid, Func:func\u003cifis\u003e)\n{\n}\n```\n\nPointers are regular variables, so can be stored and copied as normal, with one small exception.  This is fine:\n\n```pawn\nMyFunction(playerid, Func:func\u003ci\u003e)\n{\n\t@.func(playerid);\n}\n```\n\nThis is not:\n\n```pawn\npublic TheFuture(playerid, Func:func\u003ci\u003e)\n{\n\t@.func(playerid);\n}\n\nMyFunction(playerid, Func:func\u003ci\u003e)\n{\n\tSetTimerEx(\"TheFuture\", 1000, 0, \"ii\", playerid, _:func);\n}\n```\n\nIn this case, the function `func` is used after the original function ends.  If you pass the pointer in a timer, save it in a global variable, or in any other way store it to be used at some unknown point in the future you must first \"claim\" it:\n\n```pawn\nMyFunction(playerid, Func:func\u003ci\u003e)\n{\n\tIndirect_Claim(func);\n\tSetTimerEx(\"TheFuture\", 1000, 0, \"ii\", playerid, _:func);\n}\n```\n\nAnd later \"release\" it:\n\n```pawn\npublic TheFuture(playerid, Func:func\u003ci\u003e)\n{\n\t@.func(playerid);\n\tIndirect_Release(func);\n}\n```\n\nThis is similar to the old y_inline `Callback_Get` and `Callback_Free` functions, but are not always needed, and are not restricted in when they can be called (those y_inline functions are now synonyms for the more generic indirection.inc functions).\n\nAs a library writer, the `\u0026` operator must be explicitly supported for your function-taking functions.  This is done with a simple define:\n\n```pawn\nMyFunction(playerid, Func:func\u003ci\u003e)\n{\n\t@.func(playerid);\n}\n#define MyFunction(%1,\u0026%2) MyFunction(%1, addressof(%2\u003ci\u003e))\n```\n\nThe second parameter of this define is `\u0026%2`, which means it will only match when users call your function with something like `MyFunction(playerid, \u0026Callback)`.  In that case the `\u0026` is replaced with a typed call to `addressof`.  This does mean you have to specify the type twice - once in the function prototype and once in the define.  Note, however, that the define can be more strict as the specifier in the function prototype must NOT use the specifier `t` - all tagged vars are just types of integer:\n\nGood:\n\n```pawn\nMyFunction(playerid, Func:func\u003ci\u003e)\n{\n\t@.func(playerid);\n}\n#define MyFunction(%1,\u0026%2) MyFunction(%1, addressof(%2\u003ctFile:\u003e))\n```\n\nBad:\n\n```pawn\nMyFunction(playerid, Func:func\u003ctFile:\u003e)\n{\n\t@.func(playerid);\n}\n#define MyFunction(%1,\u0026%2) MyFunction(%1, addressof(%2\u003ci\u003e))\n```\n\n### MySQL Example\n\n\n### Low-Level Providers\n\nThe function variable is one of three things.\n\n1) A function pointer, in which case it will point somewhere in the `COD` section.\n\n2) A string pointer, in which case it will point to a string in the `DAT` section, RELATIVE TO `DAT`.  This relativity is how indirection.inc can tell the difference.  Since `DAT` follows `COD`, any data pointers will be larger than any possible function pointer, and the difference can then just be subtracted.\n\n3) A function structure pointer.  This points to `DAT` in the same way as strings, but the target is of the form:\n\n```pawn\nenum E_INDIRECTION\n{\n\tE_INDIRECTION_ALWAYS_NULL, // So we can tell this is not a string.\n\tE_INDIRECTION_HANDER, // Called by `@` to handle this pointer.\n\tE_INDIRECTION_CLAIM, // Called by `Indirect_Claim` to handle any required allocations.\n\tE_INDIRECTION_RELEASE, // Called by `Indirect_Release` to handle any required frees.\n\t... // Any custom data required by your custom handlers.\n}\n```\n\n`ALWAYS_NULL` is (unsurprisingly) always NULL.  This is so that the data at the pointer is a zero-length string, which is treated as not a string at all.\n\nThe `CLAIM` and `RELEASE` functions both have the same signature:\n\n```pawn\nMyCustomClaim(func[E_INDIRECTION])\n{\n}\n\nMyCustomRelease(func[E_INDIRECTION])\n{\n}\n```\n\nYou can replace the array size there with any custom one to account for additional data after the standard four items.\n\nThe `HANDLER` function has the prototype:\n\n```pawn\nMyCustomHandler(...)\n{\n}\n```\n\nAll the parameters are the parameters the user gave to `@`, i.e. the parameters for the final function to be called.  For this reason there are two magic globals: `INDIRECTION_DATA` contains a pointer to the `E_INDIRECTION` data.  `INDIRECTION_TAG` contains the index of the function's tag, the string of the tag (which includes all the specifier characters) can be retrieved with `Indirect_Tag(id, dest[64])`.\n\n## Testing\n\nTo test, simply run the package:\n\n```bash\nsampctl package run\n```\n\nAnd connect to `localhost:7777` to test.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fy-less%2Findirection","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fy-less%2Findirection","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fy-less%2Findirection/lists"}