Alternative to global variable

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
john_c
Posts: 493
Joined: 05 May 2017, 13:19

Alternative to global variable

10 Nov 2019, 11:54

I have a function that sends Ctrl-C and saves the copied contents to the PseudoClipboard variable.

Personally, I use to have an alternative way to open/launch files:

My AHK files are associated with Sublime Text. I.e. when I double-click or press the Enter key on some AHK file, it will not be launched - instead, it will be opened in the editor. Using my function, I can assign Ctrl-Enter to launch such files. Thus,

* Enter: open in Sublime Text
* Ctrl-Enter: launch it.

Very handy.

What I ask for is how to improve the function.

The problem is shown in the code itself: the excessive line that looks stupid and excessive:

Code: Select all

#SingleInstance, Force
SendMode, Input

CopyToPseudoClipboard(WaitingMethod := "ClipWait", WaitingDelayInMilliseconds := 1000) {
    OriginalClipboard := ClipboardAll
    Clipboard := ""
    Send, ^c

    ; ClipWait is more reliable, but it waits for at least 500
    ; milliseconds. To wait for a shorter period of time (e.g., 250
    ; milliseconds), you will need to use Sleep.
    If (WaitingMethod = "ClipWait") {
        WaitingDelayInSeconds := WaitingDelayInMilliseconds * 1000
        ClipWait, % WaitingDelayInSeconds
        If (ErrorLevel != 0)
            Return
    } Else {
        Sleep, % WaitingDelayInMilliseconds
    }

    PseudoClipboard := Clipboard
    Clipboard := OriginalClipboard

    Return PseudoClipboard
}

^Enter::
    CopyToPseudoClipboard("ClipWait", 1000)
    PseudoClipboard := CopyToPseudoClipboard("ClipWait", 1000) ; I don't like this line: it looks "duh" and excessive.
        ; I would like to remove it, but in such a case the script will not work.
    If (RegExMatch(PseudoClipboard, "adoc$"))
        Run, % """" . A_ProgramFiles . "\Google\Chrome\Application\chrome.exe" . """" . A_Space . """" . PseudoClipboard . """"
    Else If (RegExMatch(PseudoClipboard, "ahk$"))
        Run, % """" . A_AhkPath . """" . A_Space . """" . PseudoClipboard . """"
Return
I can workaround it by defining the PseudoClipboard variable as global:

Code: Select all

    } Else {
        Sleep, % WaitingDelayInMilliseconds
    }

    Global PseudoClipboard := Clipboard
    Clipboard := OriginalClipboard

    Return PseudoClipboard
But it doesn't look really good: as I know, the use of global variables is discouraged.

Probably there exists a better way, what do you think?
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Alternative to global variable

10 Nov 2019, 12:39

I don't understand your problem. Side note,

Code: Select all

; ClipWait is more reliable, but it waits for at least 500
    ; milliseconds.
This is not true, you can wait 250 ms by specifying 0.25.

Cheers.
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: Alternative to global variable

10 Nov 2019, 15:55

the line 'looks "duh" and excessive' because the function name is '"duh" and excessive'
what is this function meant to do? no, its certainly not CopyToPseudoClipboard
  • backup the current clipboard state
  • attempt(!) to update the clipboard
  • restore the old clipboard state
  • return the newly fetched clipboard contents(if any)
having it laid out like this should stimulate coming up with a better name. (and also reveals a flaw in ur implementation - returning from an expired ClipWait leaves ur clipboard in a clobbered state)
Spoiler
but as @helgef says, just use ClipWait, the other stuff is unnecessary
john_c
Posts: 493
Joined: 05 May 2017, 13:19

Re: Alternative to global variable

11 Nov 2019, 00:57

@Helgef

> This is not true, you can wait 250 ms by specifying 0.25.

