Sign in to follow this  
_orm_

True Variant Datatype

Recommended Posts

I mentioned in another thread that I was working on a Variant datatype and I think I am on to something. At the moment, it is extremely buggy and to be honest, I am a little embarassed by my ham-handed implementation. It is based off of a bit-stream class I found on code-project a while ago, a link to which I do not have at the moment. It has worked fairly well. The bit stream allows me to represent both primitive datatypes and strings , and then given whatever it is based off of, can run the proper conversions. Of course, any variant type will cause a hit to performance, but I think that a true variant type is a worthwile thing to have in any scripting language, as there are a multitude of uses for them.

Please bear in mind that this currently has a few things in it that are specific to my game engine, but they are mostly utility functions.

What currently works:

[list][*]String concantenation when both vars represent strings.[*]Arithmetic operations between variants regardless whether they represent a string or a primitive.[*]All assignment operators.[/list]What does not work:

[list][*]Arithmetic assignment operators[*]The string constructor (for who knows what reason).[/list]Some sample code from my test-case in my game engine.

[code]void ConsCmd_VarTest( Console@ c, const string@[]@ args )
{
if( args.length() < 2 )
{
c.Print( "Not enough arguments. Need at leasst two numbers. Note that they can have decimals in them.");
return;
}

c.Print("Testing the 'var' datatype.");

var arg1 = args[0],
arg2 = args[1];
c.Print(arg1.asString()+" + "+arg2.asString()+" = "+var(arg1+arg2).asString());
c.Print(arg1.asString()+" - "+arg2.asString()+" = "+var(arg1-arg2).asString());
c.Print(arg1.asString()+" * "+arg2.asString()+" = "+var(arg1*arg2).asString());
c.Print(arg1.asString()+" / "+arg2.asString()+" = "+var(arg1/arg2).asString());

var arg3 = arg1;

var testconstructor( 10 );
arg3 += var("10");
c.Print(arg1.asString()+" += 10 -> "+arg3.asString() );
}[/code]

Share this post


Link to post
Share on other sites
I don't think variants are useful at all. I prefer my scripting languages to be implicitly and strongly typed. Instead of doing all the work to create a variant type that could contain anything at any given moment, just have your scripting language create the proper variable type the first time it's assigned a value. To make things easier, you could implicitly convert values of one type to another when using them in expressions, but, to be honest, I find this behavior to be a good way to introduce subtle bugs.

[code]

// Creation of a numeric variable
var a = 10;

// Creation of a string variable
var b = "20";

// This can't work because the compiler can't determine what its datatype should be.
var c;

// You could implicitly use type conversion here to convert b into a numeric for the arithmetic
// operation, but how does the compiler actually know that this expression is intended
// to be an arithmetic expression? If your language uses the plus sign for string
// concatenation, then it's just as feasible that the script author wanted the string "1020" back
// instead of the numeric value 30. This is why implicit type conversion can be problematic
// and I recommend against it.
var c = a + b;

[/code]

Share this post


Link to post
Share on other sites
That is actually a good point on implicit conversion. Perhaps I should just return an invalid value in those cases? The thing is though that concantenation will only occur when both vars represent a string. Then again, there really is no real usage for adding a string representing a double to a double or an int.

Share this post


Link to post
Share on other sites
Hidden
Here's a situation where you might wind up doing arithmetic on a string and a numeric value by accident:
[code]

var moneyInMyBankAccount = 1000;

// Variants are tricksie bastards
var howMany = PromptForUserInput("Please enter how many foos you'd like to sell.");
var costPerFoo = 10.5;

var total = costPerFoo * howMany;

moneyInMyBankAccount += total;

// moneyInMyBankAccount now = "100021"! Yea!
[/code]

Share this post


Link to post
[quote name='_orm_' timestamp='1317307783' post='4867213']
That is actually a good point on implicit conversion. Perhaps I should just return an invalid value in those cases? The thing is though that concantenation will only occur when both vars represent a string. Then again, there really is no real usage for adding a string representing a double to a double or an int.
[/quote]

Errors are better. An invalid value can go unnoticed and bubble up in your code where it becomes difficult to determine what the source of the invalid data is. It's much harder to ignore an error.

Share this post


Link to post
Share on other sites
Really though, variants are more trouble than they're worth. It is almost always a bug when you change a variable's type. There really is no good reason that a variable should be a string value on line 10, but then the same variable is used to hold a numeric value on line 20. A strongly typed system with inferred typing based on first assignment gains you all the convenience of variants with none of the pitfalls of being able to change a variable's type at any time. Those bugs are so hard to track down that the programmer eventually starts explicitly converting using "asXYZType()" every time they access the variant, which completely undermines the entire reason why variants were created in the first place. It makes your code look ugly, potentially slower, and forces you to acknowledge its type everywhere you use it. You'll start using the dreaded Hungarian notation when naming variables just so you can remember what type they were supposed to be. Then anyone who ever looks at your code later will hate you and possibly contemplate removing you from the gene pool to prevent contamination of the future of the human race.

Share this post


Link to post
Share on other sites
However, I have found variants to be extremely useful when it comes to object factories. In my current setup, I have factories that take arrays of variants and then use them as paramaters for constructing game objects.

However, I guess a better way to do this would be to define a constructor interface that gets passed to a concrete factory and provides construction instructions.

[code]

class Factory
{
Body@ Construct(IFactoryConstructor@ cons)
{
return cons.Construct();
}
};

interface IFactoryConstructor
{
Body@ Construct();
};



class Brute_Constructor : IFactoryConstructor
{
Brute_Constructor( int strength, string@ resource )
{
this.strength = strength;
@this.resource = resource;
}
Body@ Construct()
{
return Body_Brute( strength, resource );
}
private int strength;
private string@ resource;
};[/code]


I get the feeling that there is an infinitely better way to do this.

Share this post


Link to post
Share on other sites
I feel that variants are useful for generic containers where you don't know exactly what will be put in them at the design time, e.g. the session object in web development, or data base access objects, or even generic properties in a game engine.

I would have to agree that variants as a variable type is not really that useful though, for the reasons given by smr. Still, I would very much like to be able to allow the application developer to properly register their own variant types, just as _orm_ has done.

I'll have a closer look at your implementation _orm_. Anything in particular that you feel is missing in the AngelScript library to let you accomplish what you want?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this