Original Post
I'm trying to automate some code now, to reduce the amount of work required later. My motivation is some of the magic features of higher-level game programming languages, like Unreal-script, where you can use keywords like 'simulated' or replication statements like 'reliable if' -- this lets game programmers write network code at a conceptual level, without having to worry about nitty-gritty implementation details. Specifically, I'm looking for a way to add higher-level functioanlity to variables (like network replication), preferably with as little game-programmer effort as possible. I've come up with 3 approaches (code is psuedo-C++) and I'd appreciate any advice on how evil they are, or if they could be improved (e.g. using boosty techniques). #1 The good old interface Engine code Game code #2 The all knowing base class Engine code Game code #3 Macro magic Engine code Game code #1 is the most familiar and easiest to implement, but it shifts all the work onto the game programmer, which is what I want to avoid. #2 cuts down the amount of work required of the game-programmer to just some kind of registration mechanism, but it seems overly complex. #3 uses all kinds of magic, but it's within the engine where magic belongs. The game programmer has a very easy time, but might be left with a bad taste in their mouth from having to use a #define like that... I'm really not sure which avenue to pursue here.
class Interface
{
public:
virtual void SetDefalts() = 0;
virtual void Save( stream& s ) = 0;
virtual void Load( stream& s ) = 0;
virtual void WriteDiff( stream& s, const Interface& o ) = 0;
};
class Thing : public Interface
{
int m_one, m_two;
public:
virtual void SetDefalts()
{
m_one = 1;
m_two = 2;
}
virtual void Save( stream& s )
{
s << m_one;
s << m_two;
}
virtual void Load( stream& s )
{
s >> m_one;
s >> m_two;
}
virtual void WriteDiff( stream& s, const Interface& o )
{
Thing* pO = dynamic_cast<Thing*>( &o );
assert( pO );
if( m_one != o.m_one ) {
s.write_bit(1);
s << m_one;
} else
s.write_bit(0);
if( m_two != o.m_two ) {
s.write_bit(1);
s << m_two;
} else
s.write_bit(0);
}
}
class Base
{
public:
void SetDefalts()
{
for each m_Vars as it
it->SetDefalts( this->*data );
}
void Save( stream& s ) {
for each m_Vars as it
it->Save( this->*data, s );
}
void Load( stream& s ) {
for each m_Vars as it
it->Load( this->*data, s );
}
void WriteDiff( stream& s, const cname& o ) {
for each m_Vars as it
it->WriteDiff( this->*data, o.*data, s );
}
protected:
struct VarInfo
{
pointer_to_member data;
func_ptr SetDefault;
func_ptr Save;
func_ptr Load;
func_ptr Diff;
}
std::vector< VarInfo > m_Vars;
};
class Thing : public Base
{
int m_one, m_two;
SetDefaultFunctor<int> m_sdOne, m_sdTwo;
public:
Thing() : m_sdOne(1), m_sdTwo(2)
{
VarInfo infoOne = { &Thing::m_one, m_sdOne, &SaveInt, &DiffInt };
VarInfo infoTwo = { &Thing::m_two, m_sdTwo, &SaveInt, &DiffInt };
m_Vars.push_back( infoOne );
m_Vars.push_back( infoTwo );
}
}
#define MAKE_VAR( type, name, val ) \
type name; //
#define ASSIGN_VAR( type, name, val ) \
name = val; //
#define STORE_VAR( type, name, val ) \
s << name; //
#define LOAD_VAR( type, name, val ) \
s >> name; //
#define DIFF_VAR( type, name, val ) \
if( name != o.name ) { \
s.write_bit(1); \
s << name; \
} else \
s.write_bit(0); //
#define DoMagic( cname, list ) \
private: \
list( MAKE_VAR ) \
void SetDefalts() { \
list( ASSIGN_VAR ) \
} \
public: \
void Save( stream& s ) { \
list( STORE_VAR ) \
} \
void Load( stream& s ) { \
list( LOAD_VAR ) \
} \
void WriteDiff( stream& s, const cname& o ) { \
list( DIFF_VAR ) \
} //
class Thing
{
#define Variables( Var ) \
Var( int, m_one, 1 ) \
Var( int, m_two, 2 ) //
DoMagic( Thing, Variables )
#undef Variables
};