Hi, Helgef. Probably you are right. (I don't know how to test it.) I said it because of this phrase in the docs:

> Specifying 0 is the same as specifying 0.5. (from ClipWait page)

In other words, 0.5 looks like a minimum possible value.
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Alternative to global variable

11 Nov 2019, 01:08

It just means that 0 is the same as specifying 0.5 not that 0.25 is the same as specifying 0.5.

You can test using a_tickcount, back-up your clipboard if needed,

Code: Select all

clipboard := ""
t1 := a_tickcount
clipwait 0.25
t2 := a_tickcount
msgbox % t2-t1

Cheers.
just me
Posts: 9482
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Alternative to global variable

11 Nov 2019, 04:50

Code: Select all

        WaitingDelayInSeconds := WaitingDelayInMilliseconds * 1000
should be

Code: Select all

        WaitingDelayInSeconds := WaitingDelayInMilliseconds / 1000
User avatar
boiler
Posts: 17043
Joined: 21 Dec 2014, 02:44

Re: Alternative to global variable

11 Nov 2019, 07:49

One thing that is excessive is calling the function twice in a row. You can/should remove the first line below, but you need to keep the second line where you're assigning it to the variable.

Code: Select all

    CopyToPseudoClipboard("ClipWait", 1000)
    PseudoClipboard := CopyToPseudoClipboard("ClipWait", 1000) ; I don't like this line: it looks "duh" and excessive.
Btw, maybe you think it looks excessive because you're using the same variable name and should already match the one inside the function (but you don't want to make it global). The reason global variables are discouraged is so that variable names inside functions don't inadvertently conflict with variables named the same outside of them. But that seems to be exactly what you want to accomplish (i.e., it's not inadvertent), so you might as well make it global if that's what you prefer in this case. There is no other reason they are discouraged.

However, I'm not sure why it's your preference. It's not "duh" or excessive to use the same variable name inside and out of functions, and they don't have to be named the same. Name one of them PseudoClip and you'll see it still works. Just make sure to rename all instance either inside or outside the function.
john_c
Posts: 493
Joined: 05 May 2017, 13:19

Re: Alternative to global variable

11 Nov 2019, 10:40

@boiler Hi. Thanks about all this information: it is really useful.

Below is the new version of the function, without Sleep, and basic examples how to use it.

Very simple things, of course, but it could be useful for another "eternal newbies" like me.

Edit: Ah... I have forgot to say. This is an excerpt from my functions.ahk before I read your post. Now I know that using Global could be a better option.

Code: Select all

CopyToPseudoClipboard(ClipWaitTimeout := 0) {
    OriginalClipboard := ClipboardAll
    Clipboard := ""
    Send, ^c

    ClipWait, % ClipWaitTimeout
    If (ErrorLevel = 0)
        PseudoClipboard := Clipboard

    Clipboard := OriginalClipboard

    Return PseudoClipboard
}

Code: Select all

F1:: ; example 1
    PseudoClipboard := CopyToPseudoClipboard(0.05) ; 50 milliseconds.
    MsgBox,,, % PseudoClipboard
Return

Code: Select all

F1:: ; example 2
    If (CopyToPseudoClipboard())
        MsgBox,,, % CopyToPseudoClipboard()
    Else
        MsgBox,,, % "Timeout"
Return

Code: Select all

F1:: ; example 3
    If (CopyToPseudoClipboard()) {
        PseudoClipboard := CopyToPseudoClipboard()
        MsgBox,,, % PseudoClipboard
    }
    Else
        MsgBox,,, % "Timeout"
Return

Code: Select all

F1:: ; example 4
    If (PseudoClipboard := CopyToPseudoClipboard())
        MsgBox,,, % PseudoClipboard
    Else
        MsgBox,,, % "Timeout"
Return
Last edited by john_c on 11 Nov 2019, 10:49, edited 3 times in total.
User avatar
boiler
Posts: 17043
Joined: 21 Dec 2014, 02:44

Re: Alternative to global variable

11 Nov 2019, 10:47

Very good. I like your example 4 better than 2 and 3 since you aren't calling the function twice.
guest3456
Posts: 3463
Joined: 09 Oct 2013, 10:31

Re: Alternative to global variable

11 Nov 2019, 13:26

for reference, this old post claimed to solve some copying problems by using vk scan codes as well as releasing modifiers:

https://autohotkey.com/board/topic/111817-robust-copy-and-paste-routine-function/

