Do prvního prvku pole ukládáme index proměnné, do druhého prvku hodnotu. To znamená: strSetData1 má index 0 a hodntotu 7 strSetData2 má index 1 a hodntotu 7 strSetData3 má index 2 a hodntotu 7
Pak nastavíme indexy proměnných, ke kterým budeme přičítat, nebo odčítat jedničku
char cIncData = 1;
char cDecData = 2;
cIncData přičte jedničku k hodnotě proměnné indexu 1 takže výsledek je 7+1=8 do konzole se vypíše (1: 8) cDecData odečte jedničku od hodnoty proměnné indexu 2 takže výsledek je 7-1=6 do konzole se vypíše (2: 6)
Do tříprvkového pole char nastavíme další proměnnou
char strAddData[] = {3, 0, 2}; // Add 1st and 3rd var, and store in 4th
strAddData má index 3 a bude přičítat hodnoty proměnné indexu 0 a 2
takže výsledek je 7+6=13 do konzole se vypíše (3: 13)
Práci s proměnnými definujeme v enumerátoru
/// Enumerations describing the bytecode instructions the VM is able to process
enum EScriptOpCode
{
K_OP_TALK,
K_OP_PRINT,
/// Variable manipulators
K_OP_SET, ///< char, char : destination index, value to set
K_OP_INC, ///< char : index to increment
K_OP_DEC, ///< char : index to decrement
K_OP_ADD, ///< char, char, char : dest index, srce index1, srce index2
K_OP_END
};
Při načítání scény
Nastavíme velikost proměnných.
// Proper instruction data size constants (temporary for safety)
const int SET_SIZE = 2*sizeof(char);
const int INC_SIZE = sizeof(char);
const int DEC_SIZE = sizeof(char);
const int ADD_SIZE = 3*sizeof(char);
Vytvoříme pole instrukcí
vector<CInstruction> vVarInstr;
vVarInstr.push_back(CInstruction(K_OP_SET, strSetData1, SET_SIZE)); // Set first 3 vars to 7
vVarInstr.push_back(CInstruction(K_OP_SET, strSetData2, SET_SIZE));
vVarInstr.push_back(CInstruction(K_OP_SET, strSetData3, SET_SIZE));
vVarInstr.push_back(CInstruction(K_OP_INC, &cIncData, INC_SIZE)); // Inc 2nd var
vVarInstr.push_back(CInstruction(K_OP_DEC, &cDecData, DEC_SIZE)); // Dec 3rd var
vVarInstr.push_back(CInstruction(K_OP_ADD, strAddData, ADD_SIZE));
vVarInstr.push_back(CInstruction(K_OP_END)); // Then end
Vytvoříme skript o čtyřech proměnných
CScript kVarScript(vVarInstr, 4); // We need 4 variables
///\brief Add script to list and retrieve id
///\param _rScript Reference to script object
size_t AddScript(const CScript& _rScript)
{
m_vScript.push_back(_rScript);
return m_uiNumScripts++;
}
Celá VM třída vypadá takto:
class CVirtualMachine
{
/// Useful abstractions
/// pointers used as non-modifying dynamic references
typedef const CScript* TScriptRef;
typedef const CInstruction* TInstrRef;
CScriptState m_kState; ///< Script state. Since we're single-threaded, only need one
std::vector<CScript> m_vScript; ///< Script array
TScriptRef m_tScript; ///< Current script
TInstrRef m_tInstrRoot; ///< Root instruction
TInstrRef m_tInstrCurr; ///< Current instruction
size_t m_uiNumScripts; ///< Number of loaded scripts
///\brief Add script to list and retrieve id
///\param _rScript Reference to script object
size_t AddScript(const CScript& _rScript)
{
m_vScript.push_back(_rScript);
return m_uiNumScripts++;
}
///\brief Set current script by id
///\param _uiScriptId Script index
void SelectScript(size_t _uiScriptId)
{
assert(_uiScriptId < m_uiNumScripts); // make sure the id is valid
m_tScript = &m_vScript[_uiScriptId];
m_tInstrRoot = m_tScript->GetInstruction();
}
void ExposeVariableState(const CScriptState& _rState) const;
public:
CVirtualMachine()
: m_tScript(0), m_tInstrRoot(0), m_tInstrCurr(0), m_uiNumScripts(0){}
~CVirtualMachine(){}
void ExecuteScript(size_t _uiScriptId);
///\brief Load script
///\param _rkScript Reference to script object
size_t LoadScript(const CScript& _rkScript)
{
return AddScript(_rkScript);
}
///\brief Show variable state (debugging)
void ShowVariableState() const
{
ExposeVariableState(m_kState);
}
};
Při běhu aplikace
Spustíme načtený skript
kVM.ExecuteScript(uiVarManipId);
Funkce vypadá takto:
///\brief Execute VM
///\param _uiScriptId Script index
void CVirtualMachine::ExecuteScript(size_t _uiScriptId)
{
// Select root script by script index
SelectScript(_uiScriptId);
// Initialize variable data
m_kState.SetDataSize(m_tScript->GetNumVars());
// Set our iterator to the beginning
m_tInstrCurr = m_tInstrRoot;
while (m_tInstrCurr)
{
switch(m_tInstrCurr->GetCode())
{
// Message functionality
case K_OP_TALK:
std::cout << "I am talking." << std::endl;
// Iterate
++m_tInstrCurr;
break;
case K_OP_PRINT:
std::cout << m_tInstrCurr->GetData() << std::endl; // print data
++m_tInstrCurr; // iterate
break;
// new semi-math functionality
case K_OP_SET:
assert(m_tInstrCurr->GetData()[0] < m_tScript->GetNumVars());
m_kState.SetVar(m_tInstrCurr->GetData()[0], m_tInstrCurr->GetData()[1]);
++m_tInstrCurr;
break;
case K_OP_INC:
assert(m_tInstrCurr->GetData()[0] < m_tScript->GetNumVars());
m_kState.SetVar(m_tInstrCurr->GetData()[0], m_kState.GetVar(m_tInstrCurr->GetData()[0])+1);
++m_tInstrCurr;
break;
case K_OP_DEC:
assert(m_tInstrCurr->GetData()[0] < m_tScript->GetNumVars());
m_kState.SetVar(m_tInstrCurr->GetData()[0], m_kState.GetVar(m_tInstrCurr->GetData()[0])-1);
++m_tInstrCurr;
break;
case K_OP_ADD:
assert(m_tInstrCurr->GetData()[0] < m_tScript->GetNumVars());
assert(m_tInstrCurr->GetData()[1] < m_tScript->GetNumVars());
assert(m_tInstrCurr->GetData()[2] < m_tScript->GetNumVars());
m_kState.SetVar(m_tInstrCurr->GetData()[0],
m_kState.GetVar(m_tInstrCurr->GetData()[1])
+ m_kState.GetVar(m_tInstrCurr->GetData()[2]));
++m_tInstrCurr;
case K_OP_END:
// Discontinue the loop
m_tInstrCurr = 0;
break;
}
}
}
Volá funkci
///\brief Set current script by id
///\param _uiScriptId Script index
void SelectScript(size_t _uiScriptId)
{
assert(_uiScriptId < m_uiNumScripts); // make sure the id is valid
m_tScript = &m_vScript[_uiScriptId];
m_tInstrRoot = m_tScript->GetInstruction();
}
Která nastaví skript podle indexu _uiScriptId
Pak nastaví pole pro všechny proměnné
m_kState.SetDataSize(m_tScript->GetNumVars());
Nová třída CScriptState
která do pole
std::vector<char> m_vVarData; ///< Char variable data array
ukládá všechny proměnné, vypadá takto:
class CScriptState
{
std::vector<char> m_vVarData; ///< Char variable data array
public:
///\brief Set data size
///\param tSize Data size
void SetDataSize(size_t _tSize)
{
m_vVarData.resize(_tSize);
}
///\brief Set variable
///\param _tSize Variable size
///\param _cVal Char variable
void SetVar(size_t _tSize, char _cVal)
{
assert(_tSize < m_vVarData.size());
m_vVarData[_tSize] = _cVal;
}
///\brief Get variable
///\param _tSize Variable size
///\retval m_vVarData[_tSize] Char variable
char GetVar(size_t _tSize) const
{
assert(_tSize < m_vVarData.size());
return m_vVarData[_tSize];
}
///\brief Get variable data array
///\retval m_vVarData Char variable data array
const std::vector<char> & GetDataArray() const
{
return m_vVarData;
}
};
Operace s proměnnými provádí virtual machine voláním těchto funkcí
// new semi-math functionality
case K_OP_SET:
assert(m_tInstrCurr->GetData()[0] < m_tScript->GetNumVars());
m_kState.SetVar(m_tInstrCurr->GetData()[0], m_tInstrCurr->GetData()[1]);
++m_tInstrCurr;
break;
case K_OP_INC:
assert(m_tInstrCurr->GetData()[0] < m_tScript->GetNumVars());
m_kState.SetVar(m_tInstrCurr->GetData()[0], m_kState.GetVar(m_tInstrCurr->GetData()[0])+1);
++m_tInstrCurr;
break;
case K_OP_DEC:
assert(m_tInstrCurr->GetData()[0] < m_tScript->GetNumVars());
m_kState.SetVar(m_tInstrCurr->GetData()[0], m_kState.GetVar(m_tInstrCurr->GetData()[0])-1);
++m_tInstrCurr;
break;
case K_OP_ADD:
assert(m_tInstrCurr->GetData()[0] < m_tScript->GetNumVars());
assert(m_tInstrCurr->GetData()[1] < m_tScript->GetNumVars());
assert(m_tInstrCurr->GetData()[2] < m_tScript->GetNumVars());
m_kState.SetVar(m_tInstrCurr->GetData()[0],
m_kState.GetVar(m_tInstrCurr->GetData()[1])
+ m_kState.GetVar(m_tInstrCurr->GetData()[2]));
++m_tInstrCurr;
Výsledky vykreslíme do konzole voláním funkce
///\brief Expose variable state
///\param _rState Reference to state
void CVirtualMachine::ExposeVariableState(const CScriptState& _rState) const
{
std::vector<char>::const_iterator itr;
int n = 0; // Used to denote indexed position of value
for (itr = _rState.GetDataArray().begin(); itr != _rState.GetDataArray().end(); ++itr, ++n)
{
std::cout << n << ": ";
std::cout << static_cast<int>(*itr); // Cast for numeric value
std::cout << std::endl;
}
}