routine_id
Definition: | integer rid = routine_id(string rtn_name) |
Description: |
Semi-deprecated, since 0.8.2 you can just code my_rtn (without quotes and not followed by '(') instead
of routine_id("my_rtn") , although both remain perfectly valid, plus the get_routine_info() as documented below
is still independently useful, and finally most of the remarks below about hll_stubs continue to apply.
While this is unlikely to go away any time soon, new code should just use the bare routine name, which the compiler treats as (aka automatically converts into) a constant integer. You may encounter some cases where that does not quite work, such as piecemeal string construction and maybe even the odd forward reference, but even then the recommendation is to refactor those nasty bits away until it does work, purely on the grounds of it’ll doubtless be nicer code. One subtle point to note is that the compiler has been very finely tuned to deeply understand scopes, and in particular a few nuances related to locality of reference at a specific point in a complex nested include hierarchy (oooh errr, listen to im) that are lost and are frankly impossible to replicate (and obviously never were) in a routine that looks up a name in one dirty great big flat symbol table. If you didn’t get any of that, the key take-home is that it all goes away if you use bare routine names. Returns an integer id number, in fact an index to the symbol table, for a user-defined procedure or function. The name of the procedure or function is given by the string sequence rtn_name. -1 is returned if the named routine cannot be found (see technicalia). |
pwa/p2js: |
Supported, although as of 0.8.2 a bare action_cb is equivalent to routine_id("action_cb") ,
and is recommended. Note they are small integers on desktop/Phix (almost meaningless indexes into the symbol table),
whereas in JavaScript they are the actual function, so avoid things like printing them or tying to use them as some
sort of index or lookup, since that simply won’t work. Also be advised that JavaScript is fundamentally a
function-scoped language and there are no namespaces or different scopes for different include files.
Since core components are coded without inline assembly in p2js.js there is no need for hll_stubs. On a related note, Icallback() is in fact a dummy function in pGUI.js which does not attempt to replicate the safety/sanity checks of cbfunc() - of course that is still valuable when developing on the desktop, and of course if you forego testing things first on the desktop and switch to a direct edit/browser mode of development instead, that’s on you. |
Comments: |
The id number can be passed to call_proc() or call_func(), to indirectly call the routine named by rtn_name. It can also be called directly, as of 0.8.1 routine_ids are first class, and as of 0.8.2 a bare reference to an existing routine name not followed by '(' is treated as if routine_id had been used, see example 1 below. The routine named by rtn_name must be visible, i.e. callable, at the place where routine_id() is used to get the id number. Indirect calls to the routine can appear earlier in the program than the definition of the routine, but the id number can only be obtained in code that comes after the definition of the routine - see example 2 below. Once obtained, a valid routine id can be used at any place in the program to call a routine indirectly via call_proc()/call_func(), or now directly. Some typical uses of routine_id() are: 1. Creating a subroutine that takes another routine as a parameter. (See Example 2 below) 2. Setting up an Object-Oriented system. 3. Defining a call_back(). (See Calling C Functions) Note that C routines, callable by Phix, also have (non-first-class) routine ids, but from define_c_proc() and define_c_func(), not this. |
Auxillary functions: |
{integer maxp,
integer minp,
string sig[,
object name]} = get_routine_info(integer rid,
bool bName=true) Returns the maximum and minimum number of parameters, along with a string signature in roottype format ("(P|F|T)&{I|N|S|P|O}"), and optionally the actual name of the routine, except when rid refers to a class method, when -1 is returned (on purpose, since their proper names are kept in builtins\structs.e, and are passed in as text anyway, so you don’t want/need conflicting entries messing up the symtab. You still(/only) get max/min/sig for class methods.) For instance get_routine_info(apply) yields {3,2,"FOIO","apply"}. Note that invoking this routine with bName=true coerces the run-time into populating the symbol table with actual names, which may make startup a fraction of a second slower when interpreting, especially on larger programs - hopefully not quite enough for anyone to actually notice, but if you don’t need the name there is not a lot of point asking for it. Under pwa/p2js minp is always 0 and sig is always "" (which should hardly be surprising in a typeless language), but maxp and name are fine, and unlike desktop/Phix there is no associated penalty of populating the symbol table names, so a bName of false is ignored and name is always returned. The minp and sig are coloured illegal here to remind you they will not work in a browser. Also, pwa/p2js does not (as yet) support classes, so none of the remarks relating to that apply, and name is always a string. On desktop/Phix only, if rid is <=0 (and >-9 as next) the call stack is consulted, with 0 meaning whichever routine actually invoked get_routine_info(), and -1 meaning one down from that, and so on. Note that p.exw deliberately invokes get_routine_info(-9) to permanently disable it, that is within p.exe itself, since populating the symtab with actual names mid-compilation kinda spanners everything. In other words this routine is not available to the compiler itself, at least for now. In reality, p.exe is already pre-populated, no issue there, however this and "p p" might or might not trigger armageddon, so I’ve played it safe, perhaps for no good reason at all. Intended for internal use in routines such as apply() to make a more informed choice about precisely how to invoke a routine, but of course it is also available for use at the application level. It is now used (at runtime) in structs.e, to check the validity of any getters and setters it finds before invoking them, and twice in pGUI.e, in the Icallback[i]() routine, and to check the K_ANY callback does not have a time-bomb signature, since I’ve managed to put quite a few of those in myself while translating C code. It has also found a fair bit of use in xpGUI.e (and xpGUI.js) to deal with (fully) optional parameters on handlers, for instance DATA, DRID, KEY, CLICK, and MOUSEMOVE. |
Diagnostics: |
sequence names = get_possible_constant_names(object v, fdx=0,
bool bGlobal=true)
Returns a list of [global] S_Const symtab entry names with [S_value]==v. fdx: 0 for any, or a file number, usually obtained from include_file[s](), or a sequence of them. bGlobal: true(default) return globals only, false gives non-globals only. This routine is intended for diagnostic purposes only, and only works when interpreted, not when compiled, and not when transpiled under pwa/p2js. As per example 3 below, should more than one name be returned it is entirely the programmer’s responsibility to further deal with/filter that. Even in an otherwise empty file, get_possible_constant_names(1) [ie fdx=0 aka all] returns a list of ~27 builtin constant names. This routine may work when compiled in some integer-only cases, but that would be a free bonus rather than an excuse to want, expect, or demand anything better. ![]() Note that Phix "pools" and inlines constants whenever it can. In the first instance, you are never (I hope) going to code say `whats_the_name_of(WM_PAINT)` but instead be asking `whats_the_name_of(15)` at run-time. Secondly, were you asking `whats_the_name_of(msg)` then you cannot argue that "msg" would not be a valid but completely useless answer, apart from the fact that the closest thing possible to that nonsense would always get "v", ie the name of whatever parameter you had just copied msg into, if you get my drift. In other words this routine simply cannot perform any of the common-sense inferences a human being might, and certainly cannot hunt down some mythical variable assignment chain, looking for a constant on the rhs, so instead it can only answer the question "well, what could that 15 have (once) been?". Plus of course a WM_PAINT/15 of interest might very well have originated from somewhere deep inside kernel32.dll anyway, and there simply be no suitable matches for that anywhere at all in the current program. |
hll_stubs: |
The file builtins/hll_stubs.e (an auto-include, except as noted below) contains hll verions of many builtins implemented in
assembly, for use when you need a routine_id. Prior to 0.8.2 you would have had to roll your own such mini-shim(s).
While you should not really care much, the overwhelming majority of routines documented in this help file are implemented as standard hll routines (eg abs), but many what you might call "true builtins" are written in assembly for performance or practical reasons, eg you might struggle to use a hll routine as any part of the mechanism for calling a hll routine, and between them length() and append() are so ubiquitous that just making those two alone always use a hll wrapper could easily make almost everything (including the compiler itself) run three or four times slower2. It would probably not be helpful (except as noted below) for either you to study or me to document which is which, rather simply you should just assume anything (genuinely useful) should be available, and let me know of anything that ain’t. Equally you should not get too miffed should a new release "upgrade" an existing hll routine to asm in the name of performance, or (as has happened to find(), match() and repeat() already) "downgraded" in the name of simplicity and sanity or enhanced functionality, although that sort of thing should probably only ever matter when grafting on the "hll_" manually, as described below. One thing you should not assume is that everything goes through such wrappers, in fact the vast majority won’t. The compiler automatically substitutes routine_id("length") with routine_id("hll_length") and likewise a bare 'length' with no following '(' with the same implied routine_id. Note the equivalent existing sq_xxx routines may prove significantly faster, and perhaps easier. For instance sq_atom(s) and apply(s,atom) both yield exactly the same results, the former however being noticeably quicker. For completeness, I should also note that apply(true,[hll_]atom,{s}) also yields the same results, however apply(true,sq_atom,{s}) yields something subtly different. Also, if you are dynamically constructing the names passed to routine_id, you may need to graft on the "hll_" part yourself. The compiler only does so for inline literals and not for anything like "len"&"gth" or apply({"length","integer"},routine_id). At the last count, there are some 25 such hll_xxx routines, and despite what I said above I suppose you do need to know which is which to figure out which is going to need the hll_prefix, but you can gather that from builtins/hll_stubs.e easily enough, or if you prefer they are all listed alphabetically in the index of phix.chm (and all linking directly to this very section), just type "hll" in the index and they’ll all appear. Also I would hope obviously this file is only an auto-include in the cases where the compiler has spotted it is needed: if you are having to graft on the "hll_" yourself, then you will probably need to "include hll_stubs.e" explicitly as well. |
Example 1: |
procedure foo() puts(1, "Hello World\n") end procedure constant r_foo = routine_id("foo") call_proc(r_foo, {}) -- same as calling foo() r_foo() -- "", in 0.8.1+ call_proc(foo, {}) -- "", in 0.8.2+ |
Example 2: |
function apply_to_all(sequence s, integer f) -- apply the specified function to all elements of a sequence for i=1 to length(s) do s[i] = call_func(f, {s[i]}) end for return s end function function add1(atom x) return x + 1 end function ?apply_to_all({1, 2, 3}, routine_id("add1")) -- displays {2,3,4} ?apply({1, 2, 3}, add1) -- (equivalent in 0.8.2+ - add1 valid & apply builtin) Note that add1 could be a private function in a completely different file; there is no need for it to be in scope when (actually) called. |
Example 3: |
constant two = 2, duo = 2, WM_DESTROY = 2 sequence res = get_possible_constant_names(2,include_file(),false) ?res -- {"two","duo","WM_DESTROY"} function begins_wm(string s) return begins("WM_",s) end function sequence wm_only = filter(res,begins_wm) if length(wm_only) then res = wm_only end if ?res -- {"WM_DESTROY"} |
Implementation: |
See builtins\VM\prtnidN.e (an autoinclude) for details of the actual implementation.
Note however that the compiler tries very hard to avoid calling/including that code, if things can be resolved at compile-time. The get_routine_info() routine is implemented separately, in builtins\get_routine_info.e (another autoinclude) |
See Also: |
call_proc,
call_func,
apply,
call_back,
define_c_func,
define_c_proc,
Calling C Functions |