john_c
Posts: 493
Joined: 05 May 2017, 13:19

Re: Alternative to global variable

16 Nov 2019, 03:51

@swagfag

> what is this function meant to do? no, its certainly not CopyToPseudoClipboard

Why?
User avatar
boiler
Posts: 17043
Joined: 21 Dec 2014, 02:44

Re: Alternative to global variable

16 Nov 2019, 07:56

Yeah, I have to say that your function name is descriptive of what is happening and swagfag’s suggested names are not. The function copies to an alternate clipboard variable (PseudoClipbard) instead of replacing the contents of the clipboard

The names suggested by swagfag imply that it is simply copying the contents of the clipboard, which it is not. It does not care what is already in the clipboard, and it is not retrieving those. It is leaving the clipboard intact and instead copying a new selection to another variable.
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: Alternative to global variable

16 Nov 2019, 17:46

consider this function which adds two numbers together:

Code: Select all

???(a, b) {
	c := a + b
	return c
}
what would u call it? if answered anything other than AddToC, then what's wrong with CopyToPseudoClipboard should be more than sufficiently evident...
yes, on the surface, that is what the function does internally(adds to c, copies to PseudoClipboard), but the fact that it does it is not worth bringing attention to. what might be worth bringing attention to is the fact that it does it safely(without overwriting the current clip, hence "safe") or the fact that it might fail while attempting to do so("try").

u have a generic function under the hood and ure trying to assign a very specific name to it. why? what if u decide to not capture the value? for example as u have done here:

Code: Select all

If (CopyToPseudoClipboard())
where is CopyToIfCondition()? god forbid u had to store the contents in an array! better set to work on writing CopyToArrayElement(), huh? nonsense

id ignore @boiler's badvice to "might as well make it global". there are places where u could make a case for globals. machine code dllcallback needs to interact with multiple objects/classes/whatever?? okay, maybe use global.
this?? dont use global lol. and just to preempt the inevitable "performance!!"(which u dont seem to be overly concerned with anyway) - return ByRef

here are a bunch more no-other-reasons not to use globals:
  • concealed flow of data
  • if u have one, u probably have a lot more than one
  • makes debugging a bitch
  • u bear no hatred towards poor sods that might one day stumble upon a piece of code of urs
at the end of the day, its ur code, so make of that what u will
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: Alternative to global variable

16 Nov 2019, 19:14

the purpose of the function is crystal clear
swagfag wrote: what is this function meant to do? no, its certainly not CopyToPseudoClipboard
  • backup the current clipboard state
  • attempt(!) to update the clipboard
  • restore the old clipboard state
  • return the newly fetched clipboard contents(if any)
its decided - the function's new and final name is safeTryGetUpdatedClipboardContents :lol:

Spoiler
User avatar
boiler
Posts: 17043
Joined: 21 Dec 2014, 02:44

Re: Alternative to global variable

16 Nov 2019, 19:16

@swagfag I understand your reasoning. I'm just saying that the OP finds it to be a nice descriptive name for how he uses the function that he has written for his own use, so I think it's fine for him to name it such. You seem to be viewing my comment in support of OP as an attack on you (e.g., "badvice"). So be it. That's not how it was meant.

Edit: I deleted a prior post trying to explain my position further, but it's not worth the back and forth. I'm not interested in getting into arguments on the forums. You win. You always win. OK?
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: Alternative to global variable

16 Nov 2019, 19:33

wrong, i view the "might as well make it global" badvice as an attack on OP and anyone else that might for god knows what reason ever wander into this thread sometime in the future
there was no need to delete the post, clearly it had to be worth at least something. u had it all typed out and everything, after all.

i win? cool cool, whats the prize?
john_c
Posts: 493
Joined: 05 May 2017, 13:19

Re: Alternative to global variable

26 Nov 2019, 07:53

swagfag wrote:
16 Nov 2019, 17:46
and just to preempt the inevitable "performance!!"(which u dont seem to be overly concerned with anyway) - return ByRef
I never used ByRef before. It seems that ByRef is used when we pass the parameters, e.g.

