Coding conventions

As I’m going forward with the community scripts project, I figured it’d be smart to get some coding conventions set up for it. I could do this arbitrarily, but I suppose it could also be useful to generate a dialog we all can benefit from. What we come up with here will eventually be formalized into coding conventions for the community scripts. I will keep an up-to-date list of agreed standards on [w]TAO:Code Conventions[/w]

I can only really cover maxScript and HLSL since that is what I’m familiar with. And I don’t think Python will be much of an issue since its formatting is pretty rigid :slight_smile:

Remember the code for highlighting is [noparse]


[/noparse] (replace mxs with mel, python, or hlsl)

[legend]MAXScript[/legend]
[ul]
[li]For single line program flow statements, either go to the next line and indent after then, or put in brackets. For example:
[/li]

if a == b then
	print "This is fine."
else if a == c then
(
	print "This is fine also."
)
else if a == d then print "This is not fine!"
else print "This may be fine, since it is an else, your call."

[li]Parenthesis should be at the same indentation as the code outside of them, NOT indented to be with the code inside of them. This is the default behavior of the MXS Pro-Editor and should be standardized.
[/li]

if true then
(
	print "Proper indentation"
)
else
	(
	print "Improper indentation"
	)

[li]counter for loops should be ‘for i = 1 to num’ as opposed to ‘for i in 1 to num
[/li][li]Restrict use of global variables. This itself deserves an entire article, I think, but please restrict your use of globals, people reading and debugging your code will thank you. You should never use globals to keep track of rollout UI control values unless there is really no other option- instead, declare the rollout as a global and refer to the values of the controllers (rollout.control.value, for example). If I can’t find a good article about restricting use of globals, I’ll write one myself. If you must use globals, give them a good long prefix, so there will be no conflicts, and you will be discouraged to type them out (the latter is the main reason :slight_smile: )
[/li][li]Declare your variable scope. When you are completing code, make sure you declare the scope of your variables explicitly (with ‘local’ or ‘global’). It makes everything much easier to read and debug.
[/li][li]Iterate for loops with i, j, k, etc.
[/li]

for i = 1 to 10 do
	for j = 1 to 10 do
		for k = 1 to 10 do
			print (i + j + k)

[li]Don’t use parenthesis needlessly. Especially in for/while/if statements, where you are comparing clauses… MXS is usually smarter than we give credit to in how it evaluates these, you don’t need to type 'if (a == b) then…, use these parenthesis the same way you would use them for math. ie, you would type 'a = b + c + d, not 'a = (b + (c + d))’, yet I see wasted parenthesis a lot. Just be smart with them, the MXS help is there to provide you with details on how Max evaluates comparison operators.
[/li][li]Tabs, not spaces. I shouldn’t have to say that, though.
[/li][li]Read “How To Make It Faster?” area of the MXS Help (thanks Bobo!). Most of the stuff here will be functions, so they should be optimized well… this area of help is one I have read many times over until I’ve memorized it and I have had countless applications for it, learn it!
[/li][li]Balance optimizations with readibility. Since MXS performs no optimizations, it can be quicker to pack all your statements into as few variables as possible… however, this can be difficult to read. It may be worthwhile to provide an ‘expanded’ version in commented code for learning/debugging/evaluation purposes.
[/li][li]Comment your code. I am such a hypocrite :wink: But ideally lots of people will read, use, and learn from your code, so the comments will actually benefit people, even if you don’t get hit by a bus.
[/li][/ul]

Disagree? Let me hear it! I don’t always follow this in my code, but if I’m writing stuff others will likely have to read or get into I try to follow it.

What’s the general consensus on same-line parenthesis opening? I see a lot of people use this method:


fn myFunction = (
   --do a whole lot of
   --code here!
)

Whereas many also use:


fn myFunction = 
(
   --do a whole lot of
   --code here!
)

I much prefer the latter over the former as it’s much cleaner to read, but others may disagree.

I’m sure I’ll have more feedback on this later, but I wanted to quickly write up some initial thoughts.

[ul]
[li]Iterate for loops with i, j, k, etc.[/li][/ul]
I actually prefer that the iterator be descriptive, although I don’t have particularly strong feelings about this. I’ve found that a descriptive iterator can be pretty useful, especially whenever you have nested for loops, with lots of code, A couple of examples would be:



for obj in objects do (
   for mat in obj.materialList do (
   
   )
)

for objIndex = 1 to myObjectArray.count do (

)


Something I would add is standardizing the comment headers for functions. My main reason for this is since you can’t declare types in MXS, the function “header” gives the user more information. We’ve found this to be particularly useful here at Volition, especially when it comes to debugging.

An example could be:


