Where do you put your INCLUDEs?
jabramson
Posts: 106
Curious to know the consensus for placement of INCLUDEs? I've put them at the beginning of my program file, in the Define_Start and in the mainline.
My includes are mostly a series of functions rolled together, but when I put them in the define_Start (which seems to make sense to load the file only once), strange things sometimes happen like DEFINE_START looping.
I also use INCLUDEs to separate portions of my program into smaller files for things like Monitor control, Camera control, etc.
My includes are mostly a series of functions rolled together, but when I put them in the define_Start (which seems to make sense to load the file only once), strange things sometimes happen like DEFINE_START looping.
I also use INCLUDEs to separate portions of my program into smaller files for things like Monitor control, Camera control, etc.
0
Comments
The code in the include is compiled into the the program.
Where you put the includes in your program depends on what is in them.
If all that is in there is functions that you call from your program, I would put them at the top of the Program Source "Master.axs".
But all that is just my preference. It depends on what you are doing with them. I generally only use includes to define variables and constants needed for modules; keeping them in a separate file just makes them easier to maintain. If you are going to put code blocks in them, you have to place them where the actual code would be going if you wrote it out.
1) stuff that always changes from project to project. They are usually called 'config'
2) stuff that never changes at all. they are usually named with the specific thing they deal with.
the configs are declared at the top for ease.
the non-changing ones usually go at the bottom of the configs so you don't have to think about them other than to make sure they're added to the project. Also, since most of what they use is actually declared in the config file above.
another opportunity for me to trot out my annoying plea for a Netlinx 'Super Include' basically an include that doesn't compile linearly with the rest of the code. That way, functions, variable, constants, devices that haven't appeared yet in the linear compile don't throw and error until the complier has made it all the way through all the code. That would solve soooooo many issues that make using includes so annoying.
squeak squeak squeak...
Sorry I blew up...
This is why I put mine farther down the axs file. I declare mine between DEFINE_LATCHING and DEFINE_MUTUALLY_EXCLUSIVE and then DEFINE_MODULE after that. I don't really have any methodology, but its worked out fine so far.
Paul
My debugging functions include and any job related constant includes go right before DEFINE_CONSTANT.
My "data" includes (with variables and lookup functions) go after DEFINE_VARIABLE, but before any function defs.
My device control and module/subsystem includes all go right before DEFINE_START.
The main thing I try to remember is always put includes on the line before a DEFINE_...something line - because the includes can sometimes have define sections in them that you forget about over time and whatever section the include leaves open or active is what the next line of your main code will be in.
so an include like this ("ex1.axi"):
would cause counter-intuitive results when used in a main code file like this:
Now you essentially have CALL 'INIT' defined in the DEFINE_PROGRAM section, and that will run it over and over.
My 'master' source:
Devices.axi:
A module example:
A 'singleton' include:
And finally, an include that can be used repeatedly (such as a common functions library)
0. General rule: put #includes just before a DEFINE_*. Rationale: your include may contain several DEFINE_SOMETHING sections. By putting it just before one in your main program, you ensure that the main program is back on the correct DEFINE after the include is... well, included.
1. Device definitions, which I put at the start of define_device (breaking Rule Zero). These includes are written per type of controller, to allow me to do my define_device section with reference to concepts like "the I/O port" without having to remember that it's 5001:17:0 or 5001:4:0. So I have files like 'ni3kdev' and 'ni700dev'. For example, my 'ni3kdev' contains ...and my main program's define_device might be:
2. Utility functions, as JasonS says. In these cases the include contains one or more define_function()s. These are general-purpose things that I use often, but I don't use all of them for every project. I put them at the beginning of the define_function / define_call area.
My philosophy is that my function libraries, like my modules, should each do one thing and do it well. I try to create robust, portable building blocks that can be reused again and again. On that note, why would anyone use an include that is customized for a specific main program? If you're going to do that, you might as well just put that code in the main program and ditch the include; if nothing else it will be more readable.
Finally, and off topic: does anyone really call their programs Master.axs? I like the program_name to at least give a small hint as to the nature of the system. Like 12th_floor_command_center.axs. Unlike includes, this really is customized to a specific system.
Just my 1.8 cents.
.
Readability is exactly why I customize those includes to the specific project. The sub systems are contained in their own include, this makes it much quicker and easier to navigate the main program. These includes generally contain structures that require customization for the particular project, room names, source destination names, security levels, etc. The includes are designed to to be customized, code that should not be edited is notated as such as well as areas where customization needs to happen. This makes the main program much clearer because it is not full of implementation details.
I completely agree with this. After you hit about 5000 or so lines in main code, you really have to start breaking things down into functional units or it will be impossible to follow the program flow.
The job I'm working on today has only 2000 lines of code in the define_event section of main code.
But that section defines all the pushable user interface buttons so I can quickly see where to look to diagnose any action that is not happening properly.
The functions that are referenced there are scattered across 1.5 Meg of axi and axs files and would have made for a main code file in the primary master with nearly 30,000 lines if they were all inline. I definitely prefer a hierarchical overview.
There are a few tricks you can use to get around this. Although the compiler is not multipass there's a bit of an oddity with functions - these can be referenced before they appear in the compile order. This enables you to then do things like build getter and setter functions (albeit without any actual privacy for the actual variables) as well as callbacks to keep things a little more modular.
I honestly don't see a big issue to fix the problem. The major errors that result from trying to use INCLUDES that are not syntax related (or other programming issues) is that something or other is not yet declared. During compile all one would have to do is essentially say, " Hmmm.. This function call/variable/Constant/ etc... Hasn't been declared yet... I'll just put a sticky note on it and keep going until the end of my first run through and see if it shows up. If not, I'll throw the error."
The first compiler pass wouldn't need to actually generate any machine code. It would essentially be a pass through to make a general outline/roadmap of the full program. If all the mojor pieces are correct, then you can run through again using the first pass as a road map to locate all the prices and parts needed to do a full compile.
I've heard the argument about loosing the linearity by having a super include but if this is true, why then does it not throw an error if you call a function on line 100 if the actual function declare is on line 200?
And while it is true that other programming languages don't do it either, it's kind of non sequitur because INCLUDES in other SDKs don't look or behave at all like includes in Netlinx.
Bingo. If you reference include file variables indirectly through function calls (Set_Variable(x), Get_Variable()) you don't need to worry so much as to the where a particular include file is the compile order.
I agree it doesn't seem like it would take much for AMX to fix this. However what I have been told by an AMX Engineer is that they are very reluctant to touch the compliler unless there is a major bug.
I never seem to have problems with variables across multiple includes, it is only function calls.