Jump to content

ByRef in built-in function


 Share

Recommended Posts

I am writing a new built-in function, RegExp which has an optional third argument to return an array that gets created in the function. How can I check that the third argument is actually a variable and not the result of a calculation or literal? :ph34r:

David Nuttall
Nuttall Computer Consulting

An Aquarius born during the Age of Aquarius

AutoIt allows me to re-invent the wheel so much faster.

I'm off to write a wizard, a wonderful wizard of odd...

Link to comment
Share on other sites

IsDeclared is looking for the variable with the given name. Inside the functions, we do not know the name, just the value. I do not feel like doing brute force searches through the global and local lists to find the variable that is passed. I guess I could just ask the user to put in the name of the variable, but I would like to able to work the actual variable directly.

David Nuttall
Nuttall Computer Consulting

An Aquarius born during the Age of Aquarius

AutoIt allows me to re-invent the wheel so much faster.

I'm off to write a wizard, a wonderful wizard of odd...

Link to comment
Share on other sites

Just use IsArray. I did that in my HotKeyLog UDF. Something like this:

Func My_Function(ByRef $variable)
  If NOT IsArray($variable) Then
    SetError(1)
    return 0
  EndIf
 ;rest of script here

[font="Optima"]"Standing in the rain, twisted and insane, we are holding onto nothing.Feeling every breath, holding no regrets, we're still looking out for something."[/font]Note: my projects are off-line until I can spend more time to make them compatable with syntax changes.

Link to comment
Share on other sites

I am looking at creating a built-in function inside AutoIt itself, not a UDF.

