Binding C++ vector to Lua - c++

I'm trying to manually bind a vector of pointers from C++ to Lua.
I'm limited to a compiler which has partial C++11 support, so can't use one of the existing binding libraries since they all seem to use C++17 now.
For example, I have a class which contains a list of pointers to a child class. The vector of children is read only from the Lua point of view - I don't need add, remove etc. Just read.
class Child
{
public:
std::string name;
};
class Parent
{
public:
std::vector <Child *>children;
};
...
Parent parent;
Child * m = new Child;
m->name = "Mary";
parent.children.push_back(m);
Child * b = new Child;
b->name = "Bob";
parent.children.push_back(b);
...
Child binding.
static int Child_name(lua_State * lua) {
// this should get a point to a Child object and return the name
lua_pushstring(lua, "child name");
return 1;
}
static const struct luaL_Reg Child_FunctionList[] = {
{ "name", Child_name },
{ NULL, NULL }
};
static int Child_tostring(lua_State * lua) {
lua_pushstring(lua, "Child");
return 1;
}
static const struct luaL_Reg Child_MetaList[] = {
{ "__tostring", Child_tostring },
{ NULL, NULL }
};
void Child_Register(lua_State * lua)
{
luaL_newlib(lua, Child_FunctionList);
if(luaL_newmetatable(lua, "ChildMetaTable"))
luaL_setfuncs(lua, Child_MetaList, 0);
lua_setmetatable(lua, -2);
lua_pop(lua, 1);
}
Parent binding.
static int Parent_count(lua_State * lua) {
// used by both the Parent function and metatable __len
lua_pushinteger(lua, parent.children.size());
return 1;
}
static int Parent_children(lua_State * lua)
{
// stack -1=number(1)
int idx = lua_tonumber(lua, -1);
Child ** c = static_cast<Child **>(lua_newuserdata(lua, sizeof(Parent *)));
*c = parent.children[idx];
luaL_getmetatable(lua, "ChildMetaTable"); // [-0, +1, m]
lua_setmetatable(lua, -2);
// return new userdata - does not work
return 1;
}
static const struct luaL_Reg Parent_FunctionList[] = {
{ "count", Parent_count },
{ "children", Parent_children },
{ NULL, NULL }
};
static int Parent_tostring(lua_State * lua) {
lua_pushstring(lua, "Parent");
return 1;
}
static int Parent_index(lua_State * lua) {
// stack -1=number(1) -2=table
int idx = lua_tonumber(lua, -1);
Child * c = parent.children[idx];
// what to return here?
return 0;
}
static const struct luaL_Reg Parent_MetaList[] = {
{ "__tostring", Parent_tostring },
{ "__len", Parent_count },
{ "__index", Parent_index },
{ NULL, NULL }
};
void Parent_Register(lua_State * lua) {
luaL_newlib(lua, Parent_FunctionList);
if(luaL_newmetatable(lua, "ParentMetaTable"))
luaL_setfuncs(lua, Parent_MetaList, 0);
lua_setmetatable(lua, -2);
lua_setglobal(lua, "Parent");
}
The Parent binding results in a global table, which is intentional. Testing the Parent table:
>print(Parent)
Parent
>print(#Parent)
2
>print(Parent.count())
2
But trying to access the children doesn't work as well
>c = Parent[1]
>print(c)
Child
>print(type(c))
userdata
>print(c.name())
[string "main"]:8: attempt to index a ChildMetaTable value (global 'c')
I get lost in Parent_index, where I need a pointer to the C Parent object rather than the Lua table. I understand the method is to use userdata or lightuserdata but can't see how to bind the class to Lua in order to do this. Same for the Child binding, which results in a ChildMetatable but no Lua table.
Edit: I've added in a children function under Parent, but still not working. Also changed some of the indexes for lua_setmetatable from bottom of stack to top of stack (negative)
Edit2: It's because I'm trying to have Parent:children act both as a table and as userdata. So I can return userdata with the C object pointer along with the ChildMetaTable with __index to determine what to do with the child methods.

What I was trying to do was to have both a Lua table and userdata at the same time.
First the parent __index method is best replaced by function in the table that creates new userdata for the child object.
static int Parent_children(lua_State * lua)
{
int idx = luaL_checkinteger(lua, -1);
Parent * p = &parent;
luaL_argcheck(lua, (idx >= 0) && (idx < (int)p->children.size()), 1, "index out of range");
Child ** pc = static_cast<Child **>(lua_newuserdata(lua, sizeof(Child *)));
*pc = parent.children[idx];
luaL_getmetatable(lua, "ChildMetaTable"); // [-0, +1, m]
lua_setmetatable(lua, -2);
return 1;
}
static const struct luaL_Reg Parent_MetaList[] = {
{ "__tostring", Parent_tostring },
{ NULL, NULL }
};
Since the child is not a Lua table, the child functions can be called from the __index method. Child_FunctionList stays the same.
static int Child_index(lua_State * lua)
{
const char * fn_name = luaL_checkstring(lua, -1);
for(const luaL_Reg * fn = Child_FunctionList; fn->name != NULL; fn++)
{
if(strcmp(fn_name, fn->name) == 0)
{
lua_pushcfunction(lua, fn->func);
return 1;
}
}
return 0;
}
static const struct luaL_Reg Child_MetaList[] = {
{ "__tostring", Child_name },
{ "__index", Child_index },
{ NULL, NULL }
};
And the child methods get a C pointer from the userdata.
static int Child_name(lua_State * lua)
{
Child * c = *reinterpret_cast<Child **>(luaL_checkudata(lua, -1, "ChildMetaTable"));
lua_pushstring(lua, c->name.c_str());
return 1;
}
And finally registering tables values for the child doesn't make sense, so can be removed but the metatable needs to be registered.
void Child_Register(lua_State * lua)
{
if(luaL_newmetatable(lua, "ChildMetaTable"))
luaL_setfuncs(lua, Child_MetaList, 0);
lua_pop(lua, 1);
}
This may not be the optimal solution, but it is heading there.
Edit:
The global parent can be passed as an upvalue in the luaL_newlib macro is expanded, rather than using the global parent.
luaL_newlibtable(lua, Parent_FunctionList);
lua_pushlightuserdata(lua, parent);
luaL_setfuncs(lua, Parent_FunctionList, 1);
...
static int Parent_children(lua_State * lua) {
Parent * parent= (Parent *)(lua_topointer(lua, lua_upvalueindex(1)));
...

Related

implementing __index metafunction in C/c++

I have a script to C++ callback/functor system that can call any "registered" C++ function using strings and/or variants.
//REMOVED ERROR CHECKS AND ERRONEOUS STUFF FOR THIS POST
int LuaGameObject::LuaCallFunction( lua_State *luaState )
{
if ( lua_isuserdata( luaState, 1 ) == 1 )
{
int nArgs = lua_gettop( luaState );
//Get GameObject
OGameObject* pGameObject = static_cast<OGameObject*>(lua_touserdata( luaState, 1 ));
if ( pGameObject )
{
//Get FunctionName
const char* functionNameString = lua_tostring( luaState, 2 );
//Get Args
std::vector<OVariant> args;
for ( int i = 3; i <= nArgs; ++i )
{
OVariant variant;
variant.SetFromLua( luaState, i );
args.push_back( variant );
}
//Call it!
CallGameObjectFunction( luaState, pGameObject, functionNameString, args );
return 1;
}
}
return 0;
}
OVariant LuaGameObject::ExecuteLua()
{
lua_State *lState = luaL_newstate();
luaL_openlibs( lState );
lua_register( lState, "Call", LuaCallFunction );
luaL_loadstring( lState, m_pScript );
//now run it
lua_pcall( lState, 0, 1, 0 );
//process return values
OVariant result;
result.SetFromLua( lState, -1 );
lua_close( lState );
return result;
}
In lua I can do something like this...
local king = Call("EmpireManager","GetKing")
Call("MapCamera","ZoomToActor",king)
However, I am feeling that I can use the __index metamethod to simplify the lua...
local king = EmpireManager:GetKing()
MapCamera:ZoomToActor(king)
I was hoping to achieve the simplified lua by using the following implemenation of the __index metamethod
Here is how I register the __index metafunction... (mostly copied from online examples)
void LuaGameObject::Register( lua_State * l )
{
luaL_Reg sRegs[] =
{
{ "__index", &LuaGameObject::LuaCallFunction },
{ NULL, NULL }
};
luaL_newmetatable( l, "luaL_EmpireManager" );
// Register the C functions into the metatable we just created.
luaL_setfuncs( l, sRegs, 0 );
lua_pushvalue( l, -1 );
// Set the "__index" field of the metatable to point to itself
// This pops the stack
lua_setfield( l, -1, "__index" );
// Now we use setglobal to officially expose the luaL_EmpireManager metatable
// to Lua. And we use the name "EmpireManager".
lua_setglobal( l, "EmpireManager" );
}
Unfortunately, I cant seem to get the callback setup right. Lua correctly calls my LuaGameObject::LuaCallFunction, but the stack does not contain what I would like. From within the LuaGameObject::LuaCallFunction, I can find the function name and EmpireManager object on the stack. But, I cant find the args on the stack. What is the proper way to set this up? Or is it not possible?
It is definitely possible to add methods to a userdata type in Lua, as explained in the Programming in Lua guide from the official website.
When you type the following Lua code:
myUserdata:someMethod(arg1,arg2,arg3)
Assuming myUserdata is a "userdata" object, the interpreter will do the following.
Call getmetatable(myUserdata).__index(myUserdata,"someMethod") to get the value of someMethod.
Call someMethod(myUserdata,arg1,arg2,arg3). someMethod can be anything callable from Lua. Examples: a Lua or C function, or a table/userdata with a __call metamethod.
Your __index metamethod should just return a function (or another object callable from Lua) implementing the method. Something like this:
// IMO, quite a misleading name for the __index metamethod (there is a __call metamethod)
int LuaGameObject::LuaCallFunction( lua_State *l)
{
// todo: error checking
OGameObject* pGameObject = static_cast<OGameObject*>(lua_touserdata( luaState, 1 ));
std::string memberName = lua_tostring( luaState, 2 );
int result = 1;
if (memberName == "method1") {
lua_pushcfunction(l,LuaGameObject::luaMethod1);
} else if (memberName == "method2") {
lua_pushcfunction(l,LuaGameObject::luaMethod2);
} else {
result = 0;
}
return result;
}
Basic skeleton of the functions returned by the __index metamethod:
int LuaGameObject::luaMethod1(lua_State* l) {
// todo: error checking.
OGameObject* pGameObject = static_cast<OGameObject*>(lua_touserdata(l, 1));
float arg1 = lua_tonumber(l, 2);
// get other args
pGameObject->method1(arg1 /*, more args if any.*/);
// optionally push return values on the stack.
return 0; // <-- number of return values.
}
Ok so after more research, I now believe that I cannot use __index metafunction to call a c functor with arguments. It only passes the table name and the key to the callback.
However, for anyone interested, it can be used for table-like objects, but not functions (as arguments are not pushed onto the stack). I will it for my "property" objects. They have no arguments and can be used in lua as follows...
local king = EmpireManager:king
king:name = "Arthur"
local name = king:name
These properly link to and call the appropriate C++ objects.functions
Actor::SetName(std::string name)
std::string Actor::GetName()
I had the same problem to call a method from my object and have used this post to develop the solution.
I hope that the example below can be useful to you.
#include <iostream>
#include <string>
#include <map>
#include <functional>
extern "C" {
#include "lua/lua.h"
#include "lua/lauxlib.h"
#include "lua/lualib.h"
}
//template<class UserdataType> // if will be work with lua garbage collector, use a function like that to delete the this_ptr (1st param)
//int DeletePtr(lua_State *lua_state) { // It's necessary register the metatable.__gc and to trust in gc (create just pointer of LuaObjects
// UserdataType** this_ptr = reinterpret_cast<UserdataType**>(lua_touserdata(lua_state, 1));
// delete (*this_ptr);
// return 0;
//}
template<class UserdataType>
int Closure(lua_State *lua_state) {
UserdataType** ptr = reinterpret_cast<UserdataType**>(lua_touserdata(lua_state, 1)); // This closure is being called by call operator ()
return (*ptr)->CallFunction(lua_state); // To access the function name called use lua stack index with lua_upvalueindex(-1)
} // Call the object method to resolve this called there
template<class UserdataType>
int ReturnClosure(lua_State *lua_state) { // This function is called as a lookup of metatable.__index
lua_pushcclosure(lua_state, Closure<UserdataType>, 1); // then we will return a closure to be called through call operator ()
return 1; // The 1st param (the only one) is the action name of function
} // Then a closure will grant access to ReturnClosure params as upvalues (lua_upvalueindex)
class LuaObject {
public:
LuaObject() : userdata_name("userdata1") {
}
void CreateNewUserData(lua_State* lua_ptr, const std::string& global_name) {
RegisterUserData(lua_ptr);
LuaObject** this_ptr = reinterpret_cast<LuaObject**>(lua_newuserdata(lua_ptr, sizeof(LuaObject*)));
*this_ptr = this;
luaL_getmetatable(lua_ptr, userdata_name.c_str());
lua_setmetatable(lua_ptr, -2); // setmetatable(this_ptr, userdata_name)
lua_setglobal(lua_ptr, global_name.c_str()); // store to global scope
}
int CallFunction(lua_State* lua_state) const {
std::string name = lua_tostring(lua_state, lua_upvalueindex(1)); // userdata:<function>(param2, param3)
auto it = functions.find(name); // <function> lua_tostring(lua_state, lua_upvalueindex(1))
if (it != functions.end()) { // <implicit this> lua_touserdata(l, 1)
return it->second(lua_state); // <param #1> lua_touserdata(l, 2)
} // <param #2> lua_touserdata(l, 3)
return 0; // <param #n> lua_touserdata(l, n+1)
}
void NewFunction(const std::string& name, std::function<int(lua_State*)> func) {
functions[name] = func;
}
private:
void RegisterUserData(lua_State* lua_ptr) {
luaL_getmetatable(lua_ptr, userdata_name.c_str());
if (lua_type(lua_ptr, -1) == LUA_TNIL) {
/* create metatable for userdata_name */
luaL_newmetatable(lua_ptr, userdata_name.c_str());
lua_pushvalue(lua_ptr, -1); /* push metatable */
/* metatable.__gc = DeletePtr<LuaObject> */
//lua_pushcfunction(lua_ptr, DeletePtr<LuaObject>);
//lua_setfield(lua_ptr, -2, "__gc");
/* metatable.__index = ReturnClosure<LuaObject> */
lua_pushcfunction(lua_ptr, ReturnClosure<LuaObject>);
lua_setfield(lua_ptr, -2, "__index");
}
}
std::map<std::string, std::function<int(lua_State*)>> functions;
std::string userdata_name;
};
int main(int argc, char* argv[]) {
lua_State* lua_state = luaL_newstate();
luaL_openlibs(lua_state);
LuaObject luaobj;
luaobj.CreateNewUserData(lua_state, "test_obj");
luaobj.NewFunction("action", [](lua_State* l)->int {
std::string result = "action has been executed";
LuaObject** ptr = reinterpret_cast<LuaObject**>(lua_touserdata(l, 1));
result += "\n #1 param is user_data (self == this) value = " + std::to_string(reinterpret_cast<size_t>(*ptr));
for (int i = 2; i <= lua_gettop(l); ++i) {
result += "\n #" + std::to_string(i)+ " = " + lua_tostring(l, i);
}
result += "\n #n param is passed on call operator () #n = " + std::to_string(lua_gettop(l));
lua_pushfstring(l, result.c_str());
return 1;
});
std::string lua_code;
lua_code += "print(test_obj:unknown_function()) \n";
lua_code += "print(test_obj:action()) \n";
lua_code += "print(test_obj:action(1)) \n";
lua_code += "print(test_obj:action(1, 2)) \n";
lua_code += "print(test_obj:action(1, 2, 'abc'))\n";
if (!(luaL_loadbuffer(lua_state, lua_code.c_str(), lua_code.length(), NULL) == 0 && lua_pcall(lua_state, 0, LUA_MULTRET, 0) == 0)) {
std::cerr << "Lua Code Fail: " << lua_tostring(lua_state, -1) << std::endl;
}
lua_close(lua_state);
return 0;
}
Output:
action has been executed
#1 param is user_data (self == this) value = 13629232
#n param is passed on call operator () #n = 1
action has been executed
#1 param is user_data (self == this) value = 13629232
#2 = 1
#n param is passed on call operator () #n = 2
action has been executed
#1 param is user_data (self == this) value = 13629232
#2 = 1
#3 = 2
#n param is passed on call operator () #n = 3
action has been executed
#1 param is user_data (self == this) value = 13629232
#2 = 1
#3 = 2
#4 = abc
#n param is passed on call operator () #n = 4

capnproto : Can I get an AnyPointer from a struct?

Given this schema
struct TestObject
{
value1 #0 : Int32 = -5;
value2 #1 : Float32 = 9.4;
}
struct TestContainer
{
object #0: TestObject;
}
Is it possible to get an AnyPointer::Builder from the TestObject::Builder in c++ code?
This is what I am trying to do:
::capnp::MallocMessageBuilder message;
auto container = message.initRoot<TestContainer>();
TestObject::Builder objBuilder = container.initObject();
//Get an AnyPointer
capnp::AnyPointer::Builder anyBuilder = capnp::toAny( objBuilder )(); //No this does not work.
MyTestObject test( 41, 643.7f );
test.serialise( anyBuilder );
What I am trying to do is have an abstract interface with a single argument type
eg.
class ISerialisable
{
virtual void serialise(capnp::AnyPointer::Builder& any) = 0;
}
class MyTestObject: public ISerialisable
{
void serialise(capnp::AnyPointer::Builder& any) override
{
auto testObjBuilder = any.getAs<TestObject>(); or should initAs be used?
testObject.setValue1( whatever1);
testObject.setValue2( whatever2);
}
}
Is it possible to go down this route?

gmock gtest how to setup the mock

Please consider the below code sample
NodeInterface * pPreNode = NULL;
NodeInterface * pChild = NULL;
for (uint16_t Index = 0; Index < Children.size(); ++Index)
{
pChild = Children[Index];
if (pPreNode == NULL)
{
pChild->SetPrevious(pChild);
pChild->SetNext(pChild);
}
else
{
pChild->SetNext(pPreNode->GetNext());
pChild->SetPrevious(pPreNode);
pPreNode->GetNext()->SetPrevious(pChild);
pPreNode->SetNext(pChild);
}
pPreNode = pChild;
}
To test this lines how to setup the mock exactly?
Children is a vector of Nodes and we are passing Mocked objects.
EXPECT_CALL(Obj, GetNode()).WillOnce(Invoke(this, &GetANewNode));
and the GetANewNode will provide new MockedNode
MockedNode * GetANewNode()
{
MockedNode * pMockedNode = new MockedNode();
return pMockedNode;
}
How to provide exact nodes for each Next(), Previous() calls?
EXPECT_CALL(*pMockedNode, SetNext(_));
EXPECT_CALL(*pMockedNode, SetPrevious(_));
EXPECT_CALL(*pMockedNode, GetNext());
EXPECT_CALL(*pMockedNode, GetPrevious());
Simple solution is to have all mocked nodes predefined before test case. And use Sequence/InSequence to be sure that everything happens in proper order.
class ObjTest : public ::testing::Test
{
protected:
const std::size_t N = ...; // I do not know how many do you need
std::vector<MockedNode> mockedNode;
std::vector<Node*> children;
Sequence s;
.... Obj; // I am not sure what is Obj in your question
ObjTest () : mockedNode(N)
{}
void SetUp() override
{
// initial setup
EXPECT_CALL(Obj, GetNode()).WillOnce(Return(&mockedNode.front())).InSequence(s);
}
};
Having such test class with initial setup - you can create test cases testing various scenarios that happen after initial sequence:
TEST_F(ObjTest, shouldLinkOneNodeToItself)
{
std::vector<Node*> children { &mockedNode[0] };
EXPECT_CALL(mockedNode[0], SetNext(&mockedNode[0])).InSequence(s);
EXPECT_CALL(mockedNode[0], SetPrevious(&mockedNode[0])).InSequence(s);
Obj.testedFunction(children); // you have not provided your tested function name...
}
And very similar test case for two children:
TEST_F(ObjTest, shouldLinkTwoNodesToEachOther)
{
std::vector<Node*> children { &mockedNode[0], &&mockedNode[1] };
// first interation
EXPECT_CALL(mockedNode[0], SetNext(&mockedNode[0])).InSequence(s);
EXPECT_CALL(mockedNode[0], SetPrevious(&mockedNode[0])).InSequence(s);
// second iteration
EXPECT_CALL(mockedNode[0], GetNext()).WillOnce(Return(&mockedNode[0])).InSequence(s);
EXPECT_CALL(mockedNode[1], SetNext(&mockedNode[0])).InSequence(s);
EXPECT_CALL(mockedNode[1], SetPrevious(&mockedNode[0])).InSequence(s);
// etc...
Obj.testedFunction(children);
}

Header functions

EDIT: by not working I mean that in my main array mA in main doesn't show any change to the elements within the array.
I have been checking my functions as I develop the headers and they have been working perfectly: Until I got to the final header MonitorArray.h.
mA.getScreen(i).checkScreen();
Didn't work and I couldn't work out why. So I created a new function within MonitorArray to do a similar job using the same function, and to my surprise it worked.
mA.pollScreens();
Which uses (Inside MonitorArray.h):
monitorArray[i].checkScreen();
Function getScreen:
ScreenArray MonitorArray::getScreen(int arrayPointer)
{
if (arrayPointer<0 || arrayPointer>=monitors)
{
return false;
}
else
{
return monitorArray[arrayPointer];
}
}
Function checkScreen and addArray:
void ScreenArray::checkScreen()
{
HDC dMonitor;
PixelArray pArray;
int lenX = 0, lenY = 0;
dMonitor = CreateDC(iMonitor.szDevice, iMonitor.szDevice, NULL, NULL);
lenX = (iMonitor.rcWork.right - iMonitor.rcWork.left) - 1;
lenY = (iMonitor.rcWork.bottom - iMonitor.rcWork.top) - 1;
pArray.setColour(0, GetPixel(dMonitor, 0, 0));
pArray...
...
...
addArray(&pArray);
ReleaseDC(NULL, dMonitor);
}
void ScreenArray::addArray(PixelArray* pA)
{
if (previousCheck(*pA))
{
arrayPosition = 0;
screenArray[arrayPosition] = *pA;
arrayPosition++;
}
else
{
screenArray[arrayPosition] = *pA;
arrayPosition++;
}
if (arrayPosition==11)
{
//Run screen saver on monitor
}
}
Why does running the command within the header file through a new function work but running the functions from main not?
Assuming that "didn't work" means "didn't affect the ScreenArray in my MonitorArray", it's because getScreen returns a copy of the array element
ScreenArray MonitorArray::getScreen(int arrayPointer)
while the new member function most likely works with the array directly.
You'll need to return a pointer to the array element instead:
ScreenArray* MonitorArray::getScreen(int arrayPointer)
{
if (arrayPointer<0 || arrayPointer>=monitors)
{
return NULL;
}
else
{
return &monitorArray[arrayPointer];
}
}
(BTW: the implicit conversion from bool to ScreenArray looks very odd.)

Inherited variables are not reading correctly when using bitwise comparisons

I have a few classes set up for a game, with XMapObject as the base, and XEntity, XEnviron, and XItem inheriting it.
MapObjects have a number of flags, one of them being MAPOBJECT_SOLID. My problem is that XEntity is the only class that correctly detects MAPOBJECT_SOLID. Both Items are Environs are always considered solid by the game, regardless of the flag's state. What is important is that Environs and Item should almost never be solid.
Each class has a very basic preliminary constructor, just initializing all varibles to zero or NULL. During the CreateX() phase, Objects are linked into the map, set into a linked linked list.
Both XItem and XEnviron are a tad sloppy. They are both new, and in the middle or my debugging attempts.
Here are the relevent code samples:
XMapObject:
#define MAPOBJECT_ACTIVE 1
#define MAPOBJECT_RENDER 2
#define MAPOBJECT_SOLID 4
class XMapObject : public XObject
{
public:
Uint8 MapObjectType,Location[2],MapObjectFlags;
XMapObject *NextMapObject,*PrevMapObject;
XMapObject();
void CreateMapObject(Uint8 MapObjectType);
void SpawnMapObject(Uint8 MapObjectLocation[2]);
void RemoveMapObject();
void DeleteMapObject();
void MapObjectSetLocation(Uint8 Y,Uint8 X);
void MapObjectMapLink();
void MapObjectMapUnlink();
};
XMapObject::XMapObject()
{
MapObjectType = 0;
Location[0] = 0;
Location[1] = 1;
NextMapObject = NULL;
PrevMapObject = NULL;
}
void XMapObject::CreateMapObject(Uint8 Type)
{
MapObjectType = Type;
}
void XMapObject::SpawnMapObject(Uint8 MapObjectLocation[2])
{
if(!(MapObjectFlags & MAPOBJECT_ACTIVE)) { MapObjectFlags += MAPOBJECT_ACTIVE; }
Location[0] = MapObjectLocation[0];
Location[1] = MapObjectLocation[1];
MapObjectMapLink();
}
XEntity:
XEntity *StartEntity = NULL,*EndEntity = NULL;
class XEntity : public XMapObject
{
public:
Uint8 Health,EntityFlags;
float Speed,Time;
XEntity *NextEntity,*PrevEntity;
XItem *IventoryList;
XEntity();
void CreateEntity(Uint8 EntityType,Uint8 EntityLocation[2]);
void DeleteEntity();
void EntityLink();
void EntityUnlink();
Uint8 MoveEntity(Uint8 YOffset,Uint8 XOffset);
};
XEntity::XEntity()
{
Health = 0;
Speed = 0;
Time = 1.0;
EntityFlags = 0;
NextEntity = NULL;
PrevEntity = NULL;
IventoryList = NULL;
}
void XEntity::CreateEntity(Uint8 EntityType,Uint8 EntityLocation[2])
{
CreateMapObject(EntityType);
SpawnMapObject(EntityLocation);
if(!(MapObjectFlags & MAPOBJECT_SOLID) { MapObjectFlags += MAPOBJECT_SOLID; }
EntityFlags = ENTITY_CLIPPING;
Time = 1.0;
Speed = 1.0;
EntityLink();
}
void XEntity::EntityLink()
{
if(StartEntity == NULL)
{
StartEntity = this;
PrevEntity = NULL;
NextEntity = NULL;
}
else
{
EndEntity->NextEntity = this;
}
EndEntity = this;
}
XEnviron:
class XEnviron : public XMapObject
{
public:
Uint8 Effect,TimeOut;
void CreateEnviron(Uint8 Type,Uint8 Y,Uint8 X,Uint8 TimeOut);
};
void XEnviron::CreateEnviron(Uint8 EnvironType,Uint8 Y,Uint8 X,Uint8 TimeOut)
{
CreateMapObject(EnvironType);
Location[0] = Y;
Location[1] = X;
SpawnMapObject(Location);
XTile *Tile = GetTile(Y,X);
Tile->Environ = this;
MapObjectFlags = MAPOBJECT_ACTIVE + MAPOBJECT_SOLID;
printf("%i\n",MapObjectFlags);
}
XItem:
class XItem : public XMapObject
{
public:
void CreateItem(Uint8 Type,Uint8 Y,Uint8 X);
};
void XItem::CreateItem(Uint8 Type,Uint8 Y,Uint8 X)
{
CreateMapObject(Type);
Location[0] = Y;
Location[1] = X;
SpawnMapObject(Location);
}
And lastly, the entity move code. Only entities are capable of moving themselves.
Uint8 XEntity::MoveEntity(Uint8 YOffset,Uint8 XOffset)
{
Uint8
NewY = Location[0] + YOffset,
NewX = Location[1] + XOffset;
if((NewY >= 0 && NewY < MAPY) && (NewX >= 0 && NewX < MAPX))
{
XTile *Tile = GetTile(NewY,NewX);
if(Tile->MapList != NULL)
{
XMapObject *MapObject = Tile->MapList;
while(MapObject != NULL)
{
if(MapObject->MapObjectFlags & MAPOBJECT_SOLID)
{
printf("solid\n");
return 0;
}
MapObject = MapObject->NextMapObject;
}
}
if(Tile->Flags & TILE_SOLID && EntityFlags & ENTITY_CLIPPING)
{
return 0;
}
this->MapObjectSetLocation(NewY,NewX);
return 1;
}
return 0;
}
What is wierd, is that the bitwise operator always returns true when the MapObject is an Environ or an Item, but it works correctly for Entities. For debug I am using the printf "Solid", and also a printf containing the value of the flag for both Environs and Items.
Any help is greatly appreciated, as this is a major bug for the small game I am working on. I am also very new at Object Oriented programming, anything tips, suggestions and/or criticism are also welcome.
Your problem appears to be that you never initialize MapObjectFlags in any classes other than XEnviron so, as a basic type, it will have an unspecified value in XItem, XEntity and other XMapObject derived objects. I suggest that, as a member of XMapObject you explicitly initialize it to a known value.
As a rule, it is generally a good idea to ensure that all members of basic type are explicitly initialized in the initializer list of every constructor that you define.
e.g.
XMapObject()
: MapObjectFlags(0)
, // ... other initializers
{
// Other initializations
}
You can't (legally) be calling XEntity::MoveEntity on a MapObject or Environ because they don't have such a method. If you're using static_cast to change your object pointer into an XEntity so you can call MoveEntity on it, then you really have no guarantees about how the bit operation will work. In some implementations, things may appear to work in MoveEntity, but what's actually happening is it's interpreting the other object's memory as an XEntity. When it tries to access the offset where it believes MapObjectFlags exists, it's not actually there and always has that bit set to 1.
I figured out the problem earlier today - It didn't have any relation to OO programming, inheritance, or bitwise; it was a simple scope error.
The problem was in the fact that during my quick test to get an Environ in game, I declared the new variable inside of the control switch sequence, so the next time any control was used, the Environ would act in unpredictable ways.
switch(Event.key.keysym.sym)
{
...
case SDLK_c: { XEnviron Environ; Environ.InitEnviron(...); }
...
}