Classes

Scenome Scripting Language supports simple, user-defined classes. The goal of these classes is to promote encapsulation in Scenome Scripting Language, in order to simplify the development process. For example: passing a single object to a function instead of a long list of separate function parameters. Later, you can change the data members in the class without having to add another function parameter, which means you won't have to go update all your call sites. In this way, Scenome Scripting Language classes are a more like structs in the C language.

  • class type-name { type-name variable-name; };

Details

Details are as follows:

  • Scenome Scripting Language classes must be declared at global scope. They cannot be declared inside command macros or functions.
  • Scenome Scripting Language classes must be declared before use, and the declaration must occur before first use in the translation unit.
  • Forward declaration is not supported.
  • Scenome Scripting Language classes can only have data member objects.
  • Data member functions are not supported. For example: there are no constructors, destructors, copy constructors, or similar.
  • Primitive type data members are automatically initialized to zero or empty.
  • Object data members must be declared with auto or intialized after a class object is instantiated.
  • You can initialize class objects with Scenome Scripting Language functions.
  • Declaring object data members with auto is best practice wherever possible.

Examples

Several examples are provided below.

The following example illustrates class declaration and initialization:

// Declare a new class.
class Inputs
{
   string m_sName;
   auto Float32Vector m_vPosition;
};
auto Inputs data;
data.m_sName = "Test";
data.m_vPosition.X = 0.0;
data.m_vPosition.Y = 0.0;
data.m_vPosition.Z = 0.0;

The following example illustrates passing a class object to a function.

// Declare the class.
class Inputs
{
   string m_sName;
   auto Float32Vector m_vPosition;
};
//  Implement the initialization function.
function int Initialize( Inputs p_oData )
{
   if( !( p_oData ) )
   {
      return 0;
   }

   p_oData.m_sName = "Test";
   p_oData.m_vPosition.X = 0.0;
   p_oData.m_vPosition.Y = 0.0;
   p_oData.m_vPosition.Z = 0.0;

   return 1;
}

//  Call the initialization function.
function int Create()
{
   auto Inputs data;
   return Initialize( data );
}

The following example illustrates late initialization of data members:

// Declare a new class.
class Inputs
{
   string m_sName;            // Automatically initialized to an empty string.
   int m_iCount;              // Automatically zero initialized.
   double m_dPrecision;       // Automatically zero initialized.
   auto Float32Vector m_vSrc; // Initialized to C++ constructor or data interface defaults.
   Float32Vector m_vDst;      // Initialized to nullptr.
};
auto Inputs data;
data.m_sName = "Test";
data.m_vSrc.X = 0.0;
data.m_vSrc.Y = 0.0;
data.m_vSrc.Z = 0.0;

// Late initialize...
auto Float32Vector a_vDst;
a_vDst.X = 1.0;
a_vDst.Y = 1.0;
a_vDst.Z = 1.0;

//data.m_vDst.X = 1.0; // This would generate a script nullptr error since data.m_vDst is still a nullptr...

data.m_vDst = a_vDst; // Here we initialize data.m_vDst...

data.m_vDst.X = 1.0; // Totally fine...

The following example illustrates nested classes:

// Declare a new class.
class Positions
{
   auto Float32Vector m_vSrc;
   auto Float32Vector m_vDst;
};

class Inputs
{
   string m_sName;
   Positions m_oPositions;
};
auto Inputs data;
data.m_sName = "Test";
data.m_oPositions.m_vSrc.X = 0.0;
data.m_oPositions.m_vSrc.Y = 0.0;
data.m_oPositions.m_vSrc.Z = 0.0;

Class objects can be stored in <TypeBuffer> objects. While not strictly equivalent to arrays of class objects, it's still very useful.

// Declare a new class.
class Positions
{
   auto Float32Vector m_vSrc;
   auto Float32Vector m_vDst;
};
auto Positions a;
auto Positions b;

auto TypeBuffer a_apStorage;
a_apStorage.Refers( a );
a_apStorage.Refers( b );

// Or...
Positions c = new Positions;
Positions d = new Positions;

auto TypeBuffer a_apStorage;
a_apStorage.Owns( c ); // Object c is destroyed when a_apStorage goes out of scope...
a_apStorage.Owns( d ); // Object d is destroyed when a_apStorage goes out of scope...

The following example illustrates memory management:

// Declare a new class.
class Inputs
{
   string m_sName;
   auto Float32Vector m_vSrc;
   Float32Vector m_vDst;
};
{
   auto Inputs data;
   data.m_sName = "Test";
   data.m_vSrc.X = 0.0;
   data.m_vSrc.Y = 0.0;
   data.m_vSrc.Z = 0.0;

   // Late initialize using new... Except for cases where
   // using new is absolutely required, we don't recommend
   // doing this... so for classes in Scenome Scripting Language,
   // we recommend "almost always auto".
   Float32Vector a_vDst = new Float32Vector( 1.0, 1.0, 1.0, 1.0 );
   data.m_vDst = a_vDst;

   // Since it's declared with auto, data.m_vSrc will be automatically
   // destroyed at end of scope. We don't have to do anything.

   // We must still manually delete m_vDst;
   data.m_vDst = null;
   delete a_vDst;

   // End of scope...
}