--
-- This is a description of what the function does.
--
-- Arguments:
--
--   <bool> arg_a:                 A description of the argument.
--   <int> arg_b:                   A description of the argument.
--   <myStructClass> arg_c:   A description of the argument.
--
-- Returns:
--
--   <bool> A description of what the function returns, if anything.
--
fn myFunction arg_a arg_b arg_c = (

)

Erilaz: Here at Volition, we use this standard on functions/structs/classes:


fn myFunction = 
(

)

struct myStruct
(

)

and this for pretty much everything else:


if true then (

) else (

)

This came from the coding standards that our programmers use.

[QUOTE=jhayes;744]

and this for pretty much everything else:


if true then (

) else (

)

This came from the coding standards that our programmers use.[/QUOTE]

It may be a personal taste thing, but I find that more problematic to read. I feel it breaks flow if you go from one standard of bracketing to another.

If it’s a programming standard, then let’s use it, but i’ve never liked it.

As to iterators, if they’re basic then the standard i loop is fine. I prefer descriptive iterators, but only if they aren’t obvious.

On inline parenthesis opening:
I used to do it inline, but switched to next line, which I like much more now. It makes the code much cleaner to read, IMO, even if it does add extra lines. All programmers I know have always done it the same way, interesting your coding standards are different over there… if I had to guess which was more predominant, I’d have to say it’d be the next-line version, but I could be wrong.

On for loop iterators: I mean only i, j, k when they are equal to an integer/are counting… I tend to have semi-descriptive iterators as well, perhaps there should be standards for this as well (I tend to use ‘for s in selection’, ‘for o in objects’, ‘for a in theArray’ (if it is a small for loop, something more descriptive if not). Should have made that clearer.

BTW, just asked a buddy who was a Visual Studio dev lead a couple years ago, he said:

Not that it matters much what one programmer says, we should go with what we are comfortable with as a community.

I wanted to comment on “comments”. Since I’ve been doing more Python, I’ve slightly altered the way I do “Doc Strings” in MAX.



fn add_numbers numArray =
(
   --<DOC> add_numbers simply loops through numbers
   --      and add 12 to them. It's pretty amazing.
   --
   --      Arguments:
   --         <array> numArray - an array of numbers
   --
   --      Return:
   --         <array> return_array - returned array values

   ...
)

The above is a method that I’ve started to use, I use this for 2 reasons.

  1. in reading through the code, I read “fn name” then followed by what the function does. It just helps me keep track of what I’m looking for.
  2. if we ever write a “doc reader”, a tool that prints the functions and their behavior, I can detect function first, then if it’s found, I can just start returning that data instead of backtracking. Doc Strings in scripts I’ve seen sometimes can be “loose” They don’t always follow the proper standard, so if they just include it right inside the function, knowing where to look becomes easier. IMHO.

Of course standards may vary from studio to studio.

I’ve always tended to use inline parentheses, and all our programmers here (and at id software too, as far as I can see) use the same technique.
Personally I just think it seems a bit unnecessary to use an entire line on a parenthesis, when you can easily see that the next line is contained within it due to being indented.

I’m a newbie, though :slight_smile:

Another thing I’ve gotten in the habit of doing is to surround all Bool test’s with
(). It might seem like overkill, but it really helps me understand the logic quickly. Also i’ve surrounded the entire expression in my example, but sometimes I do leave that off.


-- if x == True or y == False then (
-- ...
-- )

I prefer this:


-- if ((x == True) or (y == False)) then (
-- ...
-- )

So if the Test Variable is very long, say data from a big struct, surrounding helps bound what you need to read.



-- if ((struct_data_Array.PosValue >= 2.3) and (struct_data_Array.RotValue.z <= 23)) then (
-- ...
-- )


P.S. For some reason I can’t read these lines easily; Black text on Grey background; so I’ve commented it all so it shows up in green. Sorry if that is confusing.

It’s a general convention at our studio that all if expressions are individually braced, as well as the bodies of every if/while/etc. block (even the one-line blocks).

I find it more consistent and easier to read, and makes it faster to add more lines to those blocks later (which happens often).

To the original post, even though Python uses indents instead of braces for blocking, there’s nothing particularly rigid about it. The conventions in general are open, with many choices to make with regard to setting consistent standards. For example:

  • Case for functions/methods (lower camelcase -> theCoolFunction)
  • Case for Classes/Structs (upper camelcase -> TheCoolClass)
  • Case for Constants (MY_SHAMEFUL_HACK_VALUE)
  • Function/module header conventions (and optional use of doctest strings)
  • Handling for private/semi-private methods
  • Use of tabs or spaces for indents. If spaces, how many, etc.

… and so on. Despite that, I think setting consistent conventions for some central code repository on TAO is a pretty big undertaking. I’m not suggesting we don’t try, but I hope nobody expects any two of us to agree completely on every line-item in the standards. :slight_smile:

I use the Microsoft .NET Coding Standards. That way I don’t have to worry about which language I am coding in, or invent a new standard myself.

Here is a link that shows some of the convention.

Light

To quote some kind of Caribbean pirate:

“They’re more guidelines than actual rules”

Although i’d never use spaces for indentation. It gets messy when you have to nest stuff.

Regarding indentation, I used to use the inline method, but switched a while ago. Now whenever I open an old script I need to reformat it so I can read what’s going on.

But beside the easier to read aspect of the next line method, there is a very practical reason to use it. When debugging, it’s extremely fast to comment out if/then statements and thereby force the code within the brackets to execute. If your opening bracket is on the same line as the if/then, you have to do some extra typing to comment it out.

I’d also like to propose thinking about establishing common naming conventions for things like functions. The use of a common set of words on things like create vs. make, node vs. object, get vs. find, etc., will make it much easier to organize a collection of tools, and to find any particular one you’re looking for. Also, a common way of naming controls would be good. I use an underscore followed by a two or three letter abbreviation, like “doItNow_BTN” for a button, or “option01_CB” for a checkbox, etc.

I would also vote for following the Microsoft .NET Coding Standards. Why come up with new standards when there a set of already established and widely used ones?

Wiki page has been updated ( [w]TAO:Code_Conventions[/w]). Please give me suggestions and recommend changes. Thanks all for the input.

Great write-up Rob!

Should we have a convention for unique id tags in callbacks? It’s very easy to come up with a “unique” id that clashes with something else down the track.

I looked briefly, but does anyone know of a function in max that actually builds uuids? I found a nice simple one in Python, but it would be a bit convoluted to do it that way. :):