Code: Select all

Func(ByRef Var) {
  Var .= "Bar"
}

Var := "Foo"
MsgBox, % Var ; Foo
Func(Var)
MsgBox, % Var ; FooBar
Is it what you mean? I ask it because your phrase could be understood like

Code: Select all

Func(Var) {
  Var .= "Bar"
  Return ByRef Var
}
Also, is it correct that performance improvement is only a side-effect of using ByRef?
nnnik wrote:
22 Apr 2018, 13:39
Denoting that a parameter is byref is showing that the variable is used to output data.
In some cases the variable is also used for input purposes.
In AutoHotkey passing a string byRef has the side effect of being faster than passing it byVal.
That is due to the fact that AutoHotkey copies every string when it is passed byVal even if they don't get changed by the function.
Advanced AutoHotkey users abuse this effect to further enhance the speed of their functions in specific cases.
However that usage should not affect the way this language is designed as it is an unintended effect that at most shows that we need some kind of solution for the speed problem.
That solution is not byRef.
... and therefore, from the point of view of logic, it is better to avoid ByRef here? As well as in the similar cases, for example here:
(from https://www.autohotkey.com/boards/viewtopic.php?p=300542#p300542)

Code: Select all

; to remove duplicates from a string
f(byref string, del := ",", casesense := false){ 
	local s := del
	loop parse, string, % del
		if !instr(s, del . a_loopfield . del, casesense)
			s .= a_loopfield . del
	return trim(s, del)
}
guest3456: why byref the string param, and why make the func assume global by declaring the first var as local?

swagfag: byref to save urself having to copy the string into the arg once

guest3456: byref initially seemed like quite an extreme optimization simply to save one copy, but i suppose the haystack string could be quite large if we are trying to remove dupes.
Last edited by john_c on 27 Nov 2019, 01:40, edited 4 times in total.
guest3456
Posts: 3463
Joined: 09 Oct 2013, 10:31

Re: Alternative to global variable

26 Nov 2019, 10:33

john_c wrote:
26 Nov 2019, 07:53
I never used ByRef before. It seems that ByRef is used when we pass the parameters, e.g.

Code: Select all

Func(ByRef Var) {
  Var .= "Bar"
}
Is it what you mean?
yes that's what he means. it allows you to 'return' out a value by simply modifying the variable that was sent in, instead of using a return statement. consider:

Code: Select all

name := "John"
msgbox, %name%
Func(name)
msgbox, %name%

Func(ByRef Var) {
  Var .= "Bar"
}
our 'name' variable was changed outside the func
john_c wrote:
26 Nov 2019, 07:53
Also, is it correct that performance improvement is only a side-effect of using ByRef?
not necessarily correct, it just depends on your goal. it requires you to know what you're doing. if you are using ByRef for performance to save copying a large string, then its up to you to be sure to safely write your function so that you don't modify the incoming ByRef parameter

john_c
Posts: 493
Joined: 05 May 2017, 13:19

Re: Alternative to global variable

27 Nov 2019, 01:48

guest3456 wrote:
26 Nov 2019, 10:33
not necessarily correct, it just depends on your goal. it requires you to know what you're doing. if you are using ByRef for performance to save copying a large string, then its up to you to be sure to safely write your function so that you don't modify the incoming ByRef parameter
Thanks. It seems that the function posted in my previous post is designed in such a safe way.

From a philosophical point of view, as an upholder of the "most straightforward" solutions, I am still wondering whether or not to use ByRef in such cases, though.

Isn't is possible to use ByRef inside function call instead?

Code: Select all

Func(Var) {
  Var .= "Bar"
}

Var := "Foo"
MsgBox,,, % Var ; Foo
Func(ByRef Var)
MsgBox,,, % Var ; Currently Foo, but according to my idea it should be FooBar
Last edited by john_c on 27 Nov 2019, 05:14, edited 1 time in total.
just me
Posts: 9482
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Alternative to global variable

27 Nov 2019, 05:02

john_c wrote:Isn't is possible to use ByRef inside function call instead?
Yes, it isn't!

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Aqualest and 316 guests