 |
AutoHotkey Community Let's help each other out
|
| View previous topic :: View next topic |
| Author |
Message |
ahklerner
Joined: 26 Jun 2006 Posts: 1381 Location: USA
|
Posted: Fri Aug 28, 2009 3:14 am Post subject: |
|
|
Wow thanks alot for the demonstration! very good. sorry for being impatient, i just got excited using this stuff _________________
ʞɔпɟ əɥʇ ʇɐɥʍ |
|
| Back to top |
|
 |
Lexikos
Joined: 17 Oct 2006 Posts: 7290 Location: Australia
|
Posted: Fri Aug 28, 2009 5:29 am Post subject: |
|
|
Here's another demonstration: how to insert a single line without breaking when the target is the single-line body of an if/else/loop/while. It also serves as a means to determine the active OnExit subroutine, with the limitation that the function must be called at least once before OnExit. It does this by replacing OnExit <arg> with sOnExit:=<arg> .. OnExit %sOnExit% (where <arg> may be a variable, text, expression or omitted; and braces are inserted if necessary).
| Code: | ErrorLevel=0 ; Without this, insert_line_before fails for the line below.
OnExit Undetected ; With it, insert succeeds but the inserted line never runs.
MsgBox % GetCurrentOnExitRoutine()
OnExit Foo1
MsgBox % GetCurrentOnExitRoutine()
if 0 = 0
OnExit Foo2
MsgBox % GetCurrentOnExitRoutine()
if 0 = 0
{
OnExit
}
MsgBox % GetCurrentOnExitRoutine()
ListLines
Pause
Foo1:
Foo2:
Undetected:
ExitApp
GetCurrentOnExitRoutine()
{
static ACT_ONEXIT, sOnExit
if !ACT_ONEXIT
{ ; Initialize. Must be done before the first time OnExit is called.
ACT_ASSIGNEXPR := 2
ACT_ONEXIT := 97
LowLevel_init()
cg := code_gen()
line_ptr := __getFirstLine()
while line_ptr
{
ActionType := NumGet(line_ptr+0, 0, "UChar")
if (ActionType = ACT_ONEXIT)
{
; Create an impossible assignment line with three args.
assn := code_line(cg, ACT_ASSIGNEXPR)
code_arg(cg, 2, __getVar(sOnExit)) ; Output var/target of assignment.
code_arg(cg, 0) ; Empty text arg, may be overwritten.
code_arg(cg, 1, __getVar(sOnExit)) ; Extra %sOnExit% arg for OnExit.
code_line_end(cg)
code_gen_reset(cg, false) ; Reset cg and "detach" line.
l_argc := NumGet(line_ptr+1, 0, "UChar")
l_args := NumGet(line_ptr+4)
a_args := NumGet(assn+4)
if l_argc ; OnExit had an arg.
{
NumPut(a_args+32, line_ptr+4) ; line_ptr->Arg = a_args+32 (third arg)
; Overwrite second arg of assn with first arg of OnExit.
DllCall("RtlMoveMemory", "uint", a_args+16, "uint", l_args, "uint", 16)
}
; else leave OnExit with no args and assn with an empty second arg.
NumPut(2, assn+1, 0, "UChar") ; assn->Argc = 2
if !insert_line_before(assn, line_ptr)
MsgBox % "Error inserting at line " NumGet(line_ptr+8) ": " ErrorLevel
}
line_ptr := NumGet(line_ptr+20, 0, "UInt")
}
code_gen_delete(cg) ; Code was already detached via code_gen_reset.
}
return sOnExit
}
; v1.0.48 +
; Insert a single line before target, automatically wrapping both lines in braces if required.
insert_line_before(to_insert, target)
{
if !(prev := NumGet(target+16))
return 0, ErrorLevel:="Bad target->PrevLine: insertion will have no effect"
if NumGet(target+0,0,"uchar") = 9
return 0, ErrorLevel:="Bad target->ActionType: ACT_ELSE"
action := NumGet(to_insert+0,0,"uchar") ; if/else/loop/while/{/} (not in that order):
if (action >= 10 && action <= 33) || action = 104 || action = 105 || action = 108 || action = 109 || action = 8
return 0, ErrorLevel:="Bad to_insert->ActionType"
; If the previous line is of a type which requires braces (if/else/loop/while):
action := NumGet(prev+0,0,"uchar")
if (action >= 10 && action <= 33) || action = 104 || action = 105 || action = 8
{
; Since target->PrevLine is an if/else/loop/while and target is not {,
; target must be the single-line body of the line preceding it.
bb := __lineAlloc()
be := __lineAlloc()
NumPut(108, bb+0, 0, "uchar") ; bb->ActionType = ACT_BLOCK_BEGIN
NumPut(109, be+0, 0, "uchar") ; be->ActionType = ACT_BLOCK_END
NumPut(NumGet(prev+24), bb+24) ; bb->RelatedLine = prev->RelatedLine
NumPut(prev, bb+28) ; bb->ParentLine = prev
NumPut(bb, target+28) ; target->ParentLine = bb
NumPut(bb, be+28) ; be->ParentLine = bb
NumPut(bb, prev+20) ; prev->NextLine = bb
NumPut(target, bb+20) ; bb->NextLine = target
NumPut(NumGet(target+20), be+20) ; be->NextLine = target->NextLine
NumPut(be, target+20) ; target->NextLine = be
prev := bb ; Now let below insert to_insert between bb and target.
}
NumPut(to_insert, prev+20) ; prev->NextLine = to_insert
NumPut(target, to_insert+20) ; to_insert->NextLine = target
NumPut(NumGet(target+28), to_insert+28) ; to_insert->ParentLine = target->ParentLine
return 1, ErrorLevel:=0
} |
This would be a lot easier to write, more maintainable, secure, reliable, readable, etc. etc. if we had struct support. I hope to include struct support and definitions of these structures in a future version of AutoHotkey_L.  |
|
| Back to top |
|
 |
Lexikos
Joined: 17 Oct 2006 Posts: 7290 Location: Australia
|
Posted: Thu Feb 18, 2010 12:25 pm Post subject: |
|
|
Since AutoHotkey_L introduced objects and this required additional "symbol" types for the expression evaluator, some of the existing symbol types have different values to mainstream AutoHotkey. Call, which demonstrates dynamically calling a function, must therefore be updated to work in recent revisions of AutoHotkey_L:
| Code: | , SYM_INVALID:=56, SYM_VAR:=3, SYM_FUNC:=55, SYM_OPERAND:=4, VAR_ALIAS:=0
, SYM_INVALID:=62, SYM_VAR:=3, SYM_FUNC:=59, SYM_OPERAND:=4, VAR_ALIAS:=0
|
Note that Call usually has no advantages over dynamic function calls, but there is one: the actual number of parameters passed to the function is calculated based on the parameter values. For instance, if the code needs to pass between zero and four parameters, it is tempting to do this:
| Code: | fn := "MyFunc", %fn%(prm1, prm2, prm3, prm4)
| However, all parameters are passed even if some are not meaningful. So for instance, if MyFunc defines parameter 4 as optional, it will be overwritten even if prm4 has no meaningful value. Call can work around this issue:
| Code: | prm1 := "one", prm2 := "two", prm3 := "three"
prm4 := "__deFault__"
Call("MyFunc", prm1, prm2, prm3, prm4)
| In this case, only the first three parameters are passed. If MyFunc required four parameters, an empty string would be passed in place of "__deFault__". This behaviour can also be important for DllCall, where passing empty parameters is different from not passing parameters.
| Code: | prm1 := "GetCommandLine"
prm2 := "str"
prm3 := "__deFault__" ; prm3 and on will not be passed.
MsgBox % Call("DllCall", prm1, prm2, prm3, prm4) |
This post inspired by recent discussion with majkinetor. |
|
| Back to top |
|
 |
Rapte_Of_Suzaku
Joined: 29 Feb 2008 Posts: 900 Location: Vault 7
|
Posted: Fri Jun 04, 2010 4:44 pm Post subject: |
|
|
| weiterbilder wrote: | | What can you do with it in general. |
There is majkinetor's UTest
And I just finished updating my debug output library. It relies heavily on LowLevel.ahk, so I thought it would be relevant to mention here.
| Lexikos wrote: | | Quote: | | the typical AutoHotkey user |
...has nothing to do with this thread. |
While normal users will still have nothing to do with this thread, good simple to use debug output functions can be useful to anyone! And the proper use of assert statements is a great skill for any programmer (professional or otherwise) to learn.
Anyways, thank you Lexikos for the great stuff! |
|
| Back to top |
|
 |
Rapte_Of_Suzaku
Joined: 29 Feb 2008 Posts: 900 Location: Vault 7
|
Posted: Fri Jun 11, 2010 7:53 pm Post subject: |
|
|
I was playing around with LowLevel more this morning and came up with an interesting set of functions. The core of this example script is the merge function. Given 2 function names, it will take the local variables of one function and insert them into the other function (overwriting values where necessary). I also added a way to filter which variables are considered for the merge.
This script requires AutoHotkey_H (current version is 17). I tried to make
a version for AutoHotkey_L, but kept running into difficulties...
| Code: | ;==============================================================================
/*
Output will be three message boxes:
1) 0x100
2) 1-two-ASDF
3) alpha-two-hello world
*/
#SingleInstance FORCE
LowLevel_init()
test()
return
;==============================================================================
test()
{
; a list of name:value pairs, separated by |s
list =
(Join LTrim
FO_MOVE:0x1|FO_COPY:0x2|FO_DELETE:0x3|FO_RENAME:0x4|FOF_MULTIDESTFILES:0x1
|FOF_MULTIDESTFILES:0x1|FOF_SILENT:0x4|FOF_RENAMEONCOLLISION:0x8
|FOF_NOCONFIRMATION:0x10|FOF_ALLOWUNDO:0x40|FOF_FILESONLY:0x80
|FOF_SIMPLEPROGRESS:0x100|FOF_NOCONFIRMMKDIR:0x200|FOF_NOERRORUI:0x400
|FOF_NOCOPYSECURITYATTRIBS:0x800|FOF_NORECURSION:0x1000
|FOF_NO_CONNECTED_ELEMENTS:0x2000|FOF_WANTNUKEWARNING:0x4000|
)
unpack_list(list,A_ThisFunc)
MsgBox %FOF_SIMPLEPROGRESS%
; establish a box object for moving variables
box := Object()
box.a := ""
box.b := ""
box.c := ""
; use simple local variables
a := 1
b := "two"
c := "ASDF"
; pack them up
repack(box,A_ThisFunc)
MsgBox % box.a . "-" . box.b . "-" . box.c
; send the box to another function
test_helper(box)
; check out what that other function did with the box
MsgBox % box.a . "-" . box.b . "-" . box.c
}
test_helper(ByRef box)
{
; unpack the box
unpack(box, A_ThisFunc)
; play around with the contents of the box
a := "alpha"
c := "hello world"
; repack the box when finished
repack(box, A_ThisFunc)
}
;==============================================================================
unpack_list(_unpack_list,_unpack_func)
{
Loop Parse, _unpack_list, |
{
StringSplit _unpack_part, A_LoopField, :
%_unpack_part1% := _unpack_part2
}
merge(_unpack_func,A_ThisFunc,"","_unpack_")
}
;==============================================================================
; Maybe a return would be better than using ByRef here?
repack(ByRef box, caller)
{
merge(A_ThisFunc,caller,"enum,key,value,box,caller")
enum := box._NewEnum()
While enum[key,value]
box[key] := %key%
}
;==============================================================================
unpack(box, caller)
{
enum := box._NewEnum()
While enum[key,value]
%key% := value
merge(caller, A_ThisFunc, "enum,key,value,box,caller")
}
;==============================================================================
; f1 gets overwritten where overlap occurs.
; have not yet attempted to handle the global scope
merge(f1,f2, not_in_list="", not_contains_list="", match_regex=".*")
{
f1_ptr := FindFunc(f1)
f2_ptr := FindFunc(f2)
f1_var_array_ptr := NumGet(f1_ptr+20, 0, "UInt")
f1_var_array_len := NumGet(f1_ptr+28, 0, "Int")
var_array := object()
Loop % f1_var_array_len
{
ptr_ptr := f1_var_array_ptr + (A_Index - 1) * 4
ptr := NumGet(ptr_ptr+0)
name := __str(NumGet(ptr+24, 0, "UInt"))
var_array[name] := ptr
}
f2_var_array_ptr := NumGet(f2_ptr+20, 0, "UInt")
f2_var_array_len := NumGet(f2_ptr+28, 0, "Int")
Loop % f2_var_array_len
{
ptr_ptr := f2_var_array_ptr + (A_Index - 1) * 4
ptr := NumGet(ptr_ptr+0)
name := __str(NumGet(ptr+24, 0, "UInt"))
if name in %not_in_list%
continue
if name contains %not_contains_list%
continue
if !RegExMatch(name,match_regex)
continue
Alias(source_var, ptr+0)
target_ptr := var_array[name]
if !target_ptr
{
target_ptr := code_varW(name)
Alias(target_var, target_ptr+0)
target_var := source_var
__addVar(target_ptr, f1_ptr)
}
else
{
Alias(target_var, target_ptr+0)
target_var := source_var
}
Alias(target_var, 0)
Alias(source_var, 0)
}
}
;==============================================================================
; needed to use lstrcpyW for this to work (modified function from Lexikos' Code.ahk)
code_varW(name)
{
static empty
var_ptr := DllCall("GlobalAlloc","uint",0x40,"uint",28 + 2*StrLen(name) + 1)
if !var_ptr
return 0, ErrorLevel:="MEM"
NumPut(DllCall("lstrcpyW","uint",var_ptr+28,"str",name), var_ptr+24)
, NumPut(&empty, var_ptr+8)
, NumPut(2, var_ptr+20, 0, "uchar")
, NumPut(1, var_ptr+23, 0, "uchar")
return var_ptr
}
;============================================================================== |
[Moderator's note: Broke long line up to allow word wrap.] |
|
| Back to top |
|
 |
Lexikos
Joined: 17 Oct 2006 Posts: 7290 Location: Australia
|
Posted: Wed Jun 23, 2010 1:44 pm Post subject: |
|
|
I've updated __getTokenValue() in the "v1.0.48" download to support objects in variables. However, I haven't updated LowLevel.chm - SymbolType is only accurate for the official AutoHotkey build, and some struct/union members used in AutoHotkey_L aren't documented there.
| tinku99 wrote: | Variadic functions could be simulated by calling a variadic bif (such as "dllcall") with one bogus argument plus the arguments meant for the user variadic.
Source: DebugBIF thread
|
There's no need to use DebugBIF for this. Instead, flag the function as "built-in" and implement it via a callback.
[Note: All code snippets in this post require AutoHotkey_L or a build based on it.]
| Code: | LowLevel_init()
if !(vf := __findFunc("variadic"))
|| !(cb := RegisterCallback("variadic_callback", "C F", 3))
{
MsgBox FAIL
ExitApp
}
NumPut(true, vf + 49, 0, "char") ; vf.IsBuiltIn := true
NumPut(cb, vf + 4) ; vf.BIF := cb
; Example: Simulate Object(<initializers>).
foo := Object()
bar := variadic(foo, 1, "one", 2, "two")
e := bar._NewEnum()
while e[k, v]
s .= k " = " v "`n"
MsgBox % s "`nfoo " (foo==bar ? "==" : "!=") " bar"
variadic(a=0,b=0,c=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0
,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0)
{
; The body of this function is never executed.
}
variadic_impl(params)
{
; Example implementation of variadic function:
; Accepts an object and a list of key/value pairs to set.
e := params._NewEnum()
if ! e[_, obj]
return
while e[_, k] && e[_, v]
obj[k] := v
return obj
}
variadic_callback(aResultToken, aParam, aParamCount)
{
; Build array of parameters.
params := Object()
Loop % aParamCount {
params[A_Index] := __getTokenValue(NumGet(aParam+0))
aParam += A_PtrSize
}
; Call implementation function (or do stuff here).
result := variadic_impl(params)
; Store result in result token.
__setResultToken(aResultToken, result)
} |
In this example, the function variadic() must be defined with the maximum number of parameters that will be passed to it. Load-time validation will prevent scripts from passing more than this. Note that the default values are only used if a parameter in the middle of the list is omitted:
| Code: | variadic(1, , 3) ; (1, 0, 3)
|
| Quote: | | However, DebugBIF_DefaultHandler falls down on objects. | It relies on __getTokenResult to get a usable value from aParam, which is an array of pointers to ExprTokenType. Try the updated version of LowLevel.
| Quote: | | Also, how do you get the result of the DebugBIF_DefaultHandler back to the caller of the intentionally faulty bif ? | aResultToken points to an ExprTokenType structure which should hold the return value. Its use is rather involved, so I've written a function for it (used in the example above).
| Code: | __setResultToken(rt, r)
{
/* ; This section may cause loss of format, e.g. for r=0xBEEF.
if r is integer
{
; rt.symbol is SYM_INTEGER by default.
NumPut(r, rt+0, 0, "int64") ; rt.value_int64 := r
}
else if r is float
{
NumPut(2, rt+0, 8, "int") ; rt.symbol := SYM_FLOAT
NumPut(r, rt+0, 0, "double") ; rt.value_double := r
}
else
*/
if IsObject(r)
{
DllCall(NumGet(NumGet(&r)+4), "uint", &r) ; r.AddRef()
NumPut(&r, rt+0) ; rt.object := r
NumPut(5, rt+0, 8, "int") ; rt.symbol := SYM_OBJECT
}
else
{
; See TokenSetResult() in script2.cpp for how this works.
if StrLen(r) > 255 {
p := __malloc((StrLen(r) + 1) * (A_IsUnicode ? 2:1))
NumPut(p, rt+12) ; rt.circuit_token := p
NumPut(StrLen(r), rt+4) ; rt.buf := StrLen(r)
} else
p := NumGet(rt+4) ; p := rt.buf
StrPut(r, p)
NumPut(p, rt+0) ; rt.marker := p
NumPut(0, rt+0, 8, "int") ; rt.symbol := SYM_STRING
}
}
|
|
|
| Back to top |
|
 |
tinku99
Joined: 03 Aug 2007 Posts: 513 Location: Houston, TX
|
Posted: Wed Jun 23, 2010 8:04 pm Post subject: __getTokenValue() |
|
|
| Lexikos wrote: |
There's no need to use DebugBIF for this. Instead, flag the function as "built-in" and implement it via a callback... function variadic() must be defined with the maximum number of parameters that will be passed to [for] Load-time validation | That is neat. What do you think of optionally specifying number of parameters for the load time validation. Maybe with something like | Code: | func(10000){
; a registercallback with BIF prototype will handle upto 10000 parameters
} | ...To avoid having to type a 100 dummy parameters.
| Lexikos wrote: | I've updated __getTokenValue() in the "v1.0.48" download
| Could you please post the updated c++ code for __getTokenValue() ? |
|
| Back to top |
|
 |
Lexikos
Joined: 17 Oct 2006 Posts: 7290 Location: Australia
|
Posted: Wed Jun 23, 2010 10:11 pm Post subject: |
|
|
| tinku99 wrote: | | That is neat. What do you think of optionally specifying number of parameters for the load time validation? | I will not be implementing any feature which is only useful in combination with LowLevel. In this particular case at least, I think it would be a waste of time. Variadic functions are already relatively high on my priority list.
| Code: | BIF(__getTokenValue)
{
ExprTokenType *token = aParam[0];
if (token->symbol != SYM_INTEGER)
return;
token = (ExprTokenType*) token->value_int64;
if (token->symbol == SYM_VAR)
{
Var &var = *token->var;
VarAttribType cache_attrib = var.mAttrib & (VAR_ATTRIB_HAS_VALID_INT64 | VAR_ATTRIB_HAS_VALID_DOUBLE);
if (cache_attrib)
{
aResultToken.symbol = (SymbolType) (cache_attrib >> 4);
aResultToken.value_int64 = var.mContentsInt64;
}
//if (var.mAttrib & VAR_ATTRIB_HAS_VALID_INT64)
//{
// aResultToken.symbol = SYM_INTEGER;
// aResultToken.value_int64 = var.mContentsInt64;
//}
//else if (var.mAttrib & VAR_ATTRIB_HAS_VALID_DOUBLE)
//{
// aResultToken.symbol = SYM_FLOAT;
// aResultToken.value_double = var.mContentsDouble;
//}
else if (var.mAttrib & VAR_ATTRIB_OBJECT)
{
aResultToken.symbol = SYM_OBJECT;
aResultToken.object = var.mObject;
}
else
{
aResultToken.symbol = SYM_OPERAND;
aResultToken.marker = var.mContents;
}
}
else
{
aResultToken.symbol = token->symbol;
aResultToken.value_int64 = token->value_int64;
}
if (aResultToken.symbol == SYM_OBJECT)
aResultToken.object->AddRef();
} |
Btw, it's probably impossible to pass more than 254 parameters in a function call. Expressions are limited to 512 tokens, which includes commas, parentheses and function references in addition to the actual parameters. Supporting even 100 parameters would be overkill. Who would actually use that many parameters in a single function call? |
|
| Back to top |
|
 |
tinku99
Joined: 03 Aug 2007 Posts: 513 Location: Houston, TX
|
Posted: Thu Jun 24, 2010 1:06 am Post subject: loadtime validation |
|
|
| Quote: | | That is neat. What do you think of optionally specifying number of parameters for the load time validation? | Never mind.
Its easy enough to bypass load time validation of parameters using dynamic function call.
But its nice to know that default parameters can still be specified.
Its also nice to be able to forward the params to a builtin function as well.
Example: simulate objInsert() | Code: | LowLevel_init()
if !(vf := __findFunc("variadic"))
|| !(cb := RegisterCallback("variadic_callback", "C F", 3))
|| !(ppObjInsert := __findFunc("objInsert") + 4)
{
MsgBox FAIL
ExitApp
}
NumPut(true, vf + 49, 0, "char") ; vf.IsBuiltIn := true
NumPut(cb, vf + 4) ; vf.BIF := cb
pObjInsert := numget(ppObjInsert+0)
; Example: Simulate objInsert
foo := Object()
foo.insert := "variadic"
foo.insert(foo, 1, "one", 2, "two")
e := foo._NewEnum()
while e[k, v]
s .= k " = " v "`n"
MsgBox % s
return
variadic(){
; should only be called indirectly, to avoid load time validation
}
variadic_callback(aResultToken, aParam, aParamCount)
{
global pObjInsert
dllcall(pObjInsert + 0, "ptr", aResultToken, "ptr", aParam, "ptr", aParamCount, "cdecl")
} |
Is there anything special about resultTokens? I was not able to use __SetResultToken to modify the param tokens before passing them onto objInsert... | Code: | variadic_callback(aResultToken, aParam, aParamCount)
{
global pObjInsert
offset := 0
Loop % aParamCount {
__SetResultToken(NumGet(aParam + offset), "instrumented" . A_Index) ; fails
offset += A_PtrSize
}
dllcall(pObjInsert + 0, "ptr", aResultToken, "ptr", aParam, "ptr", aParamCount, "cdecl")
} |
|
|
| Back to top |
|
 |
Lexikos
Joined: 17 Oct 2006 Posts: 7290 Location: Australia
|
Posted: Thu Jun 24, 2010 8:14 am Post subject: |
|
|
__setResultToken is intended only for use with aResultToken, which has special use:
- ExpandExpression uses 'buf' to pass the function a pointer to a buffer which may be used to return a value (<= 255 characters). __setResultToken relies on this, which is probably why your example fails.
- If the function needs to allocate memory for its result, it can pass it back to ExpandExpression via 'circuit_token'. This memory is freed automatically when expression evaluation is complete.
- If circuit_token is used for the function's result, 'buf' must be set to the string's length.
None of this applies to parameters, as they are only used to pass values to the function. You only need to set 'symbol' and the appropriate value field ('marker' for strings). |
|
| Back to top |
|
 |
tinku99
Joined: 03 Aug 2007 Posts: 513 Location: Houston, TX
|
Posted: Fri Jun 25, 2010 7:54 am Post subject: |
|
|
| Lexikos wrote: | | __setResultToken is intended only for use with aResultToken | I got variadic custom object initializers to work in richObject.
Needed the following:
__getResultObject, __setParamToken.ahk
Hopefully, I am not leaking variables, objects, etc... |
|
| Back to top |
|
 |
Lexikos
Joined: 17 Oct 2006 Posts: 7290 Location: Australia
|
Posted: Fri Jun 25, 2010 8:45 am Post subject: |
|
|
| tinku99 wrote: | | Hopefully, I am not leaking variables, objects, etc... | You are allocating memory with __malloc which will not be freed. Also, there is no reason for the StrLen(r) > 255 check, nor the minimum 256 character allocation. It is used in __setResultToken so that a malloc can be avoided when the caller-provided buffer will suffice.
For string parameters, I see two options:
- Explicitly allocate memory for parameters before you call the function and explicitly free it after the function returns.
- Store the parameters' string values in variables and let AutoHotkey do all the work.
Why did you write __getResultObject rather than simply using __getTokenValue? Both functions will cause the object's reference count to be incremented but neither function will release the reference which is contained within aResultToken. You'll need to do that before overwriting it, as shown in the documentation.
Note that your __setParamToken does not support floating-point numbers... If you change "is number" to "is integer", they will at least be supported as strings. |
|
| Back to top |
|
 |
tinku99
Joined: 03 Aug 2007 Posts: 513 Location: Houston, TX
|
Posted: Fri Jun 25, 2010 5:03 pm Post subject: __setparamtoken |
|
|
@Lexikos, Thanks for the advice with memory management.
I have incorporated your suggestions in __setParamToken(). | Lexikos wrote: | For string parameters, I see two options:
- Explicitly allocate memory for parameters before you call the function and explicitly free it after the function returns.
- Store the parameters' string values in variables and let AutoHotkey do all the work.
|
I chose the second route, sort of. Using a heap of variables.
| Code: | __setParamToken(rt, r)
{
static heap, pointer
if !heap
{
heap := object()
heap._SetCapacity(256)
pointer := 1
}
if IsObject(r)
{
if (NumGet(rt+0, 8, "int") == 5) ; rt.symbol := SYM_OBJECT
{
oldObjectPointer := NumGet(rt+0) ; rt.object := r
DllCall(NumGet(NumGet(oldObjectPointer+0)+8), "uint", oldObjectPointer)
; decrement reference count
}
DllCall(NumGet(NumGet(&r)+4), "uint", &r) ; r.AddRef()
NumPut(&r, rt+0) ; rt.object := r
NumPut(5, rt+0, 8, "int") ; rt.symbol := SYM_OBJECT
}
else if r is integer
{
NumPut(1, rt+0, 8, "int") ; rt.symbol := SYM_INTEGER
NumPut(r, rt+0, 0, "Int64") ; rt.symbol := SYM_INTEGER
}
Else if r is Float
{
NumPut(2, rt+0, 8, "int") ; rt.symbol := SYM_FLOAT
NumPut(r, rt+0, 0, "double") ; rt.value_double := r
}
else {
; See TokenSetResult() in script2.cpp for how this works.
; p := __malloc((StrLen(r) + 1) * (A_IsUnicode ? 2:1))
heap[pointer] := r
p := heap._GetAddress(pointer)
StrPut(r, p)
NumPut(p, rt+0) ; rt.marker := p
NumPut(0, rt+0, 8, "int") ; rt.symbol := SYM_STRING
pointer := Mod(pointer + 1, 256)
}
} |
| Lexikos wrote: | | Why did you write __getResultObject rather than simply using __getTokenValue? | Because it didn't work. |
|
| Back to top |
|
 |
Lexikos
Joined: 17 Oct 2006 Posts: 7290 Location: Australia
|
Posted: Sat Jun 26, 2010 4:27 am Post subject: Re: __setparamtoken |
|
|
| tinku99 wrote: | Because it didn't work.
...
; map := __getTokenValue(aResultToken) ; doesn' work | You were using it incorrectly. Please read the provided documentation for this function (in LowLevel.chm).
FYI, I don't like to see my code butchered the way you've done with __setResultToken -> __getResultObject. The majority of the text was /* obsolete/irrelevant code */, which would take only a few seconds of your time to delete. The comments were also irrelevant. Now you can just delete the function as it shouldn't be needed. |
|
| Back to top |
|
 |
tinku99
Joined: 03 Aug 2007 Posts: 513 Location: Houston, TX
|
Posted: Sat Jun 26, 2010 3:26 pm Post subject: Re: __setparamtoken |
|
|
| Lexikos wrote: | | tinku99 wrote: |
; map := __getTokenValue(aResultToken) ; doesn' work | You were using it incorrectly. | The doc is clear. Wasn't thinking... | Code: | | map := __getTokenValue(aResultToken + 0) ; works |
| Lexikos wrote: | | FYI, I don't like to see my code butchered the way you've done with __setResultToken -> __getResultObject... | Sorry about that.
__getResultObject was sort of a work in progress, which I had a feeling was going down an unnecessary path anyway as you pointed out.
I have removed it.
I do need to write more readable code and present it in a nice way like Chris, you, and many others here do. Thanks for the wakeup call. |
|
| Back to top |
|
 |
|
|
You can post new topics in this forum You can reply to topics in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|