J
Jim Langston
I am attempting to map the variables in a class or structure to use with
MySQL. I got something to work but I'm not happy with it. Here is a
snippet showing what I'm not happy with:
class COffsetMap
{
public:
COffsetMap() {}
virtual ~COffsetMap() {}
void SetBase( void* Base ) { Base_ = reinterpret_cast<char*>( Base ); }
void SetOffset( const std::string& Var )
{
Offsets.push_back( COffset( reinterpret_cast<const char*>( &Var ) -
Base_, SQL_FieldType_VarChar ) );
}
void SetOffset( int& Var )
{
Offsets.push_back( COffset( reinterpret_cast<const char*>( &Var ) -
Base_, SQL_FieldType_Int ) );
}
void SetOffset( unsigned int& Var )
{
Offsets.push_back( COffset( reinterpret_cast<const char*>( &Var ) -
Base_, SQL_FieldType_UnsignedInt ) );
}
// More of these
std::vector< COffset > Offsets;
void ShowOffsets()
{
std::cout << "Count:" << static_cast<unsigned int>( Offsets.size() )
<< std::endl;
for ( unsigned int i = 0; i < Offsets.size(); ++i )
std::cout << static_cast<unsigned int>( Offsets.Offset ) <<
std::endl;
}
private:
char* Base_;
};
Which is used like:
class CCharFieldMap: public COffsetMap
{
public:
void SetMap( CCharacter* Base )
{
SetBase( Base );
SetOffset( Base->Version );
SetOffset( Base->Name );
SetOffset( Base->Password );
SetOffset( Base->Avatar );
SetOffset( Base->GM );
// More of these
}
};
int main()
{
CCharacter Player;
CCharFieldMap CharFieldMap;
CharFieldMap.SetMap( &Player );
CharFieldMap.ShowOffsets();
// ...
}
Now, what I'm not happy with is const correctness. You may notice that
void SetOffset( int& Var )
is not const correct. The reason being if the user attempts to map a var
that is an enum, I do not want it to compile, they need to do something
special for those (not sure what yet). But, if this is declared as:
void SetOffset( const int& Var )
it compiles anyway and gives a wrong offset. Digging a little into it I
found it was creating a temporary int (converting enum to int) and passing
the reference to that, which is stored on the stack. If I remove the const
it won't compile (which is good).
I can just see myself, however, 4 months down the line refactoring code and
thinking, "oh, missed const correct on this one" and putting in the const
and breaking code. I could add a comment like
// Can not be const correct becuase of enum
but the fraility of this program depending on a const or not has me worried.
Has anyone else done this, or have a better way to do this? The whole
object is being able to do something like:
(psuedo code)
if ( ! PlayerTable.LoadTable( "Name", "Serpardum" ) )
std::cout << "Serpardum not found" << std::endl;
else
PlayerTable >> Player;
Which would use the internal offset map to load from the MySQL query
directly into the object. I've already tested if I can do this and it does
in fact work for my test case I did as a function.
Yes, I know it breaks encapsulation, but I figure if someone didn't want to
use this because it breaks encapsulation for their class, they could use the
other methods I'm building in such as
Player.name( PlayerTable["Name"] );
and other methods.
MySQL. I got something to work but I'm not happy with it. Here is a
snippet showing what I'm not happy with:
class COffsetMap
{
public:
COffsetMap() {}
virtual ~COffsetMap() {}
void SetBase( void* Base ) { Base_ = reinterpret_cast<char*>( Base ); }
void SetOffset( const std::string& Var )
{
Offsets.push_back( COffset( reinterpret_cast<const char*>( &Var ) -
Base_, SQL_FieldType_VarChar ) );
}
void SetOffset( int& Var )
{
Offsets.push_back( COffset( reinterpret_cast<const char*>( &Var ) -
Base_, SQL_FieldType_Int ) );
}
void SetOffset( unsigned int& Var )
{
Offsets.push_back( COffset( reinterpret_cast<const char*>( &Var ) -
Base_, SQL_FieldType_UnsignedInt ) );
}
// More of these
std::vector< COffset > Offsets;
void ShowOffsets()
{
std::cout << "Count:" << static_cast<unsigned int>( Offsets.size() )
<< std::endl;
for ( unsigned int i = 0; i < Offsets.size(); ++i )
std::cout << static_cast<unsigned int>( Offsets.Offset ) <<
std::endl;
}
private:
char* Base_;
};
Which is used like:
class CCharFieldMap: public COffsetMap
{
public:
void SetMap( CCharacter* Base )
{
SetBase( Base );
SetOffset( Base->Version );
SetOffset( Base->Name );
SetOffset( Base->Password );
SetOffset( Base->Avatar );
SetOffset( Base->GM );
// More of these
}
};
int main()
{
CCharacter Player;
CCharFieldMap CharFieldMap;
CharFieldMap.SetMap( &Player );
CharFieldMap.ShowOffsets();
// ...
}
Now, what I'm not happy with is const correctness. You may notice that
void SetOffset( int& Var )
is not const correct. The reason being if the user attempts to map a var
that is an enum, I do not want it to compile, they need to do something
special for those (not sure what yet). But, if this is declared as:
void SetOffset( const int& Var )
it compiles anyway and gives a wrong offset. Digging a little into it I
found it was creating a temporary int (converting enum to int) and passing
the reference to that, which is stored on the stack. If I remove the const
it won't compile (which is good).
I can just see myself, however, 4 months down the line refactoring code and
thinking, "oh, missed const correct on this one" and putting in the const
and breaking code. I could add a comment like
// Can not be const correct becuase of enum
but the fraility of this program depending on a const or not has me worried.
Has anyone else done this, or have a better way to do this? The whole
object is being able to do something like:
(psuedo code)
if ( ! PlayerTable.LoadTable( "Name", "Serpardum" ) )
std::cout << "Serpardum not found" << std::endl;
else
PlayerTable >> Player;
Which would use the internal offset map to load from the MySQL query
directly into the object. I've already tested if I can do this and it does
in fact work for my test case I did as a function.
Yes, I know it breaks encapsulation, but I figure if someone didn't want to
use this because it breaks encapsulation for their class, they could use the
other methods I'm building in such as
Player.name( PlayerTable["Name"] );
and other methods.