Jump to content
  • Advertisement
Sign in to follow this  
jake_Ghost

Unknown variable type problems

This topic is 3267 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

[Background info] C++ Visual studio 2008 SP1 I'm having a little problem figuring out how to program my scripting engine. I have most of it planned out but the one thing that I just can't figure out is how to program scripted variables. Currently I am using boost::variant and a class to handle the loading and allocating but that leaves me with the problem of an unknown variable type at runtime. The boost::get function requires me to either know the variable type or go through some kind of switch statement. Boost visitors don't help much either since I don't know the type at compile time. It'd be nice to have the VB's variant type in c++, wouldn't it? Any help would be great Thanks, Jake

Share this post


Link to post
Share on other sites
Advertisement
Can you be more specific with where you encounter the problem? Any possibility of posting the applicable code?

There are a few different approaches to handling the concept of variants, but we'd really need more detail on your situation to know what would work best.

Share this post


Link to post
Share on other sites
Of couse, sorry about that.

Here is my variable class. My original goal was to have the Get() method return whatever type the variable is, eg. variable_type Get(). But of course that doesn't really work at all.


#pragma once

#include <windows.h> // Header File For Windows
#include <math.h> // Math Library Header File
#include <stdio.h> // Header File For Standard Input/Output
#include <mmsystem.h>
#include <fstream>
#include <sstream>
#include <cctype>
#include <iomanip>
#include <cmath>
#include <iostream>
#include <string>
#include <vector>
#include <iostream>
#include <boost/variant.hpp>
#include "GhostVars.h"

using namespace std;

typedef boost::variant<int, float, bool, string> Variant;

enum VariableType
{
Variable_Int = 0,
Variable_Float,
Variable_Bool,
Variable_String
};

class cVariable
{
public:
cVariable()
{
Name = "";
}

~cVariable()
{

}

public:
bool New(string Text, string Value = "");

__inline string GetName(string *name = NULL) { if (name) *name = Name; return Name; }
__inline Variant &Get() { return Var; }
__inline int GetType() { return Type; }

private:
Variant Var;
Variant OValue;

string Name;

int Type;
};





So the syntax for using this class would be like this...


cVariable V;

// "float i = 1.0" would be a line in a file
V.New("float i = 1.0");

float a = boost::get<float>(V.Get());





Currently, the way of retrieving the data requires me to know the variable type and to handle different types I'd need a switch statement, if, ect.

When passing one of these variables to a function, this becomes realllly difficult when that function has 2 or more types of variables.

I don't know if that can be done with boost::variant or not. All that matters to me is that I can end up with something like...


cVariable V;

V.New("float x = 1.0");

SomeFunc(V.Get())



Share this post


Link to post
Share on other sites
As far as I'm aware, you have to break down and basically implement a switch somewhere; the very fact that you're parsing script text means that you can't know the needed types at compile time. Since C++ is statically typed, it won't let you keep a type undefined until runtime, meaning that somewhere along the line you need to hardcode a mapping that converts some kind of "type ID number" (generally an enum suffices for this) into instances of the correct variable class. Some template magic can help ease the pain, but you'll have to bite the bullet eventually.

You can see my own personally preferred solution in the Epoch language project (download Release 7, open the Fugue solution in the Source folder, select the FugueDLL project, and look in Virtual Machine/Variables/Variable.h and Variable.cpp). (Actually, just use this link to browse around in the relevant code directly on the web.) It's a little bit cumbersome but very flexible and has served me well so far.

[Edited by - ApochPiQ on December 4, 2009 10:48:36 PM]

Share this post


Link to post
Share on other sites
That is quite the project!

Thanks for the link :D. I think I know what I'm going to do now. This damn game keeps getting bigger and bigger lol. Maybe one day... I'll finish it.

Jake

Share this post


Link to post
Share on other sites
I think, with boost::variant, you could easily end up with all the functions being visitors:


cVariable V;

V.New("float x = 1.0");