:( I am trying to figure out how to determine if the Variant in a vParams element refers to a variable or is the result of a calculation. I am hoping Larry or Jon, amoung others, might have some ideas. I will give another look tonight when I get home. Hmm, maybe the reference flag would be handy. :ph34r:

Edited by Nutster

David Nuttall
Nuttall Computer Consulting

An Aquarius born during the Age of Aquarius

AutoIt allows me to re-invent the wheel so much faster.

I'm off to write a wizard, a wonderful wizard of odd...

Link to comment
Share on other sites

  • Administrators

I looking at creating a built-in function inside AutoIt itself, not a UDF. 

:( I am trying to figure out how to determine if the Variant in a vParams element refers to a variable or is the result of a calculation.  I am hoping Larry or Jon, amoung others, might have some ideas.  I will give another look tonight when I get home.  Hmm, maybe the reference flag would be handy. :ph34r:

A variant in vParams is the result of a calculation, for example if you did:

MsgBox($myflag + 2 + 25, "", "text")

then in the MsgBox function vParams[0] would be the result of "$myflag + 2 + 25" and would have nothing to do with the actual variable "myflag". The only two ways I can think of around it are searching the global/local lists for a memory address that matches whatever you have (yuk). Or rewriting the expression parser so that it keeps track of if an expression consists of a result or a "pure" variable (also yuk).

Edited by Jon
Link to comment
Share on other sites

I think searching for the memory address would look ugly and it wouldn't be the most efficient method of finding the Variant, but I think that you could do it in VariableTable/VariableList and at least provide a nice interface to it. I think the actual code should be quite simple for it, though.

I think it could be useful for at least 3 reasons:

  • David needs a method for his RegEx thing, obviously.
  • I need some way to trace a variable all the way back and obtain it's name (This allows the ability to fake having pointers when used in conjunction with Eval, by the way).
  • In the future, there may be other functions that are written which need to modify a variable. Obviously, that's not currently possible.
Ugly as it may be, Jon, would you be willing to make such a change if somebody submitted the code?
Link to comment
Share on other sites

Having a closer look, searching for the address in the list doesn't seem any more ugly than the way variable names are looked up. You just loop through the linked list comparing addresses instead of AStrings, that's all.

Here is what I am thinking:

  • Add a new class which inherits _VarEntry as private, making it read-only.
  • Offer up an isReference() function returning bool so you can determine if a variable is a reference or not (This is all you need, right, David?).
  • Also szName() and pVariant() to get the other important information (Not that pVariant is needed..).
  • Overload findvar() in VariableList to return the new class and takes the address of a Variant and searches for it in the list.
  • Overload GetRef() in VariableTable to make use of the new findvar().
If nobody spots any glaring logic flaws in the next 30 - 60 minutes, I'll take a look at writing this.

Edit: Oops, I forgot. Simply getting a non-NULL object back from GetRef() would be enough for David to do what he needs to.

Edited by Valik
Link to comment
Share on other sites

Simply getting a non-NULL object back from GetRef() would be enough for David to do what he needs to.

That was my general idea. I just have to make sure that when a variant is set into the vParams list, it sets the reference, not the value.

David Nuttall
Nuttall Computer Consulting

An Aquarius born during the Age of Aquarius

AutoIt allows me to re-invent the wheel so much faster.

I'm off to write a wizard, a wonderful wizard of odd...

Link to comment
Share on other sites

I have it working now. It's... nothing like the above description, however, but it isn't hard or anything. It's quite nice.

I'm going to do some tests on it and make sure nothing is messed up. I only added stuff, so things should be good. I'm also going to write the following functions for AutoIt:

NameOf($var, "resolve") - Returns the name of the variable. If the variable is ByRef, then it's local name will be returned, unless resolve is non-zero, then the actual variable that gets modified is returned.

Assign("varname", "value") - Assigns the variable (By name) with the value.

These two functions along with Eval() allow pointers to be emulated. A trivial (And silly) example:

Dim $array[3]   ; An array of pseudo-pointers
Global $count = 0

Global $var1, $var2, $var3

DoSomething()

Func DoSomething()
    pus_back($var1)
    push_back($var2)
    push_back($var3)

    Assign($array[2], " pointer.")
    Assign($array[0], "A")
    Assign($array[1], " fake")

    Local $res = "", $i
    For $i = 0 To UBound($array) - 1
        $res = $res & Eval($array[$i])
    Next
    MsgBox4096, "", $res & @CRLF & $var1 & $var2 & $var3)
; Would print this:
; A fake pointer.
; A fake pointer.
EndFunc

Func push_back(ByRef $a)
    $array[$count] = NameOf($a, 1)
    $count = $count + 1
EndFunc

I need to do some bug-testing (I found one while writing this post), but I should be able to send it to Jon by tomorrow, his time.

Link to comment
Share on other sites

Okay, I thought I had it working, but apparently not. It's not likely to work as-is, either. When pushing an element onto a VectorVariant, it's copied by value. It was a dumb mistake on my part which fooled me into thinking it was working.

However, I'm still going to attempt to get this to work in some fashion or another as I still think it will be useful.

Edited by Valik
Link to comment
Share on other sites

Sometimes the simplest approach is the one that works. This has got to be one of the dumbest ideas I've ever had actually work. I added a static counter to the Variant class and I also added a private member which is that Variant's ID. On construction, the ID is set to the counter and the counter incremented. On copy, the ID is also copied. So now there is a unique identifier for every individual Variant. Copies are true copies in that they share the same ID. This allows two variants to be compared for equality using the ID, even if they do not point to the same place in memory.

I was able to use my already written code for look up VarEntry's with the only change in how the comparison is done (Now by ID, obviously). This means you should be able to use this code, David, to determine if a Variant actually is a variable or not (You can of course get a pointer to the Variant itself through its VarEntry if necessary, as well).

Thats all done and taken care of. I need to clean it up a bit, but that's about it. I'll do just a bit more testing to make sure no bugs exist, but I will hopefully get it sent to Jon in the next day or so.

By the way Jon, I noticed at least one unreferenced local variable which the compiler wasn't catching. Have you ever compiled with warning level 4 just to try to spot any unnecessary things like that and fix them up?

Link to comment
Share on other sites

  • Administrators

By the way Jon, I noticed at least one unreferenced local variable which the compiler wasn't catching.  Have you ever compiled with warning level 4 just to try to spot any unnecessary things like that and fix them up?

When I test the compile under Mingw I catch them then (gcc being the most picky compiler ever) :ph34r:
Link to comment
Share on other sites

I just found sort-of-a-bug. It seems that the following 2 methods for defining variables are completely different:

Global $var1, $var2, $var3

Global $g1
Global $g2
Global $g3

The problem is, in Parser_Keyword_DIM, vTemp is declared outside the for loop, so every new variable is created in vTemp, then copied somewhere else and thus, all variables declared on the same line end up with the same ID. Placing the declaration of vTemp inside the scope of the for loop causes a new local variable created every iteration, so a new ID is generated and the variables are unique. I've spent about an hour debugging my code trying to figure out why all my global variables had identical ID's, finally I accidentally stumbled upon this answer.

Link to comment
Share on other sites

But the variables being created are actually seperate in the VariableTable. Correct?

I decided to actually pass a Variant set to be a reference of a variable in vParams if the argument is a variable. I then modified the Variant class so that most operations on a reference actually refers to the original variable. That way the other built-ins do not have to be modified. I did not get it working properly. I did not add the ID.

What will happen to the search if you call the built-in function from a UDF, passing a variable that was passed by reference, from a UDF where it is a local variable. Will I be able to find the ID? :ph34r:

Edited by Nutster

David Nuttall
Nuttall Computer Consulting

An Aquarius born during the Age of Aquarius

AutoIt allows me to re-invent the wheel so much faster.

I'm off to write a wizard, a wonderful wizard of odd...

Link to comment
Share on other sites

But the variables being created are actually seperate in the VariableTable. Correct?

Correct. The change I made was to force vTemp to generate a new ID when creating a new variable when multiple variables were declared on the same line. There has been no functional change anywhere within AutoIt with either the addition of the ID to the Variant class or the tweak to the way variables are created (The change was also arguable as to whether the old way was good C++ style or not, since the variable was only used inside the loop, so it didn't need to be declared at the scope it was, but that's a debate best saved for another day). At least there has been no change that I can see.

What will happen to the search if you call the built-in function from a UDF, passing a variable that was passed by reference, from a UDF where it is a local variable. Will I be able to find the ID?

No, but you still might be able to get access to the Variant object itself. I return the VarEntry structure in my search function. That means that it contains a pointer to a Variant, where that Variant should contain a pointer to another Variant which could be the real object (More on this later, I'm a bit confused how the Variant class works in this area).

I can only search two scopes, because they are all that are currently accessable; local and global. This is because of how the local variables are pushed onto StackVarList which only offers a top() member. In the following situation, the only thing that can be returned is "B".

Example 1:

Func Test1()
    Local $a
    Return Test2($a)
EndFunc

Func Test2(ByRef $b)
    Return NameOf($b, 1); The 1 says to attempt to resolve if it's a global variable
; In this case, since $b resolves to something neither in local scope nor global scope
; The only thing that can be returned is "B"
EndFunc

MsgBox(4096, "", Test1())

This however, will properly resolve the name of the global variable.

Example 2:

Global $g_var

Func Test1(ByRef $a)
    Return Test2($a)
EndFunc

Func Test2(ByRef $b)
    Return NameOf($b, 1)
EndFunc

MsgBox(4096, "", Test1($g_var))

I thought about this for awhile. I debated on whether or not to add an at() or operator[] to StackVarList so that it's possible to view other scopes, too, and thus resolve a variable as in example 1. I decided that for an exposed function (i.e. NameOf()), that being able to find the name of a variable out-of-scope was not such a good idea.

If necessary, I can add some way to get to other scopes. The GetRefVarEntry() function I've added to VariableTable could easily accomdate that just by using the VARTABLE constants. All that means is that StackVarList needs some way of traversing to other scopes.

This is where I'm unclear on how the Variant class works. What is the pValue() return when a Variant references another Variant which also happens to reference a real Variant object. Does the pValue of the local Variant return the real Variant object, or does it return a pointer to the Variant object one scope below it (With that Variant, which is out of scope, returning the address of the real Variant?). For example, in order to take a variant and find the object it refers to, would this be one way to do it?

*myVariant;    // A pointer to a Variant which is a reference to another Variant
while (myVariant->pValue() != NULL)
    myVariant = myVariant->pValue();

// myVariant should now point to the real object that needs manipulation, regardless
// of what scope it is in.

Or, does myVariant->pValue() automatically give you the address of the real object, no matter if it's been passed through a dozen functions with ByRef parameters?

Also, while on the subject, I need to apologize for taking so long on getting these changes sent. I haven't had time to sit down and package the changes up into zip files and I also haven't been able to write my Assign() function yet.

Link to comment
Share on other sites

The pValue() returns the m_pValue value. This is assigned by the operator=(Variant *) overload. The pointer that is passed is the address of the actual value, not the variable and rarely a VAR_REFERENCE. It is possible to have a reference to a reference. If I can get the automatic dereferencing working, then this should be great. As far as looking for the name: If I have the address, which I will if I have a reference, then I can search through the global list and each local variable stack frame, deliberately, in order to find the name.

Devil's advocate time again. :ph34r: If you find the name of Func1::$x (returned as x) and try to use it in Func2, you will find the wrong variable.

The reason that the variable was created outside the For loop is to reduce the overhead of creating the object over and over again. This saves on processing and a little memory. Take a look at the constructor to see what gets done each time the Variant is created.

David Nuttall
Nuttall Computer Consulting

An Aquarius born during the Age of Aquarius

AutoIt allows me to re-invent the wheel so much faster.

I'm off to write a wizard, a wonderful wizard of odd...

Link to comment
Share on other sites

What gets done is a bunch of stuff that can be done using an intializer list... :ph34r:

If I have the address, which I will if I have a reference, then I can search through the global list and each local variable stack frame, deliberately, in order to find the name.

You only have a chance to get the address if the Variant passed via a VectorVariant is a reference to another Variant (Meaning it would have to be passed through a user function first). Otherwise, you have the address of a Variant but it is not the same address the corresponding Variant in the variable table has. This was the reason for the ID conecept. I say ignore the address unless you've used the search function I've added and have a valid VarEntry (Which obviously contains a pointer to a Variant).

Unless you know of something I didn't see, you can only serach the local and global variables. There is no way to traverse StackVarList or even get a StackVarListNode. Method's to do that need to be added.

However, what I do have in place can get you the address of a Variant either at the local or global scope. If that happens to be a reference, then either your dereferencing code or just simply following the pValue() all the way will yield the Variant that needs modified.

Link to comment
Share on other sites

I created that stack of local variables. :ph34r: I know what I can do with it. I am thinking of creating a function in the VarList to find the variable which has a given address. I would also create operator[] to look at the other stack frames. Only those functions that would deliberately look inside the non-current stack frames would actually find the out-of-scope variables.

I have to get some time to do these changes. Maybe tonight.

Edit: Fixed spelling and context

Edited by Nutster

David Nuttall
Nuttall Computer Consulting

An Aquarius born during the Age of Aquarius

AutoIt allows me to re-invent the wheel so much faster.

I'm off to write a wizard, a wonderful wizard of odd...

Link to comment
Share on other sites

David, if I make these changes to what I have, will this be all you need?

  • Add operator[] to StackVarList OR expose a prev() member in StackVarListNode (This will allow one-directional traversal of the stack in a top-down manner.)
  • Modify GetVarEntryRef() to do the following:
  • VARTABLE_ANY - Resolves all variables, regardless of scope (Any scope
  • VARTABLE_FORCELOCAL - Return local VarEntry only, even if it is a reference (Local scope only)
  • VARTABLE_FORCEGLOBAL - Return either the local VarEntry if a local variable, or the global VarEntry if it is global (Local or global scope, nothing in between)
This is the current declaration of GetRefVarEntry:

bool GetRefVarEntry(Variant *pvVar, VarEntry **pvVarEntry, int nReqScope = VARTABLE_ANY);   // Get pointer to VarEntry

The input Variant (pvVar) compares ID's with Variants in the variable tables to find the correct VarEntry. pvVarEntry is either a valid entry in one of the lists, or it's NULL. I can use nReqScope to do the stuff I mentioned in the list above. You should be able to just do something like this with my code:

vVar;    // Assume this is a Variant you have been passed
VarEntry *pvVar;
if (g_oVarTable.GetRefVarEntry(vVar, &pvVar, VARTABLE_ANY))
    pvVar->pvVariant = "Hi";
// GetRefVarEntry returns true if pvVar != NULL
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...