An interesting concept behind Structs in MAXScript

loocas | 3ds Max,maxscript,technical | Tuesday, December 8th, 2009

I bumped into this issue of referencing values inside of Structs, which is a very elegant solution to using variables across your code, while avoiding global declarations. The issue was pretty much that I wasn’t aware of the implementation design of Structs in MAXscript.

Basically, Structs are these overly simplified custom classes know from such languages as Python (to which I’ll try to compare these). However, Structs are really so simple that they don’t even implement such functionality as inheritance (a pitty by the way), or more advanced functionality known from Python. Structs, rather than classes, could be called groups. That’s what I’ve been using them for mainly. I grouped a bunch of functions and called them via a standard attribute reference paradigm.

Here’s a super simple Struct encapsulating a function that prints “HELLO WORLD”:

struct tstStruct
(
	fn printHello =
	(
		print "HELLO WORLD"
	)
)

You can then call the function via it’s parent, the struct, like this:
tstStruct.printHello()

A very convenient method for grouping tons of functions together and not messing around with namespaces or any other, possibly existing, names in your MAXScript session. However, after I read the article on avoiding using global variables at all times, over at tech-artists.org, I discovered new possibilities and immediately started writing much cleaner and more elegant complex scripts! However, since I’m used to classes from Python and I’ve never used variables inside Structs, I bumped into a problem I didn’t know about. When you declare a struct with a variable in it, the variable isn’t immediately accessible from the outside of the Struct, you have to, sort of, initialize the variable at first. This is what I did wrong:

struct tstStruct
(
	myVar = 0.,

	fn printHello =
	(
		print "HELLO WORLD"
	)
)

I simply assumed that the variable is there and I can access and modify it at will. But I was wrong. You see, accessing the variable myVar right after the Struct’s declaration will throw this error:

-- Unknown property: "myVar" in #Struct:tstStruct(
  myVar:<data>,
  printHello:<fn>)

What I didn’t know was that I had to initialize the variables in the Struct in order to be able to access them. The problem was that Structs don’t provide special methods (functions) for such things as Python does, for example. So I couldn’t use something like the __init__ function. I fired up the MAXScript reference and read that in order to initialize the variables, I have to assign the Struct to a new object and explicitly assign to the variables, which is highly inconvenient, by the way. Anyways, here’s how to do it:

struct tstStruct
(
	myVar = 0.,

	fn printHello =
	(
		print "HELLO WORLD"
	)
)

clsInst = tstStruct myVar:0.

At that moment, you have to call the clsInst object in order to access all the functions and all the explicitly declared variables:

clsInst.myVar; clsInst.printHello()

Now it works as it should and I can use the variable inside the Struct to assign to and read from in my scripts, which is a very good practice as I’ve learned :)

Let’s see how it’s done in Python. Python’s classes are the real deal, they support such advanced features as inheritance, multiple inheritance even, and also provide a much, much broader functionality, such as special methods. However, there is a bit of similarity between MAXScript’s Structs and Python’s Classes. Let’s see:

class tstClass:
	myVar = 0.
	
	def __init__(self):
		self.testVal = 10.

	def printHello(self):
		print "HELLO WORLD"

The simple code example above declared a new Class object named tstClass. The Class itself contains a variable object, myVar, a mandatory __init__ method and a custom function, printHello, similar in functionality to the MAXScript counterpart. However, the similar behavior of the Class and the Struct is in that when I call the Class directly and ask for its myVar variable, I get a value of 0.0, which is correct, since I haven’t initialized the Class object yet and thus the __init__ method wasn’t executed. The similarity is in that the Class wasn’t initialized, but I can still access the variable myVar without Python throwing an error. Which was what I assumed from MAXScript at first.

However, as soon as I initialize the Class by assigning the object to a new variable, I execute the __init__ method and thus overwrite the myVar variable with a value of 10.0. By calling this code snippet:

instCls = testClass()

print testClass.testVal
print instCls.testVal

I get this output:

0.0
10.0

Meaning that the myVar value called upon uninitialized Class is still the default 0.0, but as soon as the Class gets initialized, the variable gets assigned a value of 10.0 in the __init__ special method.

Unfortunately MAXScript doesn’t utilize special methods for its Structs, thus the initialization is a bit cumbersome and unintuitive, also quite inconvenient when having tons of variables in the Structs. Same goes for the self special object that references objects inside the Class, which is very handy.

EDIT: As Mathieson pointed out in the comments below, you don’t need to explicitly call the variables when instantiating the Struct. All that really is needed is to instantiate the Struct properly for the inicialization process to take place and then you can access all the variables within the Struct. Thanks again to Mathieson for pointing that out!

3 Comments

  1. You need python, a truly object oriented programing language :-)

    Comment by César Sáez — December 8, 2009 @ 20:10

  2. You don’t have to initialize the variables explicitly, as you have done in your example. Rather than “clsInst = tstStruct myVar:0.” you can simply do “clsInst = tstStruct()” and then the declaration inside the struct should kick in and automatically initialize.

    The problem before was that you were trying to access a value from the struct definition, rather than from an instance of the struct. The variable cannot store a value until an instance of the struct is created, as all it is is the definition.

    When I use structs for creating tools, I usually store an instance of the struct into the same variable name as the struct, immediately after I have defined the struct. Not a good idea if you plan on creating multiple instances of the struct, for instance when using to store data inside of a script, but usually when creating a tool I only create a single instance anyways.

    struct tstStruct
    (
    myVar = 0.,

    fn printHello =
    (
    print “HELLO WORLD”
    )
    )
    tstStruct = tstStruct()
    tstStruct.myVar

    Hope that helps out. I’d hate it if I had to initialize every variable individually. :)

    Comment by Mathieson — December 8, 2009 @ 20:11

  3. Thanks a lot, Mathieson! It makes perfect sense. I appretiate it. :)

    Comment by loocas — December 8, 2009 @ 21:49

RSS feed for comments on this post.

Sorry, the comment form is closed at this time.

Powered by WordPress | Theme by Roy Tanck