Cpp Compilation (XSISDK)

A common hurdle for developers who wish to start C++ API development is the question of machine configuration. The topic "Setting Up Your C++ API Environment" in the XSI SDK documents gives some tips.

Table of contents

Environmental Variables

The most important tip for getting up and running quickly is to make sure you start Visual Studio from a Softimage Command Prompt. This makes sure that XSISDK_ROOT and other important environmental variables are set properly. This works even if multiple versions of XSI are installed on your machine - just use the correct prompt based on the version you want to work with.

Examples

Get the SDK Examples compiling before trying to work on your own .dll or .so file, this will help verify that your machine configuration is in good shape. The documentation for these examples give some helpful compilation instructions.

Linking

The \XSISDK\lib contains all libraries that you should need, unless you are using a 3rd party library like CG, OpenGL etc in which case you should follow the documentation of those packages.

Make sure that all C++ API plug-ins link to sicoresdk.lib and sicppsdk.lib.


Windows Tips

See Visual Studio for tips related to the recommended compiler on the Windows platform.

Compiling Plug-ins with .Net 2005

By default, .Net 2005 compiles in 64 bit, so if you are working in a 32-bit environment, change the Active solution configuration from Debug x64 to Debug:

  1. With the solution open, select Configuration Manager from the Build menu.
  2. If the value in the Active solution configuration drop-down list is Debug x64 or Release x64 and you are compiling for a 32-bit environment, choose Debug or Release from the list and click Close.

Compiling Plug-ins with .Net 2008

VS2008 will recognize and correctly update the .vcproj files generated by XSI to work with that version.

Using Managed Code within plugin

It is possible to use C++/CLI via the /CLR switch. Fairly heavy use of '#pragma unmanaged' is required to prevent compiler errors in this mixed environment. Managed code use comes with limitations: You have to attach to the running XSI process and there is no support for compile-and-continue. Debugging needs to be set to 'Mixed mode'.

Depends

