Mapping Class/Structure to use with DB

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.
 
K

Kai-Uwe Bux

Jim said:
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).


You could try something like this:

template < typename T >
void dummy ( T const & );

template <>
void dummy<int> ( int const & i ) {}

enum E { a, b };

int main ( void ) {
int i;
dummy( i );
dummy( a ); // this yields a linker error.
}


or even:

template < typename T >
struct dummy_struct;

template <>
struct dummy_struct<int> {

static
void func ( int const & i ) {}

};

template < typename T >
void dummy ( T const & t ) {
dummy_struct<T>::func( t );
}

enum E { a, b };

int main ( void ) {
int i;
dummy ( i );
dummy ( a ); // now this yields a compiler error
}


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.


Best

Kai-Uwe Bux
 
J

Jim Langston

Kai-Uwe Bux said:
Jim said:
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).


You could try something like this:

template < typename T >
void dummy ( T const & );

template <>
void dummy<int> ( int const & i ) {}

enum E { a, b };

int main ( void ) {
int i;
dummy( i );
dummy( a ); // this yields a linker error.
}


or even:

template < typename T >
struct dummy_struct;

template <>
struct dummy_struct<int> {

static
void func ( int const & i ) {}

};

template < typename T >
void dummy ( T const & t ) {
dummy_struct<T>::func( t );
}

enum E { a, b };

int main ( void ) {
int i;
dummy ( i );
dummy ( a ); // now this yields a compiler error
}


Actually, its the var and not the enum value that I would have a concern
with. Something doing (in my code)

E MyEnum;
SetOffset( MyEnum );
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.
 
K

Kai-Uwe Bux

Jim said:
Kai-Uwe Bux said:
Jim said:
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).


You could try something like this:

template < typename T >
void dummy ( T const & );

template <>
void dummy<int> ( int const & i ) {}

enum E { a, b };

int main ( void ) {
int i;
dummy( i );
dummy( a ); // this yields a linker error.
}


or even:

template < typename T >
struct dummy_struct;

template <>
struct dummy_struct<int> {

static
void func ( int const & i ) {}

};

template < typename T >
void dummy ( T const & t ) {
dummy_struct<T>::func( t );
}

enum E { a, b };

int main ( void ) {
int i;
dummy ( i );
dummy ( a ); // now this yields a compiler error
}


Actually, its the var and not the enum value that I would have a concern
with. Something doing (in my code)

E MyEnum;
SetOffset( MyEnum );


You want compilation of those lines to fail, right? Well, then the code I
gave should give you a hint because, it is not the value that triggers the
error message: it is the type. The following also fails to compile:

int main ( void ) {
int i;
E e;
dummy ( i );
dummy ( e ); // this yields an error
}

Maybe, I am misunderstanding you.
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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,143
Latest member
DewittMill
Top