Page 1 of 1
Confusion around macros and variable expansion
Posted: Wed Nov 27, 2024 9:16 am
by Quin
Hi all,
I'm trying to make a helper function to automatically bind menu items to keyboard shortcuts on all platforms, but it's not working. It seems to be because I have a weird confusion about variable expansion in macros. Here's my code:
Code: Select all
Macro PBShortcut(_Key) : #PB_Shortcut_#_Key : EndMacro
; Get a bitmask of #PB_Shortcut constants from a key name in the format of, e.g. Ctrl+F
Procedure GetShortcutConstant(Shortcut$)
Protected Res, I, CurKey$
For I = 1 To CountString(Shortcut$, "+") + 1
CurKey$ = LCase(StringField(Shortcut$, I, "+"))
Select CurKey$
Case "ctrl" : Res | #PB_Shortcut_Control
Case "alt" : Res | #PB_Shortcut_Alt
Case "shift" : Res | #PB_Shortcut_Shift
Default : Res | PBShortcut(CurKey$)
EndSelect
Next
ProcedureReturn Res
EndProcedure
Procedure MenuShortcutItem(ItemID, Text$)
Protected Shortcut$
MenuItem(ItemID, Text$)
If Not FindString(Text$, Chr(9)) : ProcedureReturn : EndIf
Shortcut$ = Mid(Text$, FindString(Text$, Chr(9)))
AddKeyboardShortcut(#WndMain, GetShortcutConstant(Shortcut$), ItemID)
EndProcedure
When I run this, it tells me that #PB_Shortcut_CurKey$ is not a constant, which makes utterly no sense to me. If I'm passing it my variable, shouldn't it expand the value of the variable and substitute that? There are people much smarter and more experienced than me here so hoping someone can talk some sense into me :lol
Thanks in advance!
Re: Confusion around macros and variable expansion
Posted: Wed Nov 27, 2024 10:03 am
by Andesdaf
no, the macro replaces the parameter by the given expression in the call without expanding it. See the second example in
https://www.purebasic.com/documentation ... acros.html
You could try to get the shortcut value by using the runtime library.
Re: Confusion around macros and variable expansion
Posted: Wed Nov 27, 2024 11:19 am
by spikey
This is the compile-time/run-time divide in action. The macro pre-processor operates in compile-time and has concluded by the start of run-time. It has no access to run-time context information. It operates literally on the text value of the expression supplied at the text level. (Think more autocorrect in a word processor than procedure call in a program). The expanded text literals are then supplied to the compiler to turn into opcodes along with the other source code in the file.
So in the example you give the macro will expand to the literal text '#PB_Shortcut_CurKey$' and the compiler doesn't recognise this when it arrives.
In this particular case you could probably map the ASCII code of the text to the #PB_Shortcut constant. See
ASC(). EDIT: Actually a cursory look at the underlying constants suggests that the ASCII codes are used for these constants.
Re: Confusion around macros and variable expansion
Posted: Wed Nov 27, 2024 12:26 pm
by Psychophanta
Please make a working code.
In your code #PB_Shortcut_CurKey$ is not defined.
Besides of it, you can not perform bit OR management with alfanumeric strings...
Re: Confusion around macros and variable expansion
Posted: Wed Nov 27, 2024 2:38 pm
by Quin
spikey wrote: Wed Nov 27, 2024 11:19 am
This is the compile-time/run-time divide in action. The macro pre-processor operates in compile-time and has concluded by the start of run-time. It has no access to run-time context information. It operates literally on the text value of the expression supplied at the text level. (Think more autocorrect in a word processor than procedure call in a program). The expanded text literals are then supplied to the compiler to turn into opcodes along with the other source code in the file.
So in the example you give the macro will expand to the literal text '#PB_Shortcut_CurKey$' and the compiler doesn't recognise this when it arrives.
In this particular case you could probably map the ASCII code of the text to the #PB_Shortcut constant. See
ASC(). EDIT: Actually a cursory look at the underlying constants suggests that the ASCII codes are used for these constants.
Thanks so much, this was incredibly helpful! Reworked my code to this:
Code: Select all
; Get a bitmask of #PB_Shortcut constants from a key name in the format of, e.g. Ctrl+F
Procedure GetShortcutConstant(Shortcut$)
Protected Res, I, CurKey$
For I = 1 To CountString(Shortcut$, "+") + 1
CurKey$ = StringField(Shortcut$, I, "+")
Select CurKey$
Case "Ctrl" : Res | #PB_Shortcut_Control
Case "Alt" : Res | #PB_Shortcut_Alt
Case "Shift" : Res | #PB_Shortcut_Shift
Default : Res | Asc(CurKey$)
EndSelect
Next
ProcedureReturn Res
EndProcedure
Procedure MenuShortcutItem(ItemID, Text$)
Protected Shortcut$
MenuItem(ItemID, Text$)
If Not FindString(Text$, Chr(9)) : ProcedureReturn : EndIf
Shortcut$ = Mid(Text$, FindString(Text$, Chr(9)) + 1)
AddKeyboardShortcut(#WndMain, GetShortcutConstant(Shortcut$), ItemID)
EndProcedure
And all works as expected!

Thanks everyone for your answers.
Re: Confusion around macros and variable expansion
Posted: Wed Nov 27, 2024 2:43 pm
by Quin
Never mind, it sadly doesn't work for all keys. F3 for example juss gives me the code for F...

Will I just need to put special cases for all of those?
Re: Confusion around macros and variable expansion
Posted: Wed Nov 27, 2024 3:56 pm
by spikey
Quin wrote: Wed Nov 27, 2024 2:43 pm
Will I just need to put special cases for all of those?
You will need a special case yes, you'll need to detect the case where an F prefixed section is longer than 1 character. However, the #PB_Shortcut_Fn constants are sequential too, starting at 112. You could take the
Val() of the suffix digits and add this to the base value of 111.
Re: Confusion around macros and variable expansion
Posted: Wed Nov 27, 2024 4:09 pm
by Quin
Managed to come up with this, and all seems to work as expected!
Code: Select all
Procedure GetShortcutConstant(Shortcut$)
Protected Res, I, CurKey$
For I = 1 To CountString(Shortcut$, "+") + 1
CurKey$ = StringField(Shortcut$, I, "+")
If CurKey$ = "Ctrl" : Res | #PB_Shortcut_Control
ElseIf CurKey$ = "Alt" : Res | #PB_Shortcut_Alt
ElseIf CurKey$ = "Shift" : Res | #PB_Shortcut_Shift
ElseIf Left(CurKey$, 1) = "F" And Len(CurKey$) > 1 : Res | 111 + Val(Right(CurKey$, Len(CurKey$) - 1))
Else : Res | Asc(CurKey$) : EndIf
Next
ProcedureReturn Res
EndProcedure
Procedure MenuShortcutItem(ItemID, Text$)
Protected Shortcut$
MenuItem(ItemID, Text$)
If Not FindString(Text$, Chr(9)) : ProcedureReturn : EndIf
Shortcut$ = Mid(Text$, FindString(Text$, Chr(9)) + 1)
AddKeyboardShortcut(#WndMain, GetShortcutConstant(Shortcut$), ItemID)
EndProcedure
Thanks everyone!
Re: Confusion around macros and variable expansion
Posted: Wed Nov 27, 2024 4:30 pm
by spikey
You're welcome!