Use Dependency Walker (http://www.dependencywalker.com/) to diagnose any problem with loading a .dll. This tool is so useful Microsoft includes it as part of Visual Studio.

Make sure that you launch Depends using the Softimage Command Prompt so that it runs with the same environment as XSI.

Any time you see an error message about XSI not able to find or load a .dll is a signal that you should load it with depends. This includes the following misleading error message: ERROR : 2223 - Library not found:. This is a Microsoft message that is returned even if the .dll actually exists on disk, but fails to load.

It is often useful to turn on display of Full Paths (F9). If you have multiple copies of a dll like XSIFTK.DLL then you may find that the wrong one is being loaded.

On many machines Depends reports and error about MPR.DLL ("Warning: At least one module has an unresolved import due to a missing export function in a delay-load dependent module.") This warning can be ignored.

Distributing Debug Dlls

If you want to send your plug-in to a different user make sure to send the "release" build version. It will be faster and a smaller file. But more importantly, debug dlls depend on certain Visual Studio dlls (MSVCR71D.dll, MSVCP71D.DLL) that may not be installed on a machine without Visual Studio on it.

Debugging your plug-in

You can use the Visual Studio Debugger to debug your plug-in. You can even use the "Apply Code Changes" feature. You should compile your plug-in with a "Debug" configuration so that the necessary information is included.

Linux

XSI includes several C++ examples with makefiles for Linux, these will be useful for creating your own working makefile.

If your plug-in relies on other dynamic shared objectss (.so files) then you may see error messages if those libraries are not found, or if the wrong version was found in the path. Fortunately the error messages tend to be more clear than the Windows versions, but there are also standard Linux tools for looking at import/export information.

Diagnosing Plug-ins That Refuse to Load

Run ldd from a command prompt (with the correct Softimage environmental varialbles set) to get extra information about a plug-in that fails to load.

ldd -r filename.so

The warning

undefined symbol: mainwin_init

can be savely ignored.

Linking Statically Against a Library

If you use third part libraries, it is often advantageous to link them statically to remove the dependency of you plug-in from that lib (and hence no longer force the user to find/build/install that very lib).

The default GNUMakefile that comes with the XSISDK's examples and from which most people likely derive their initial makefile from doesn't call the linker explicitly but rather has gcc do that implicityly when it's done its job.

Hence, the LDFLAGS variable needs specific flags to tell gcc to pass information to the linker. The resp. flag is -Wl,<linker flag,[linker flag],...>

Below is an example LDFLAGS definition that contains information to statically link boost_filesystem

LDFLAGS:= [...] -L/usr/lib/boost -lsicppsdk -Wl,-Bstatic,-Bsymbolic -lboost_filesystem-gcc -Wl,-Bdynamic

The -Wl,-Bdynamic at the ned is merely there for safety reasosn. If you add any -l<libname> flags at the end of your LDFLAGS, these, again, will be linked dynamically. If you make sure all your static libs are at the end you can get rid of that flag.

Now this still doesn't work. If you do an ldd of your plugin, you'll see that it still has unresolved symbols pointing to your lib unless that lib is the current library searchpath. The reason is that all SDK examples GNUMakefiles contain a gcc compile line with variables specified in the wrong order:

myplugin.so: $(OBJ) $(CPPOBJ)
  @$(CXX) -shared $(LDFLAGS) $(CPPOBJ) $(OBJ) $(DEPLIBS) -o $(BINDIR)myplugin-$(VERSION).so 

Notice where the LDFLAGS are? At the very beginning!

At the time gcc calls the linker, it has long forgotten about the -Bstatic,-Bsymbolic options we wanted it to pass onwards. That line hence should look like this:

myplugin.so: $(OBJ) $(CPPOBJ)
  @$(CXX) -shared $(CPPOBJ) $(OBJ) $(DEPLIBS) -o $(BINDIR)myplugin-$(VERSION).so $(LDFLAGS)

Easy eh?

Plug-In Diet

Gcc is not very good at finding out which symbols are needed and which can be stripped from a dynanimc library by default. It hence keeps everyhing! I found that XSI finds the required entry points, even if the plug-in was stripped of all symbols. It is hence completely safe to strip your shared library from redundant symbols like so (Makefile excerpt):

myplugin.so: $(OBJ) $(CPPOBJ)
  @$(CXX) -shared $(CPPOBJ) $(OBJ) $(DEPLIBS) -o $(BINDIR)myplugin-$(VERSION).so $(LDFLAGS)
  @strip $(BINDIR)myplugin-$(VERSION).so

Here's what it yields in my case.

Without strip:

5207484 myplugin-0.84.1.so

With strip:

2213984 myplugin-0.84.1.so

Voila—about 3 MB of redundant symbol names—gone!

Common Compiler Errors

Undefined Type

Example:

error C2027: use of undefined type 'Project'
c:\softimage\xsi_4.2\xsisdk\include\xsi_application.h(30) :
see declaration of 'Project'

Fix: include <xsi_project.h>

or

error C2079: 'ppg' uses undefined class 'XSI::PPGLayout'

Fix: include <xsi_ppglayout.h>

or

error C2664: 'XSI::CustomOperator::AddIOPort' : cannot convert parameter 1  
from 'XSI::Primitive' to 'const XSI::CRef &'
Reason: cannot convert from 'XSI::Primitive' to 'const XSI::CRef'
Source or target has incomplete type

Fix: include <xsi_primitive.h>

error C2679: binary '=' : no operator found which takes a right-hand operand of type 'XSI::Model' 
(or there is no acceptable conversion)

Fix: include <xsi_model.h>

Details: To increase compiler speed the C++ API header files often uses forward declaration, which can lead to this error if you don't include the header files for all the objects you want to use. Because of the naming convention of the header files it is easy to predict what header file contains each header. Tip: If you prefer you could also create your own header file that includes many of the common XSI header files.

Cannot Convert

CValue is the generalized data type that can store many types of Data, including XSI objects. So code that is being ported from scripting or which is generated by the cmdstubs utility will often use CValue to represent arguments.

For example:

void DoSomething( const CValue & in_XSIRef )
{
  //... stuff happens...
}

Now suppose you want to pass a XSI object to this function, for example a X3DObject....

X3DObject someObj;
// Won't compile: 
DoSomething( someObj );

This is the compiler error:

error C2664: 'DoSomething' : cannot convert parameter 1 from 'XSI::X3DObject' to 'const XSI::CValue &'
Reason: No constructor could take the source type, or constructor overload resolution was ambiguous

This is because a CValue takes a CRef as its representation of a XSI object. All XSI objects can be represented as a CRef, but they don't directly derive from CRef so they aren't really "CRef" objects.

However you can easily turn a XSI object into a CRef, e.g. using the cast operator or the GetRef() method.

And you can easily turn a CRef into a CValue using the constructor or assignment operator.

Put these two steps together an you can build a CValue out of an X3DObject.

In fact it is the fact that there are several ways to convert an XSI object into a CValue that causes the compiler to complain!

So the solution is to give the compiler some hint about exactly how it should turn the X3DObject into a CRef

DoSomething( CValue( someObj ) );

or

DoSomething( someObj.GetRef() );

or

DoSomething( ( CRef& )someObj );

To be really explicit you could use temporary variables. This shows what methods and constructors really get called:

// This is just one of several options
CRef& ref( someObj.GetRef() );
CValue val( ref );
DoSomething( val );

Alternatively you could consider changing the function to be more specific about what type of object you expect, CValue is very vague!

If the function only works for X3DObjects then that should be clear from the signature:

void DoSomething( const X3DObject & in_XSIRef )
{
  //... stuff happens...
}

Or if the function works with any XSI object you might want to use CBase or SIObject:

void DoSomething( const SIObject & in_XSIRef )
{
  //... stuff happens...
}

Note: the conversion between these CValue, CRef, CBase are very fast and CRef and CBase are lightweight objects, so it is unlikely that you will have a big performance problem in normal circumstances. However within a tight loop or other performance critical bit of code it could be good to use explicit types instead of CValue.


This page was last modified 18:04, 12 Jul 2009.
This page has been accessed 40103 times.

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