If you don’t mind a somewhat unweildy ID, you can use:

 
uniqueID = (genClassid returnValue:true) as string

Although IDs in callback defs are optional, it might be better to just leave that to the person using the code. I often group several related callbacks by the same ID.

I guess I’m a “ParenNut.”

I prefer them indented at the same depth as the code, when seeing the ( it’s inline visually with the code that is relative to those ()'s

Ever since Ctrl-B started “selecting all” within the (), {} ,[]'s… I’ve used them liberally… If I wish to reuse a value, collection, expression, or part of one… I click inside and hit Ctrl-B till I have what I want…

The only time I use the inline paren’s, is for simple try()catch() situations.

Try(
)Catch()

That way I can “–” only two lines to disable it…

It’s a good guideline for community work


KTM 525XC-G

How about adding parenthesis for


for i = 1 to 10 do
	for j = 1 to 10 do
		for k = 1 to 10 do
			print (i + j + k)

Since it is exemplary code, it should at least conform to the previous standard. And no, the above is not a very short nor simple statement.

As for braces around conditional expressions, it might be a good idea even if maxscript is lenient enough to not require it. It breaks up the code into semantic areas for better readability. Likewise goes for the optional statement termination ‘;’. Its not needed, but it does give a strong intent that the line ends NOW.

I like the terminator, in that anything I ever see after that, I can chalk up to someone accidentally hitting the keyboard when the max code window popped up - which incidentally happens to me quite often.

As for globals variables, might I suggest pseudo-scoping them in a struct?


struct FooNamespaceStruct
(
  fooCount;
)
FooNamespace = FooNamespaceStruct();

As for Balance optimizations with readibility, I’m not sure if it’s such an issue except for the most inner loops. The main cost with MXS is spooling up the parser. Variable access ranks far down on my list of concerns, far less than memory allocations. I’d hate to see someone make code less readable because of this guideline. Maybe we can create another page on optimizing maxscripts?

Ted

I agree with Ted about the parentheses and the optional statement termination ‘;’. To me its less confusing since I’m constantly going form C++,C# and Max script. Further more we use Hungarian notation in our project Both MaxScript and C++ even though I hate it, but it better to respect a coding standard then having none. As far as going global for rollout instead using global variables we started doing this, its less painful to use that way in my opinion.

But for both C#, or cross project C++ Maxplugin I use like Yunus the Microsoft .NET Coding Standards. Coding standard is ext really important but I prefer to see lousy syntax then lousy architecture. One thing i cant stand is kilometers of code in event handlers A script should be able to run both in ui or batch mode but again that’s just an opinion :wink:

I ran across Jeff Hanna’s coding conventions writeup from January 2007, might be applicable here…
http://www.scriptspot.com/3ds-max/tutorials/maxscript-coding-standards

I’m just starting out with MXS, so I can’t really provide any input yet.