I have an Entity
class that contains some attributes (pieces of data). These attributes are stored in a map from name => value.
class Entity
{
public:
// Sets the attribute with the specified name.
void attribute(const std::string& name, const GenericType& value) {
m_attributes[name] = value;
}
// Returns the attribute with the specified name.
GenericType& attribute(const std::string& name) {
return m_attributes[name];
}
template<typename AttributeType>
void attribute(const std::string& name, const AttributeType& value) {
m_attributes[name] = GenericType(value);
}
template<typename AttributeType>
AttributeType& attribute(const std::string& name) {
return generic_cast<AttributeType>(m_attributes[name]);
}
private:
// Map of attributes from name => value.
std::unordered_map<std::string, GenericType> m_attributes;
}
I have a method to create or overwrite an attribute and another method that returns the attribute with the specified name. The first two methods are exposed to Lua. The last two methods are for accessing and modifying attributes from my c++ source code. The problem is that attributes can be of any type. For example, I want to be able to do:
entity.attribute("Health", 100)
entity.attribute("Position", Vector3(1,2,3))
entity.attribute("Position").x = 4
It should be possible to read and modify attributes both from my c++ source files and from lua scripts. I have previously been using ChaiScript where I used the Boxed_Value class as my GenericType
. This worked well but excessive compile times have forced me to look elsewhere.
Is there a way to achieve this with LuaBind (or any other Lua binding library)? The luabind::object
class looks promising but it takes a lua_State
pointer in its constructor. This worries me as I feel that the Entity
class really shouldn't know anything about the Lua state.
In LuaBind your entity class doesn't need to know anything about lua state. A Lua binding does not have to be 1-to-1 syntactically identical to the C++ API, as these are quite different languages.
In your case, I would rather split the api into getters and setters. With the shown Entity
class you might get struggling with making LuaBind do what you want to do unambiguously. What you could do is write a wrapper class for Entity on the C++ side, which would have a LuaBind-conforming, simple interface, i.e. splitting getters and setters using unambiguous names.
Blufs is an example, demonstrating what I mean.
As a simple example, with LuaBridge:
somewhat tricky to create a simple, non hand-rolled binding:
class Entity {
std::map<std::string, int> attributes;
public:
void attribute(std::string const& key,int value) {
attributes[key] = value;
}
int& attribute(std::string const& key) {
return attributes[key];
}
};
a wrapper can be bound instead:
class EntityWrapper {
Entity entity;
public:
void set_int(std::string const& key,int value) {
entity.attribute(key,value);
}
int get_int(std::string const& key) {
return entity.attribute(key);
}
};
a simple binding:
void luabridge_bind(lua_State *L) {
luabridge::getGlobalNamespace(L)
.beginClass<EntityWrapper>("Entity")
.addConstructor<void(*)(), RefCountedPtr<EntityWrapper> /* creation policy */ >()
.addFunction("get_int", &EntityWrapper::get_int)
.addFunction("set_int", &EntityWrapper::set_int)
.endClass()
;
}
and in Lua:
local e = Entity()
e:set_int("bla",42)
print(e:get_int("bla"))
If you need Entity to interact with other APIs, write small wrappers that get the original wrapped object and pass it to other functions.