Notifications
Clear all

[Closed] The simplest bug ever

This works:


names = #("test.ms")
i = 1
include names[i]

This doesn’t work:


names = #("test.ms")
for i = 1 to 1 do
 include names[i]

– Compile error: include expected filename string

– In line: include n

What’s going on here? Am I blind to an obvious programming bug, or have I uncovered a bug in the compiler?

7 Replies
1 Reply
(@bobo)
Joined: 1 year ago

Posts: 0

As explained in the Reference, include() is execured at compile-time. The file name should be resolvable when the code is compiled, it is never execured while the code is executed.

In your first example, the argument of include is an expression containing global varaibles which are resolvable at compile time (since the string is “visible” to the function from any scope). If you put the same code into a local scope by enclosing it in ( ), it will stop working.

In the second example, your code says “include this file i times during the loop”. This is, of course, not allowed because it would mean executing include at run time, while we already know it is executed only at compile time. The for loop defines a local scope, so the argument to it cannot be resolved without actually running the code – the i variable is valid only during the for loop execution and is removed as soon as the for loop exits, so it does not exest when the parser is going through your code…

Hmm. I see. Thank you for pointing that out, Bobo. The reason I wanted this is because I am making a rather extensive plugin, and I would like the users (or myself) to be able to write their own plugins for the plugin…sort of like writing custom shaders for a renderer.

I suppose this could be accomplished by making a separate “plugins.ms” script that was called by the main script, and then have entries to each plugin script manually entered as includes in the plugins.ms script

1 Reply
(@bobo)
Joined: 1 year ago

Posts: 0

This sounds like a possible strategy.

I would like to point out though that there is no reason to try to include multiple source files into a single code stream! You can also use FileIn (which is executed as runtime and is similar to calling EXECUTE() on a complete .MS file, including the fact that the execution is performed in Global scope). FileIn() is much more flexible, you can call it from a loop, which means that you could have a UI control for editing plugin paths, write these paths to INI file using setIniSetting, then next time you run the script, read the INI settings and fileIn all the sources of “plug-ins” in a for loop… As long as these plug-ins do not have to be included in some inside scope of the main script (usually they do not have to be), this method will be much nicer.

In addition, you can also put all functions used by your code in a global struct and save as a separate .MS file. While completely independent from your other script files, if placed in the \StdPlugs\StdScripts or in a subfolder thereof, the struct will be accessible by your other code and thus be indirectly part of it, without any include() or fileIn().

There are really not many cases where you HAVE TO use include() and fileIn() in MAXScript, at least compared to other languages. Don’t let your knowledge of C++ or MEL dictate your scripting style in MAXScript, it is much more flexible and expression-based than those…

This sounds like a possible strategy.

Oops, nope it’s not! It turns out that the compiler does not do any pre-processing on the included files! Thus, if you nest include statements, it won’t work! This seems pretty silly…definitely not like C++ includes.

Filein is nicer not only because it gets around this but because it lets me include encrypted files. Unfortunately, it does not check for file headers or anything…so it seems that in order to include an encrypted file the file MUST have the extension of “.mse”

There are really not many cases where you HAVE TO use include() and fileIn() in MAXScript, at least compared to other languages. Don’t let your knowledge of C++ or MEL dictate your scripting style in MAXScript, it is much more flexible and expression-based than those…

I think that abstraction necessitates it. My efficiency gets lower and lower as my script files get longer and longer. It gets harder to scroll around and locate things, harder to make sure that dependencies are maintained (with order of declarations), etc. What I wouldn’t give to be able to define/declare separately, and use header files!!!

The more I work in max script, the more tricks I learn to help me manage my code with better style. I think that I am going to have to start using a lot of fileins to keep my “main” script file clean.

I’d be happy to hear what kind of tricks YOU use to maintain good code style…because I know you are working on some large scripts, like that graph material editor (which is pretty cool).

Here’s my current organization plan:

ORDER WITHIN MAIN MS FILE
Same as regular MS files, except put ( ) around everything to act as a namespace

ORDER WITHIN MS FILES:

  1. define any top-level constants
  2. “declare” top-level funcs with empty stubs
  3. “declare” top-level structs with empty stubs
  4. “include” each top-level func from an external ms file
  5. “include” each top-level struct from an external ms file

ORDER WITHIN STRUCTS:

  1. “declare” each local function with empty stub
  2. define any “do_not_use” variables, which allow you to pass in parameters to be used directly in “constructor” functions
  3. define each local variable, make sure to set default values or constructor functions, or undefined if no default value to prevent them from being dropped from instances

EXCEPTIONS
Note: If struct requires “this” member, then instantiating the object must always be proceeded by settign the this variable to the instance pointer

Note: If “this” member needs to be used by any constructor functions, then use undefined instead of the constructor functions, and make an “init” local function which does all the constructing, only to be called externally after “this” has been declared

eg:

instanceName = structName()
instanceName.this = instanceName
instanceName.init()

Something else to keep in mind when using fileIn:

Normally the compiler will use the most recently defined version of a function/struct, which allows you to define all your functions with stubs at the top and then use them later on without worrying about the order that things were defined in…

…but this appears to be done statically rather than dynamically, so if you want to use a function which should be available due to including with fileIn, it won’t be recognized. You could make a function stub for it before the fileIn, but it won’t be overriden…so then you’ll get a different error!

There seems to be no solution, other than compiling twice in a row the first time you attempt to compile after restarting max.

1 Reply
(@bobo)
Joined: 1 year ago

Posts: 0

Don’t think this is the case. Is the function stub defined in GLOBAL scope?
(FileIn evaluates in Global scope, so if your function/struct is not global, it will not be updated)

You are correct