User Tools

Site Tools


proton:variants

Why variants?

A problem with boost and all this signal/slot stuff is if you want to send variables you have to know in advance what variables should be passed.

We get around that by using Variant and VariantList.

Variant

Variant is a special kind of variable that can be a number, a string, a vector3, or a pointer to an entity or component. (It's pretty easy to add support for more data types btw)

Example:

Variant v; //so far it doesn't know what it is
LogMsg("V is %s", v.Print().c_str()); //output: V is Unknown
v.Set(uint32(10));
LogMsg("V is %s.  Or, in number form: %d", v.Print().c_str(), v.GetUINT32()); //output: V is 10.  Or, in number form: 10
 
//what if we try to access it wrong?
float f = v.GetFloat(); //it will assert and tell you it's a uint32, not a float

So if you see a function that expects a Variant type, you can call it like this:

SomeFunc(Variant("Hello! Hope you wanted a string!"));
//or
SomeFunc(1.0f);

If the compiler isn't sure if it's supposed to be an int or a float, it will give you an “ambiguous call to overloaded function” error.

In that case, you can help it out by doing uint32(0) or 0.0f to help the compiler out.

VariantList

In the world of dynamic signals and slots we also don't know how many variables are going to be sent in advance. In that case, instead of passing a variable, we will pass a VariantList, which contains more than one.

Some code to see how they work:

//instantiate a VariantList with four things in it
VariantList vList(1.0f, 2.0f, string("Hello!"), uint32(42));
 
//retrieve them by index if we know what is in them:
LogMsg("The first value is %s, of type %d.", vList.Get(0).Print().c_str(), vList.Get(0).GetType());
 
//or you could remove variants from them like this
Variant v = vList.Get(3); //index 3 is the fourth one, the 42 from above
assert(42 == v.GetUINT32())
 
//Let's clear it and set it one by one
vList.Reset();
vList.Get(0).Set("Index 0");
vList.Get(1).Set("Index 1, and so on");

A function asking for a VariantList (or more like, a pointer to one) can be called like this:

//Let's send a string and a CL_Vector2
DrawText(&VariantList(string("Some text to draw"), CL_Vector2(20,50)));

Caveat: Currently it will assert if you try to use more than 6 variables in a single VariantList. For speed, I don't dynamically allocate them as needed. It's a simple define to raise that limit though.

VariantDB

A VariantDB is exactly what it sounds like. A key/data database of variants!

Use a VariantDB anywhere you want to store a bunch of key/data pairs, aka, named variables.

Example:

VariantDB db;
 
db.GetVar("gigawatts")->Set(1.21f); 
db.GetVar("trivia")->Set("Doc pronounced giggawatt wrong in the movie");
 
//retrieve the data like this:
LogMsg("Gigawatts: %.2f, trivia: %s", db.GetVar("gigawatts")->GetFloat(), db.GetVar("trivia")->GetString().c_str());
 
//output:  Gigawatts: 1.21, trivia: Doc pronounced giggawatt wrong in the movie	

You can also save and load them, making them perfect for keeping track of game settings.

db.Save(GetSavePath()+"game.dat");
 
//load her back in the same way
bool bFileExisted;
db.Load(GetSavePath()+"game.dat", &bFileExisted);
db.Print(); //print all database content out, good for debugging
 
//another trick is to load a setting but also specify the default
bool bSoundEnabled = db.GetVarWithDefault("sound", uint32(1))!= 0;
 
bSoundEnabled will get the value and set it to true if the key doesn't already exist.

There is no limit to how many key/data pairs you can have other than memory.

VariantDB's secret power: Function objects

Here is where it might get weird for you.

In addition to setting and retrieving variant data by name, you can create and retrieve “function objects”.

A function object (why do I call it this? confusing) is basically a named signal. (You read the earlier part about slots and signals, right?)

In this way, you can sort of create labels that can be triggered easily, and these triggers can activate real functions that have been attached to them.

See, there is no way to explain that in a way that makes sense so I'll just show you a contrived example.

//First, our function we're going to call with a function object in a second..
void SaySomething(VariantList *pVlist)
{
  LogMsg( ("I will say:" + pVList->Get(0).GetString()).c_str() );
}
 
//...
 
VariantDB db;
//by getting the function object "Say", it will create it for us. We then connect its signal to the real function.
db.GetFunction("Say")->sig_function.connect(&SaySomething);
 
//now, let's trigger it so the function will really get called
db.GetFunction("Say")->sig_function(&VariantList(string("Hello, worlds")));
 
//output: I will say:Hello, worlds

Ok, so you see how we can link a real function to a function object, then call it, without knowing what the real function is, or that it is linked. Or how many real functions are linked to that signal, as it could be more than one.

You're probably thinking how worthless that is, because you could have just saved some code and called SaySomething() directly, and why would a variant database even care about function objects?

Because, good sir… <dramatic pause> …every entity and component has its own VariantDB!

Once you get to the entity and component section this will all come together, I swear.

proton/variants.txt · Last modified: 2012/07/24 09:43 by aki