Jump to content

processing #include file's contents before the #include statement is actually executed


Recommended Posts

Here is a case where AutoIt has been generating some unexpected results for me. AutoIt appears to be, at least partially, processing #include statements and the #include file's contents before the #include statement is actually executed. Any help in working around this would be appreciated.

To demonstrate the behavior, I have the following files:

a.au3

; Sample Include A
#include-once
Global $sample = "A"

Func SampleMe()
    MsgBox(0, @ScriptName, "Include file a.au3.  $sample = " & $sample)
EndFunc

b.au3

; Sample Include B
#include-once
Global $sample = "B"

Func SampleMe()
    MsgBox(0, @ScriptName, "Include file b.au3.  $sample = " & $sample)
EndFunc

c.au3 and d.au3 are similarly done.

test1.au3

#include <a.au3>
SampleMe()

Can't get much simpler. This script will run fine.

test2.au3

$condition = "c" ;this would normally be based off of machine parameters or
  ;; command line parameters rather than explicitly defined
Switch $condition
    Case "a"
        #include <a.au3>
    Case "b"
        #include <b.au3>
    Case "c"
        #include <c.au3>
    Case Else
        #include <d.au3>
EndSwitch
SampleMe()

In this case, even though the #include statement for a.au3 is never executed, I get one of two error messages when I run this script:

1 - Line 5 (file "X:\xxx\xxx\a.au3") -- Error" "Switch" statement is missing "EndSwitch" or "Case" statement.

2 - Line 5 (file "X:\xxx\xxx\a.au3") -- Error: "Func" statement has no matching "EndFunc".

The first error seems to come up most often, but occasionally, I get the second one instead, running the same test2.au3 script.

Why would I think I could do this?

The help file and its example imply that the #include statements are processed inline, so it would make sense that using conditional statements to "skip" including should get around the warning about Funcs with the same name. The fact that doing this does not generate the "Duplicate function" error mentioned in the help file would support the concept. It would make sense that this would make compiled versions larger, since all of the #includes would have to be "included" in the compiled file in a "physical" sense, but AutoIt shouldn't be processing the #include files' contents until the #include statements are actually encountered, based solely on the help file.

Why am I trying to use #include statements this way?

I have two circumstances where I would want to do this.

1 - I have some scripts, where almost every other command has to be enclosed in case statements or nested If statements to accommodate various conditions that are established at or near the beginning of the script. Altering which #include is used based on these conditions would allow me to write cleaner, easier to debug code.

