I've been working on a project to map a MySQL database to a C++ class.
Well, I actually got it to work, but some if it I just feel is exceptionally
ugly. For example, in my operator<< override:
template<typename T>
CMySQLTable& operator<<(const T& ClassTable)
{
/**/
}
I have a switch statement and code that looks like this:
switch ( ThisField.FieldType() )
{
case SQL_FieldType_Unknown:
std::cerr << " SQL_FieldType_Unknown: Error!" << std::endl;
assert(false);
break;
case SQL_FieldType_VarChar:
ThisField = *reinterpret_cast<const std::string*>( reinterpret_cast<const
char*>( &ClassTable ) + ThisOffset.Offset );
break;
case SQL_FieldType_Int:
ThisField = *reinterpret_cast<const int*>( reinterpret_cast<const
char*>( &ClassTable ) + ThisOffset.Offset );
break;
case SQL_FieldType_UnsignedInt:
ThisField = *reinterpret_cast<const unsigned int*>(
reinterpret_cast<const char*>( &ClassTable ) + ThisOffset.Offset );
break;
case SQL_FieldType_Bool:
ThisField = *reinterpret_cast<const bool*>( reinterpret_cast<const
char*>( &ClassTable ) + ThisOffset.Offset );
break;
/**/
}
It's the complicated casts that I find ugly. And in the operator>override
it's just as bad, if not worst.
template<typename T>
CMySQLTable& operator>>(T& ClassTable)
{
/**/
}
switch ( ThisField.FieldType() )
{
case SQL_FieldType_Unknown:
std::cerr << " SQL_FieldType_Unknown: Error!" << std::endl;
assert( false );
break;
case SQL_FieldType_VarChar:
*reinterpret_cast<std::string*>( reinterpret_cast<char*>( &ClassTable ) +
ThisOffset.Offset ) = ThisField.Value();
break;
case SQL_FieldType_Int:
*reinterpret_cast<int*>( reinterpret_cast<char*>( &ClassTable ) +
ThisOffset.Offset ) = StrmConvert<int>( ThisField.Value() );
break;
case SQL_FieldType_UnsignedInt:
*reinterpret_cast<unsigned int*>( reinterpret_cast<char*>( &ClassTable )
+ ThisOffset.Offset ) = StrmConvert<unsigned int>( ThisField.Value() );
break;
case SQL_FieldType_Bool:
*reinterpret_cast<bool*>( reinterpret_cast<char*>( &ClassTable ) +
ThisOffset.Offset ) = ThisField.Value() == "0" ? false : true;
break;
/**/
}
Can you think of a cleaner way to do this, or is this the way I'm stuck with
just because of what I'm doing (working with pointers and offsets).
What I'm doing, and it works in test, is I have a class include a subclass
in which I map the variables I want to pull from the database.
void CCharFieldMap::SetMap( CCharacter* Base )
{
SetBase( Base );
SetOffset( "Version", Base->Version );
SetOffset( "Name", Base->Name );
SetOffset( "Password", Base->Password );
SetOffset( "Avatar", Base->Avatar );
SetOffset( "Race", *reinterpret_cast<unsigned int*>( &Base->Race ) );
SetOffset( "Sex", *reinterpret_cast<unsigned int*>( &Base->Sex ) );
SetOffset( "GM", Base->GM );
/**/
}
My overrides for << and >look at this instance that contains information
about the variables (Type of variable, offset into class) and transfer data
back and forth using the base address of the passed reference and the
offsets.
It's the only way I could find to do it, and right now it seems it will be
extremely easy to use for the user (me) for new classes. No longer do I
have to go through SQL schenanigans to pull data to/from databases, I can
simply do code like:
if ( ! PlayerTable.init( "192.168.1.100", "serpardum", "somepassword",
"abyssal", "players", 3307 ) )
{
std::cerr << "Initialization failed" << std::endl;
std::string wait;
std::getline( std::cin, wait );
return 1;
}
if ( ! PlayerTable.LoadTable( "Name", "Serpardum" ) )
{
std::cout << "Serpardum not found" << std::endl;
return 1;
}
PlayerTable >Player;
which would open my MySQL database, load the record where the Name ==
Serpardum, and load it into my class.
I like it, but as I said, I find some of it ugly. Any suggestions?