apply_visitor(SomeFunc(), V.Get())




Where SomeFunc might be something like:


struct SomeFunc: public boost::static_visitor<void>
{
void operator()(float f) const
{
//do something
}


template <class T>
void operator()(const T& t) const
{
//something generic for other types
}
};



I'm afraid, using variant types just can't be too easy.

Share this post


Link to post
Share on other sites
In your example, how does the compiler know the return type of V.Get()? If the return type is already correct (e.g. float), then there is no need for a visitor, as you can just do float foo = V.Get(); and skip the overhead of the template code generation.

Share this post


Link to post
Share on other sites
Quote:
Original post by visitor
I think, with boost::variant, you could easily end up with all the functions being visitors:

*** Source Snippet Removed ***

Where SomeFunc might be something like:

*** Source Snippet Removed ***

I'm afraid, using variant types just can't be too easy.


No they definatly are not. But how would you go about making visitors for member functions like this?


void SetEmitterEx(const float x, const float y, const int size, const float velocity, const float angle, const float angleLow, const float angleHigh, const int direction, const float rlow, const float rhigh, const float life, float xoffset = 0);



Quote:

In your example, how does the compiler know the return type of V.Get()? If the return type is already correct (e.g. float), then there is no need for a visitor, as you can just do float foo = V.Get(); and skip the overhead of the template code generation.


The Get() function returns a boost::variant type.

Share this post


Link to post
Share on other sites
Quote:
Original post by jake_Ghost
The Get() function returns a boost::variant type.



Ah right, of course [smile]

Either way, it doesn't really help much - if you want to have dynamic, run-time-determined types for your variants, you'll have to use some run-time logic to select the correct compile-time code for each type, i.e. the moral equivalent of a switch. Since C++ has no reflection capabilities, there's not much else you can do.

Share this post


Link to post
Share on other sites
So I have again run into a pickle.

How can I use a switch statement to execute something as simple as this even


//Code in Test.txt
void Test()
{
// assuming b, c have values
float a, b;
int c;

a = b + c;
}





Now to run this in c++ assuming I know each variable type it would look like this


//assuming a b c are now variant types
switch (OperatorType)
{
case PLUS:
a = boost::get<float>(b.Get()) + boost::get<int>(c.Get());
break;
}





Since I need my script engine to "mostly" run functions (I am making a space shooter) I don't think this stuff will need to be tooooooo extensive but it would be nice to do a = b + c lolllll. One way to make this simpler would be to assume that the variable types are all the same as "variable a" so I could use some templates. Of course, this makes it very limiting and prevents me from doing things like string str = "Hello player " + PlayerNum + "!"

One other way that I was think would be to do something like this,


// example of variables and functions in class cOperator
class Test
{
public:
Test()
{
a = 0.0f;
b = 1.5f;
c = 1;
}

Variant a, b, c;

template<typename T0, typename T1, typename T2>
void Plus(Variant &Dest, Variant &A, Variant &B)
{
Dest = (T0)boost::get<T1>(A) + (T0)boost::get<T2>(B);
}
};

// to keep things simple ill assume that only int/float are available
enum
{
INT_ONLY,
FLOAT_ONLY,
INT_FLOAT,
FLOAT_INT
}

// cOperator
Execute()
{
// stuff here determining what the scripted code is doing
// now lets use a = b + c again
switch (operator_type)
{
case PLUS:
switch (destination_type)
{
case FLOAT:
switch(variable_types)
{
case FLOAT_INT:
// actual variable names are a = Dest.Get(), b = Var[n].Get(), c = Var[n+1].Get()
Plus<float, float, int>(a, b, c);
break;
}
break;
}
break;
}
}





To me, this seems very hard to implement when i start adding strings and bools and eventually d3d vectors.

How would I go about making that simpler?

Also for the Plus function I was thinking of replacing that with a member function pointer to get rid of at least 1 switch statement. I just need to make sure i can do that with templates.

Jake

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!