Per said:
I'm trying to write a base class for scriptable objects. The subclasses
of this class hold a set of properties that must be identified by name
(a string), that can be retrieved or set by a script. But never mind the
scripting, I just explain it so you can see why the properties must be
identified by names. When the member functions
int Scriptable::luaGetProperty( lua_State *lua, string &propertyName )
int Scriptable::luaSetProperty( lua_State *lua, string &propertyName )
are called on the scriptable objects, the respective subclasses must be
free to implement the getting and setting of these properties
themselves, so it's not possible to just let these properties be held by
some hashtable. It would be nice to let the base class just forward the
requests to the following member functions:
int Scriptable::luaGetProperty( lua_State *lua, int propertyCode )
int Scriptable::luaSetProperty( lua_State *lua, int propertyCode )
which take an int code (i.e. an enum) instead of an action name, cause
then I can just use switch statements like this:
switch( propertyCode ) {
case HEIGHT_PROPERTY:
// perform code to get/set height
case WIDTH_PROPERTY:
// etc...
}
My problem is the following: I thought it would be a bad idea to
hardcode all property names and respective codes in the base class. So,
I thought I'd let the base class hold a registry of property names and
codes in a hashtable, as a static member shared by subclasses.
You want to respect the relationship between the base class and its
subclasses.
Not just use the base class as a universal repository of static
properties.
Its completely conceivable to have some static repository of properties
in your program, but don't put it in your base class. Let a static
collection of properties live outside of the class. That way, you can
create different Scriptable derivatives based on different sets of
static data.
See if the code below helps. Note that std::map is not neccessarily a
solution here. Its just a suggestion (std::maps load reletively slowly
but search/find is infinitely faster that an array with op[]). You
might also consider using a reference to a static collection in your
base class with a set of Scriptables referring to one static map
collection and another set of Scriptables referring to some other
static collection. The possibilities are endless.
Note the ctors that take an external std::map collection as a
parameter.
#include <iostream>
#include <ostream>
#include <map>
class Base
{
std::map< std::string, int > properties;
public:
Base() : properties() { }
Base(std::map< std::string, int >& r_m) : properties(r_m) { }
Base(const Base& copy) { properties = copy.properties; }
virtual ~Base() { }
/* virtual member functions */
virtual void /* staggered to fit */
registerPropertiesCode(const std::string&, const int) = 0;
virtual int
getProperty(const std::string& r_s) const;
/* global friend op<< */
friend std:
stream&
operator<<(std:
stream&, const Base&);
};
void
Base::registerPropertiesCode(const std::string& r_s, const int n)
{
properties.insert( std::make_pair< std::string, int >(r_s, n) );
}
int
Base::getProperty(const std::string& r_s) const
{
typedef std::map< std::string, int >::const_iterator MIter;
MIter found = properties.find( r_s );
if(found != properties.end())
return (*found).second;
else
return -1;
}
std:
stream& operator<<(std:
stream& os, const Base& r_b)
{
typedef std::map< std::string, int >::const_iterator MIter;
for( MIter miter = r_b.properties.begin();
miter != r_b.properties.end();
++miter )
{
os << "property: " << (*miter).first;
os << "\t" << "value: " << (*miter).second;
os << "\n";
}
return os;
}
class Scriptable : public Base
{
public:
Scriptable() { }
Scriptable(std::map< std::string, int >& m) : Base(m) { }
Scriptable(const Scriptable& copy) : Base(copy)
{
}
/* required virtuals */
void
registerPropertiesCode(const std::string& r_s, const int n)
{
Base::registerPropertiesCode(r_s, n);
}
};
int main()
{
Scriptable instance;
instance.registerPropertiesCode("HEIGHT_PROPERTY", 60);
instance.registerPropertiesCode("WIDTH_PROPERTY", 100);
instance.registerPropertiesCode("XCOORD_PROPERTY", 10);
instance.registerPropertiesCode("YCOORD_PROPERTY", 0);
std::cout << "get WIDTH_PROPERTY: ";
std::cout << instance.getProperty("WIDTH_PROPERTY");
std::cout << std::endl;
std::cout << "get BOGUS_PROPERTY: ";
std::cout << instance.getProperty("BOGUS_PROPERTY");
std::cout << std::endl;
std::cout << instance << std::endl; // friend op<<
Scriptable copy(instance); // copy the properties
std::cout << copy << std::endl;
static std::map< std::string, int > mycollection;
mycollection.insert(
std::make_pair< std::string, int >("FIRST_PROPERTY", 1) );
mycollection.insert(
std::make_pair< std::string, int >("SECOND_PROPERTY", 2) );
mycollection.insert(
std::make_pair< std::string, int >("THIRD_PROPERTY", 3) );
Scriptable another(mycollection);
std::cout << another << std::endl;
}
/*
get WIDTH_PROPERTY: 100
get BOGUS_PROPERTY: -1 // probably not appropriate
property: HEIGHT_PROPERTY value: 60
property: WIDTH_PROPERTY value: 100
property: XCOORD_PROPERTY value: 10
property: YCOORD_PROPERTY value: 0
property: HEIGHT_PROPERTY value: 60
property: WIDTH_PROPERTY value: 100
property: XCOORD_PROPERTY value: 10
property: YCOORD_PROPERTY value: 0
property: FIRST_PROPERTY value: 1
property: SECOND_PROPERTY value: 2
property: THIRD_PROPERTY value: 3
*/
Merry Xmas to all !!