Squirrel features both Delegates and Classes for creating types and providing default values and actions. They are mutually exclusive, meaning you either use a class or a delegate (attached to a table/array).

The following is a collection of notes on Classes.

Sample Class

The class below features a constructor, metamethods (like operator overloading), and a few additional functions.

class vec2 {
	x = 0.0;
	y = 0.0;

	// with "..." as an argument, you get an array named vargv. //
	constructor (...) {
		if ( vargv.len() >= 2 ) {
			x = vargv[0];
			y = vargv[1];
		}
	}
	
	// + and += operator //
	function _add(vs) {
		return ::vec2( x+vs.x, y+vs.y );
	}

	// _sub, _mul, _div go here //
	
	// negative operator //
	function _unm() {
		return ::vec2( -x, -y );
	}
	
	// name to return with typeof //
	function _typeof() {
		return "vec2";
	}

	// What gets printed when we print this. //
	function _tostring() {
		return "(" + x + "," + y + ")";
	}
	
	// a potentially useful function, at least for this class. //
	function toarray() {
		return [x,y];
	}

	function dot(vs) {
		return (x*vs.x) + (y*vs.y);
	}
		
	function tangent() {
		return ::vec2( y, -x );
	}
}

Create instances with function syntax

Creating an instance is a lot like JavaScript.

local MyVar = vec2(10,10);

The constructor is called with mentioned arguments.

Not to be confused with the _call metamethod.

function _call( original_this, ... ) {
    print("I have soooo many arguments. A whole " + vargv.len() );
}

// ... //

local MyInstance = MyClass();   // Constructor //
MyInstance();                   // _call metamethod //

Problems to consider: Functions are not overloaded, but replaced

In C++ and GLSL/HLSL, it’s common to have operators overloaded that let you Add, Subtract, and Multiply vectors by other vectors, scalars and matrices. This can (mostly) be done in squirrel by looking at the type of the argument received by your metamethods. Here’s an example that supports scalar multiplication as well as vector multiplication (component wise).

function _mul(vs) {
		if ( typeof vs == "integer" )
			vs = vs.tofloat();
		if ( typeof vs == "float" )
			return ::vec3( x*vs, y*vs, z*vs );
		return ::vec3( x*vs.x, y*vs.y, z*vs.z );
	}

The problem though is this is one way. I can do “MyVec * 10”, but “10 * MyVec” will not work. To make it work, you would have to add a custom _mul metamethod to the number type… except I’m not sure that’s allowed by Squirrel (custom delegates are, but metamethods?).

One solution would be to create a “real” type (similar to GEL), but Squirrel doesn’t exactly provide a nice way to create custom automatic type conversions, so the syntax wouldn’t be ideal (i.e. real(10) * vec2(10,20) everywhere). I do live with this in GEL, but it would be a shame to not have a cleaner syntax here. This also assumes custom math metamethods are not supported by Squirrel (and technically I have the source, so it’s not like I couldn’t add any feature I wanted).

It’s also a shame the operator code ends up being so complex. A function that started as one line (the last line) has become 5 lines to add float and integer support. Add in Matrix multiplication too, and then I start getting scared.

An alternative solution would be to add .tovec3() and .toscalar3() functions to the default float delegate (where tovec3() is (10,0,0) and toscalar3 is (10,10,10)). This would be a cleaner option, but is somewhat wasteful, especially once we get to higher order maths like Matrix multiplication.

One more option would be to add language level vector and matrix support. After all, a table with x,y,z slots can’t exactly be the most efficient thing. This would certainly be the most difficult though.

It’s a shame Squirrel doesn’t have a way of adding shadow members. Like a MyObject.x() without the brackets that could be assigned (MyObject.x = 10).

I shouldn’t be expecting a scripting language to be ideal for high performance math anyway. Like I’ve said, specialization is the key. That is what scripting is for. Leave the low-level to the low-level.

Copy Constructors: meh

This is inconclusive currently. I may have found a bug with what should be the ideal way of doing it. I’ve since posted a question to the forum on it.

What I would like to do:

// WARNING! THIS DOES NOT ACTUALLY WORK! FOR REFERENCE ONLY! //

class vec2 {
    x = 0.0;
    y = 0.0;
    
    constructor (...) {
        if ( vargv.len() >= 2 ) {
            x = vargv[0];
            y = vargv[1];
        }
        else if ( vargv.len() >= 1 ) {
            if ( typeof vargv[0] == "vec2" ) {
                this = clone vargv[0];
            }
        }
    }

    function _typeof() {
        return "vec2";
    }
};

// WARNING! THIS DOES NOT ACTUALLY WORK! FOR REFERENCE ONLY! //

What I have to do instead.

class vec2 {
    x = 0.0;
    y = 0.0;
    
    constructor (...) {
        if ( vargv.len() >= 2 ) {
            x = vargv[0];
            y = vargv[1];
        }
        else if ( vargv.len() >= 1 ) {
            if ( typeof vargv[0] == "vec2" ) {
                x = vargv[0].x;
                y = vargv[0].y;
            }
        }
    }

    function _typeof() {
        return "vec2";
    }
};

This works, but isn’t as elegant as cloning.

Weak References

All built-in types have a .weakref() function in their delegate (classes, instances, generators, tables, etc). Bools, floats and integers return the actual type, but everything else returns a weakref type.

