Porting Scripts to Cpp (XSISDK)

Table of contents

Tips for converting Scripting code to the C++ API.

Major reasons to port a working script to C++ is to make it faster or to hide proprietary source code. Other reasons could include the desire to connect to a 3rd party library like opengl.

Missing Objects

As described in "How is the COM API Implementation Different from the C++ API?" (http://softimage.wiki.softimage.com/sdkdocs/ca459594.htm) some scripting objects are not available in C++ API.

Often there are clear reasons, and an equivalent is easily found in the C++ objects.

For example SIObject::GetNestedObjects (http://softimage.wiki.softimage.com/sdkdocs/sicppsdk/html/classXSI_1_1SIObject.html#a21) can retrieve operators under a primitive, reducing the need for a ConstructionHistory (http://softimage.wiki.avid.com/sdkdocs/ConstructionHistory.htm) object.

In other cases the only way to port to C++ is to use CComAPIHandler, which lets you call Scripting objects from C++, without forcing you to use actual COM code. See Mixing the C++ API with the COM API (http://softimage.wiki.softimage.com/sdkdocs/ec447735.htm).

Also important to mention: if you have certain code that works really well already in scripting consider leaving it in scripting and calling it as a Custom Command from C++. Because XSI supports a mix of Scripting and C++ you can have the best of both worlds.

Working with Type

The major challenge when converting scripting code to C++ is the fact that C++ is a typed language. Each variable, function argument, return value must be declared with a specific type.

function get_springforce (in_del, in_len, in_springK, in_bounce, in_compression)

// C++ 
void get_springforce (const CVector3 & in_del, double in_len, double in_springK, double in_bounce, bool in_compression)

Be careful when converting jscript expressions to C++. Since Jscript is not a strong type language, it's more permissive with expressions that are mixing double and LONG values. However C++ is very sensitive when mixing doubles with LONG so it's easy to get into trouble.

// JSscript
var segment = 1/len;

// C++
double segment = 1/len; // wrong

will give different results than

double segment = 1.0/len; // good


CValue is the C++ API equivalent to the VARIANT structure which scripting languages use to represent all values and variables. It offers major relief from the fact that C++ is so strict about declaring type.

When porting scripting code use CValue for any arguments or variables that have unclear type. You may get compiler errors for some variables, but this approach can provide a quick start for handling some of the most straightforward code.

A CValue can hold a pointer to an XSI object (e.g. a CRef). However you can't call any methods on the object without first assigning the CValue to a specific C++ API object such a X3DObject, Parameter or Property. See Cpp Compilation (XSISDK)#Cannot_Convert for a tip about a compiler error you might see.

With XSI v5.1 CValue now supports CVector3 objects. However other math objects are not yet supported (CRotation, CMatrix4 etc).


CValueArray is the approximate equivalent of a VBScript array (SAFEARRAY) or JScript array. Because it is based on CValue it can store many types of data, including strings, numbers and references to XSI objects.

However as a performance advantage XSI also offers specialized arrays, e.g. CDoubleArray. It takes much less memory to represent many doubles in a CDoubleArray than in a CValueArray.

Arrays of Objects

XSI objects are represented with the CRef object.

So the workhorse representation of a list of objects is a CRefArray. It is the equivalent of an XSICollection in the scripting world.

You can also hold a list of objects inside a CValueArray, because a CValue can contain a CRef.

A single CValue can contain a CRefArray, just as a scripting variable (VARIANT) can hold a VB or JScript array.

Important: There are also specialized collections such a CParameterRefArray. These are returned by various C++ methods. However you normally should use CRefArray instead of one of these collections because you cannot add and remove arbitrary items to these collections. So, to build a list of Parameters use CRefArray instead of CParameterRefArray.

Easy LogMessage

You can concatenate strings with a syntax that is similar. Thanks to the CString constructor and CString::operator+ method.

Application.LogMessage( "Property name: " + prop.FullName ) ;

Application().LogMessage( L"Property name: " + prop.GetFullName() );

Non-string types can be converted easily with the CString constructor.

// JScript
var x = 55 ;
Application.LogMessage( "A number: " + x ) ;

// C++
double x = 55 ;
app.LogMessage( L"A number: " + CString(x) );

CValue::GetAsText() is the long way for converting non-string types.

CValue paramVal = oParameter.GetValue() ;
app.LogMessage( L"Parameter value: " + paramVal.GetAsText() );

Common Compiler Error with Numeric Constants

<filename>(53): error C2664: 'XSI::Parameter::PutValue' 
: cannot convert parameter 1 from 'int' to 'const XSI::CValue &'

Provoked by code like this:

Parameter newParam ;
newParam.PutValue( 5 ) ;

XSI uses LONG exclusively rather than int. This is for compiler safety across windows, linux and the 32 and 64 bit platforms.

So to get the code above you need to add a type cast:

newParam.PutValue( (LONG)5 ) ;

Note: Using the "L" symbol (as in 5L) will fix the compilation on 32 bit machines, but results in a long rather than LONG. For truly cross platform code you must use the typecast instead.

An alternative solution if the expected value is actually a float or double is to add a decimal point:

newParam.PutValue( 5.0 ) ;

With XSI v5.1 this restriction has been fixed (with a new constructor for CValue), so newParam.PutValue( 5 ) ; will compile


All string literals will need an L character in front of them. E.g. "posx" becomes L"posx". This is because XSI works with wide characters (two bytes per character) for its CString object. This is necessary for Unicode support.

See also C++ API Overview - Strings

Invoking XSI Commands

Many scripts invoke lots of XSI commands. You can still invoke these commands from C++ API, using Application.ExecuteCommand. The cmdstubs utility script that ships with XSI makes it more convenient to call XSI commands.

Object Properties

All scripting code that uses properties must be converted into calls to the equivalent C++ API methods.

For example in scripting the name of an object is exposed as the Name property.

obj.Name = "NewName"
logmessage obj.Name

In C++ all data of objects is exposed with Get/Put methods. There is a naming convention so that it is easy to find the equivalent methods for each property.

obj.PutName( L"NewName" ) ;
app.LogMessage( obj.GetName() );

In the case of grabbing the parameter from a property, you may have to break apart the line into two halves. This is because GetProperties() returns a CRefArray, so you eventually have to cast the extracted CRef into a Property before you can continue accessing its parameters. Here's an example:

obj.properties("display").wirecol.value = 126;

turns into

Property displayproperty = obj.GetProperties().GetItem(L"display");

See below for a more detailed explanation of why this works.

Parameter Shortcuts

Here is a typical example of setting a parameter value on the property of an object.

myobj.properties("display").wirecol.value = 126;

You can port it if you deconstruct the line a bit.

  • Access the properties of myobj
  • Of that list, choose the property called "display"
  • On the property find the parameter called "wirecol" (using the scripting parameter shortcut syntax)
  • Set the value of that parameter to 126

C++ does not support Parameter Shortcuts (because it is a strongly typed language). But some convenient methods are provided.

So an example C++ equivalent:

Property displayProp = myobj.GetProperties().GetItem( L"display" ) ;
Parameter wirecolParam = displayProp.GetParameter( L"wirecol" ) ;
wirecolParam.PutValue( (LONG)126 ) ;

There is also a convenient shortcut for setting a parameter value without creating a Parameter object:

Property displayProp = myobj.GetProperties().GetItem( L"display" ) ;
displayProp.PutParameterValue( L"wirecol", 126 ) ;

This can also be expressed in a single line, just as in scripting but it is probably too complex for a single line of code:

Property( myobj.GetProperties().GetItem( L"display" )).PutParameterValue( L"wirecol", 126 ) ;

Note that it is necessary to create a temporary Property proxy. This is because CRefArray::GetItem() does not return a Property, but instead a CRef object, which does not offer the PutParameterValue method.

Nurbs - UV versus VU

The scripting version of X3DObject.AddNurbsSurface and NurbsSurfaceMesh::Set expect the UV values to be organized as rows of Vs. But the C++ API expects the transposed ordering - as rows of Us.

In otherwords for scripting the data is:

 (0,0)...(0,cntV-1), (1,0)....(1,cntV-1),(cntU-1,0)...(cntU-1,cntV-1)

and in C++ it should be:

(0,0)...(cntU-1,0), (0,1)....(cntU-1,1)...(0,cntV-1)...(cntU-1,cntV-1)

(This different data representation will be more clearly documented in future releases of the documentation.)

Handling a JScript Array in C++

In C++, if you call execute JScript command that returns a JScript array object it will show up in your C++ return value as CRef. This contains a pointer to the JScript automation object. It is possible to access this using the CComAPIHandler but it isn't fun.

A quicker method is to change the JScript command to return a SAFEARRAY object and let XSI do the conversion to CValueArray for you.

function JS2VBArray( objJSArray )
    var dictionary = new ActiveXObject( "Scripting.Dictionary" );

    for ( var i = 0; i < objJSArray.length; i++ )
        dictionary.add( i, objJSArray[ i ] );

    // this returns a VARIANT of type SAFEARRAY
    return dictionary.Items();

This page was last modified 21:40, 10 Dec 2010.
This page has been accessed 31109 times.

© Copyright 2009 Autodesk Inc. All Rights Reserved. Privacy Policy | Legal Notices and Trademarks | Report Piracy