2 - When writing machine deployment scripts that include potentially destructive functions (renaming machine, adding to or changing the machine's domain, forced reboots, etc...) I could call a simulation #include that only simulates these functions while debugging on my own system, then switch to the live #include to run on a test system or for actual deployment. I currently set a $debug variable at the start of my script using command line parameters to control which debugging lines of code get executed, and which are skipped, but it turns into a mass of conditionals.

Since Autoit does not seem to handle #include files the way expected, based on the help file, are there any suggestions to achieve the same result, without having to muddle my scripts with hudreds of conditionals?

Link to comment
Share on other sites

  • Moderators

willichan,

I think you are fundamentally misunderstanding the #include directive. Like all AutoIt/AutoIt3Wrapper directives (to my knowledge) it will be actioned before any other interpreting takes place - that is the code associated with the #include file is inserted in one piece at the point where the directive is placed in the code.

If I compile your code (overridding the errors that AutoIt3Wrapper and Obfuscator give me) I get the following (after a run through Tidy):

$condition = "c"
Switch $condition
    Case "a"
        Global $sample = "A"
;### Tidy Error -> case Not closed before "Func" statement.
;### Tidy Error -> "func" cannot be inside any IF/Do/While/For/Case/Func statement.
        Func SampleMe()
            MsgBox(0, @ScriptName, "Include file a.au3.  $sample = " & $sample)
        EndFunc   ;==>SampleMe
        Case "b"
            Global $sample = "B"
;### Tidy Error -> case Not closed before "Func" statement.
;### Tidy Error -> "func" cannot be inside any IF/Do/While/For/Case/Func statement.
            Func SampleMe()
                MsgBox(0, @ScriptName, "Include file b.au3.  $sample = " & $sample)
            EndFunc   ;==>SampleMe
            Case "c"
                Global $sample = "c"
;### Tidy Error -> case Not closed before "Func" statement.
;### Tidy Error -> "func" cannot be inside any IF/Do/While/For/Case/Func statement.
                Func SampleMe()
                    MsgBox(0, @ScriptName, "Include file c.au3.  $sample = " & $sample)
                EndFunc   ;==>SampleMe
                Case Else
                    Global $sample = "d"
;### Tidy Error -> case Not closed before "Func" statement.
;### Tidy Error -> "func" cannot be inside any IF/Do/While/For/Case/Func statement.
                    Func SampleMe()
                        MsgBox(0, @ScriptName, "Include file d.au3.  $sample = " & $sample)
                    EndFunc   ;==>SampleMe
;### Tidy Error -> "endswitch" is closing previous "case" on line 17
            EndSwitch
            SampleMe()
;### Tidy Error -> case is never closed in your script.
;### Tidy Error -> case is never closed in your script.
;### Tidy Error -> case is never closed in your script.

So you can see that all 4 includes are included regardless of your Switch statement and that is what is giving you the errors. :P

This has always been the case, and I imagine it always will. Conditional compilation has aleays been a "no-no".

I hope this helps clear the fog a bit. :mellow:

M23

Public_Domain.png.2d871819fcb9957cf44f4514551a2935.png Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind

Open spoiler to see my UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Link to comment
Share on other sites

Lets imagine for a second that AutoIt simply substitutes the include line for the new code.

$condition = "c" ;this would normally be based off of machine parameters or
  ;; command line parameters rather than explicitly defined
Switch $condition
    Case "a"
        ; Sample Include A
        #include-once
        Global $sample = "A"

        Func SampleMe()
            MsgBox(0, @ScriptName, "Include file a.au3.  $sample = " & $sample)
        EndFunc
    Case "b"
        #include <b.au3>
    Case "c"
        #include <c.au3>
    Case Else
        #include <d.au3>
EndSwitch
SampleMe()

And so you have errors because you are using a function inside a switch statement.

Edit: He may be older than me, but Melba is a minute faster all the time :mellow:

Edited by Mat
Link to comment
Share on other sites

I see your points. I was coming round to the conclusion that that was how AutoIt was handling it. It would make sense, especially since nested includes would also need to be accounted for.

So, are there any ideas for producing the desired mechanic ... by other means? Since the script is interpreted at runtime, perhaps something similar to function overrides in OOP might be possible?

Link to comment
Share on other sites

Ok. I think I found a solution. Not best, maybe, but workable.

I modify the #include files, to put variable declarations and initializations into a func, then rename each func, pre-pended with an identifier for that version of the include.

I then create the functions with their normal names within the main script, which then use the Call function to virtualize the included funcs, pre-pending the $condition variable to match the included funcs. Thus, changing the $condition will function mechanically the same as a function override (less the ByRef ability, but that can be worked around)

EX:

a.au3

; Sample Include A
#include-once
Func a_Initialize()
    Global $sample = "A"
EndFunc

Func a_SampleMe()
    MsgBox(0, @ScriptName, "Include file a.au3.  $sample = " & $sample)
EndFunc

test2.au3

#include <a.au3>
#include <b.au3>
#include <c.au3>
#include <d.au3>
$condition = "c"
Initialize()
SampleMe()

;virtualize included functions here
Func Initialize()
    Call($condition & "_Initialize")
EndFunc

Func SampleMe()
    Call($condition & "_SampleMe")
EndFunc

This appears to work in my testing. Did I miss anything?

Link to comment
Share on other sites

  • Moderators

willichan,

While your workaround seems to do the trick, may I ask why you are going to all this trouble? There must be an easier way to do what you are trying to do - could you explain it in simple terms? :mellow:

M23

Public_Domain.png.2d871819fcb9957cf44f4514551a2935.png Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind

Open spoiler to see my UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Link to comment
Share on other sites

While your workaround seems to do the trick, may I ask why you are going to all this trouble? There must be an easier way to do what you are trying to do - could you explain it in simple terms? :mellow:

Certainly,

Some scripts need to perform differently based on various criteria. I realize that support for the older OS versions have been dropped, but for simplicity lets just say OS Version. (we have myriads of conditions on our network, but I won;t go into that here)

Lets say that I have 10 or more functions that are referenced, that need to perform basically the same tasks, but do them in different ways depending on the OS. That means that each function will have to contain the appropriate conditional statements (Ifs, switch, etc.) to control what it does under each OS. With only two or three OSes, or two or three functions, this may not be so bad, but when you get more OSes and more functions, the code becomes much more difficult to debug.

With each OS's commands located within its own set of functions, you remove the conditionals determining OS from the function's code, and can focus on the function for that OS. You also get more optimized code, since you do not have to make the CPU perform near so many conditional statements; it only needs to do it once.

As an added bonus, if an error is generated, you do not need to wade through code intended for another OS to debug, because AutoIt will report the error as being in a.au3 or b.au3, and you can go strait to the problem.

It is basically the same line of reasoning behind function overrides in many programming languages.

Link to comment
Share on other sites

You can indeed write separate functions, each one dedicated to processing functionality XYZ in some condition/context e.g. _Install_X86(...) and _Install_X64(...) and invoke them either from inside test branchs (If, Switch, whatever) or more programmatically like you did: Call("_Install_" & @CPUarch, $p1, $p2...). Group functions for XYZ in XYZ.au3 or group them by OS and you're not far from home.

Depending on the common code between, say, x86 and x64 versions, you may find that approach easier to deal with. If the behavior is really too different, then it's probably better to have separate functions. So you have, for a given task, either one messy function with a lot of tests inside, or a lot of functions to maintain in parallel.

If you have to constantly test for condition K, then make separate functions else test inline. The choice is not different of when to split a function into pieces or refactor three functions into one.

In practice, you will probably find that the real world often calls for a mixed approach: a set of functions for each OS and a set of common subfunctions to factor out common code among OS versions.

This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.
Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe here
RegExp tutorial: enough to get started
PCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta.

SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.
SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.
An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.
SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)
A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!
SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt)

Link to comment
Share on other sites

In practice, you will probably find that the real world often calls for a mixed approach: a set of functions for each OS and a set of common subfunctions to factor out common code among OS versions.

Yes. You are absolutely correct. This approach is definitely not one to be used generally, and a mixed approach is often best. This was for a couple of specific scripts that were getting out of hand with conditional statements (we sometimes get some rather convoluted requirements passed down from the parent company). Every time I needed to implement a change, I had to do far too much analysis to find the right place to edit, even with comments-a-plenty ... and I'm the one that wrote it. If I left, I would hate to be the guy who had to figure out what I was doing in those scripts. One drawback to this method is the high probability of redundant code, but the trade-off is in readability for someone else who may have to try to figure it all out, were I not available to explain it.

The main drive behind it all, I guess, is really to make more complex scripts comprehensible to someone else, that may have it dropped in their lap without any warning one day. I have had to try to figure someone else's mindset on many occasions before, and would never wish figuring mine out on anyone. :mellow:

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...