# 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
Technicalia | When possible the routine_id is resolved at compile-time, but (especially for constructed strings) can be left until run-time. There is also an inline assembler compiler directive of the same name, which must however be resolved at compile-time, and forward references are explicitly disallowed. See pilasm.e for implementation details. Copy
        #ilASM{
                ...
                mov edx,routine_id(fopen)           -- mov edx,imm32 (sets K_ridt)
                mov ecx,$_Ltot                      -- (symtab[fopen][S_Ltot])
                call :%opFrame
                pop dword[ebp]                      -- filepath
                pop dword[ebp-4]                    -- openmode
                mov [ebp+28],:openret
                jmp $_il                            -- (symtab[fopen][S_il])
              ::openret
                ...
              }
Note that there are no quotes around the routine name when invoked in this manner within #ilASM. As shown, $_Ltot and $_il can be used to obtain the equivalent fixed constant literal values from the symtab entry corresponding to the most recent routine_id directive. It is currently coded to issue an error if you forget to use all three, routine_id first. The statement "mov edx,routine_id(fopen)" is compiled to and executes as (say) "mov edx,118", and also sets the K_ridt (known routine_id target) flag on symtab[fopen], therefore forcing full parameter typechecking. The above example is taken from builtins\VM\pfileioN.e (wherein you can find 32 and 64-bit versions with proper reference counting) and implements a hll open() statement. Of course the latter is much easier, and this method should only be used when some other reason makes it necessary, in this specific case that would be a combination of getting these things into the optable (so that pfileioN.e does not have to be recompiled every time when interpreting) and the subsequent implications for thread-safety (in top-level code). In Phix, a return value of 0 for failure would probably make more sense, however for compatibility with Euphoria, on which 0 is a valid routine_id, it returns -1. Previous versions of Phix could also return -2 if there were multiple globals of that name, and -3 if the namespace did not exist, and some (Phix-only) routines check the result for <=0, or even <=T_const1(==26) as nothing in the symbol table <=26 can be a valid routine_id target. As of 0.8.1, routine_ids are first class, ie [also] callable directly, and as of 0.8.2 known routine names not followed by '(' are treated as valid routine_ids [routine_id() itself still exists and is still sometimes necessary, eg for constructed string names]. Eg:
                procedure show(string s) ?s end procedure
                constant r_show = routine_id("show")
                -- show("40")   (normal non-routine_id call [still valid!])
                call_proc(r_show,{"41"})                -- old style [""!]
                r_show("42")                            -- *** new (0.8.1) ***
                call_proc(show,{"43"})                  -- *** new (0.8.2) ***

                function f(integer i) return i end function
                constant r_f = routine_id("f")
                --?f(40)        (normal non-routine_id call [still valid!])
                ?call_func(r_f,{41})                    -- old style [""!]
                ?r_f(42)                                -- *** new (0.8.1) ***
                ?call_func(f,{43})                      -- *** new (0.8.2) ***
Any integer followed by '(' is treated as a call_func() or a call_proc(), depending on the context, and obviously as above the normal/old styles remain perfectly valid and fully supported. However I decided against supporting this sort of thing (at least not before someone comes up with a compelling reason to):
                --?r_f(r_f)(44)
                --r_f(r_show)(45)
                --sequence rids = {r_show}
                --rids[1](46)
Instead you have to code integer rid=r_f(r_f) then ?rid(44), etc. (Some wag might say that makes them second class, but it is not like you could ever do anything like that with "show" anyway.) Note that i(5) where i is not a routine_id will fail just as badly and in exactly the same way as call_func/proc(i,{5}) always did. Likewise the additional compile-time validation of argument types that occurs on a show() call is not performed/deferred until run-time on r_show(), in exactly the same way as it always was for explicit/old-style call_func/proc() calls. These changes (first class routine_ids) were made to complement the syntax enhancements for the new struct/class handling, specifically class methods. One almost accidental benefit of first class routine names is that `rtn_name` triggers an immediate compilation error should it be mis-spelt, whereas routine_id("rtn_name") might but is not guaranteed to and instead generate -1 at run-time, which may of course be a tad trickier to track down. There is inevitably1 perhaps three or four times as much overhead on a call_func/proc(), and equivalently on r_show()-style, as there is on a direct call, however that should be of no concern unless you are invoking it millions of times, in which case I would suggest you consider three versions of a tight inner loop rather than just the one inner loop that can call any of three different routines. 1 The arguments are boxed into a single sequence, additional validation occurs on rid and the length of said sequence, then each element of the sequence is individually extracted and put into place (same deal for r_show()-style), whereas of course a direct call has no rid to validate, the number and type of arguments is validated (and fairly often only) at compile-time, and each argument is put directly into place. Each routine that might be a target of routine_id/call_func/proc is also marked with K_ridt, which forces full typechecking on all parameters, whereas for direct-only routines the compiler uses type inferencing and omits any provably unnecessary typechecking. 2 A similar argument applies to hll routines with their variables in memory and typechecks vs. asm and direct/specific CPU registers.