The weakref type is exactly that, a type containing a reference. To access the data referenced, you can call the .ref() function.

function GainFocus() {
	local MyVec = vec3(10,25,5);
	local MyWeakRef = MyVec.weakref();  // create a weak reference //
	Log( MyWeakRef.ref() );             // vec3.tostring() //
}

If the data referenced runs out of references, it is recycled. Any weakrefs pointing to it will thereafter return null.

In addition, Squirrel does some cleverness with weakref types. If you omit the .ref() call, it will still return the data referenced. For the most part, a weakref will act like the real type, except typeof will be weakref instead of the original type. To get the type referenced, you do typeof on the value returned by .ref().

function GainFocus() {
	local MyVec = vec3(10,25,5);
	local MyWeakRef = MyVec.weakref();  // create a weak reference //
	Log( MyWeakRef );                   // vec3.tostring() //
	Log( typeof MyWeakRef );            // weakref //
	Log( MyWeakRef.ref() );             // vec3.tostring() //
	Log( typeof MyWeakRef.ref() );      // vec3 //
	MyVec = "No Longer a vec3";
	Log( MyWeakRef );                   // null.tostring() //
	Log( typeof MyWeakRef );            // weakref //
	Log( MyWeakRef.ref() );             // null.tostring() //
	Log( typeof MyWeakRef.ref() );      // null //
}

Strong References

Okay, the reason for the brief exploration of weak references was because of an experiment I was doing with strong references.

function Step() {
	local Pos = vec3(10,25,5);
	local Old = Pos;              // strong reference //
	Log( Old );                   // (10, 25, 5) -- Correct, but... //
}

Contrary to C++, instead of an assignment operator being called, a strong reference is created. Thus both Pos and Old point to the same data. In this way, Squirrel works like Java/JavaScript/C#, in that everything (other than int/float/bool) are references.

function Step() {
	local Pos = vec3(10,25,5);
	local Old = Pos;            // STRONG REFERENCE! WHOA! //
	Old.x += 0.5;
	Pos.y += 0.25;
	Log( Pos );                 // (10.5, 25.25, 5) -- Yuck! //
}

Instead, one should use clone to make a copy.

function Step() {
	local Pos = vec3(10,25,5);
	local Old = clone Pos;      // Copy //
	Old.x += 0.5;
	Pos.y += 0.25;
	Log( Pos );                 // (10, 25.25, 5) -- That's Better! //
}

Clone performs a shallow copy, meaning all top-level members are copied. Integers, Floats, and Bools work as expected, but most other types are references, which may not be the behavior you desire.

Cloning Classes

In the case of a class, you can write a simple _cloned metamethod to handle the above case..

class cObject {
	Id = 0;
	Pos = vec3(0,0,0);
	Old = vec3(0,0,0);
	
	function _cloned(orig) {
		// NOTE: Id not required, as it will be correctly copied during the shallow copy
		Pos = clone orig.Pos;
		Old = clone orig.Old;
	}
}

If a table, you can use a delegate like the following to automatically clone all members.

CloneChildren <- {
	function _cloned( original ) {
		foreach ( idx, val in original ) {
			local Type = typeof val;	// Squirrel raises errors on cloning these types
			if ( (Type != "integer") && (Type != "bool") && (Type != "float") ) {
				this[idx] = clone val;
			}
		}
	}
};

MyTable.setdelegate( CloneChildren );

No General Purpose Copier for Classes

The general purpose copier doesn’t seem to work very well with classes. At least when I was trying to do it, I couldn’t get it working properly. Here’s a snapshot.

// WARNING! THIS DOES NOT ACTUALLY WORK! DEMONSTRATION ONLY! //

// Generalized the clone function to be used in both classes and tables
clonemychildren <- function( original ) {
	foreach ( idx, val in original ) {
		local Type = typeof val;
		if ( (Type != "integer") && (Type != "bool") && (Type != "float") ) {
			this[idx] = clone val;
		}
	}
};

// The Table version. Do a .setdelegate( clonechildren ) on your Table. Works fine.
clonechildren <- {
	function _cloned( original ) {
		return clonemychildren.call(this,original);
	}
};

class MyClass {
	Pos = vec3(0,0,0);
	Old = vec3(0,0,0);

	// How this should have worked... //	
	function _cloned(original) {
		clonemychildren.call(this,original);
	}
	
	// However doing a foreach on a class requires one of these... //
	function _nexti(prev) {
		if ( prev == null )
			return 0;
		else
			return prev + 1;
	}
	
	// Which needs one of these... //
	function _get(idx) {
		if ( idx == 0 ) {
			return Pos;
		}
		else if ( idx == 1 ) {
			return Old;
		}
		throw null;
	}
	
	// To be honest, this code isn't working so I don't know if I need this. //
	function len() {
		return 2;
	}
};

// WARNING! THIS DOES NOT ACTUALLY WORK! DEMONSTRATION ONLY! //

_get metamethod: are you even useful?

I have this code in my 3D vector class.

function _get(idx) {
		if ( idx == 0 )
			return x;
		else if ( idx == 1 )
			return y;
		else if ( idx == 2 )
			return z;
		throw null;
	}

It’ll let you syntactically use the class like an array, but it’s not an array. So this is fine for inside Squirrel code, but if you have native code expecting an array, these will be useless.