Reputation: 745
I want to copy a part of a class to a buffer. I need this to make something that can look if there are changes in a class, and send it over the network to update the changes on the server.
I made a template class that can back-up and restore classes. Now I am making the "look for differences" function. I want that the user can define how big the memory blocks will be. This split the class in parts and takes less data to send.
The problem is that I can't copy a part of the class memory to the heap, I can't get the address correct. If I do "address of the class" + "0x04". Then I don't get the correct addres.
This is an exaple that I made:
testclass test1;
testclass test2;
test1.set(1,1);
test2.set(1,2);
cout << &test1 << " " << (&test1 + 0x04) << endl; //0018FF24 0018FF44
memcpy(&test2,&test1 + 0x04,4);
test2.echo(); //Wrong data!
The header:
class testclass
{
int test,test2;
public:
void set(int a, int b) {test = a, test2 = b;}
void echo() {cout << test << " " << test2 << endl;}
};
I hope someone help me with this problem.
Thanks!
Upvotes: 2
Views: 232
Reputation: 1022
1) At first, you need your class to be POD. At least you can take out data members to separate structure.
2) Then, if choose your offset granularity = 1, 2, 4 or 2^n bytes as necessary; and determine the type that suits this requirement: 2^n == sizeof(chunk_type)
. For example, you wish to byte-to-byte comparison, so cast you pointer to _State
(or MyClass
see #1) to desired type: (char*)this->m_state.
Here is the function, that tries to find the first chunk that differs in both classes, and returns its offset or -1 if no differences found.
class MyClass {
struct _State {
int a,b
};
_State m_state;
public:
typedef char chunk_type;
int next_diff_pos(const MyClass& other, size_t offset = 0) const {
chunk_type *pThis = &m_state,
*pOther = &other.m_state;
if (offset < sizeof(_State)) {
size_t n = offset;
while(*pThis++ == *pOther++ && n < sizeof(_State))
n++;
// We need to test this again, because of ambigous while condition
if (n < sizeof(_State))
return n;
}
return -1;
}
};
PS: Of course, your chunk_type
must have ==
operator defined (this done already for char, int and other scalars).
(I've not tested the code)
Upvotes: 0
Reputation: 279305
&test1 + 0x04
means to add 4 times the sizeof testclass
to the address of test1
, and the resulting pointer has type testclass*
. It would point to the fifth element of an array whose first element is at the address of test1
, except that test1
isn't part of an array, so the addition has undefined behavior.
What you seem to want is to add 4 bytes to the address of test1
. You can do that for example with ((char*)&test1) + 4
, which results in a pointer of type char*
. Beware that the standard doesn't guarantee that sizeof(int)
is 4, nor does it guarantee that offsetof(testclass, test2) == sizeof(int)
.
You're permitted to inspect any object's memory as char*
or unsigned char*
. But there are some limits on how useful this ability is:
Upvotes: 0
Reputation: 20739
Where does the magic 0x04 and 4 come from?
If this works, it is just because of a particular alignment and implementation. Better a more structured way:
class testclass
{
int test,test2;
public:
void set(int a, int b) {test = a, test2 = b;}
void echo() {cout << test << " " << test2 << endl;}
void backup_to(testclass& s)
{ s.test2 = test2; }
bool has_changed_respect(const testclass& s)
{ return s.test2 == test2; }
friend std::ostream& operator<<(std::ostream& s, const testclass& a)
{ return s << "testclass["<<&a<<"]: test="<<a.test<<", test2="<<a.test2<< std::endl; }
};
int main()
{
testclass t1, t2;
t1.set(1,1);
t2.set(3,4);
std::cout << t1 << t2;
t1.backup_to(t2);
std::cout << t2;
t1.set(5,6);
cout << t1 << t2 << t1.is_changed_respect(t2) << std::endl;
return 0;
}
Upvotes: 0
Reputation: 33415
Basically, you can't muck around with pointers like that. You generally can't rely on the compiler to coincidentally put meaningful data there.
If you want the address of members you should write &(test1.test2)
not &test1+0x04
because even IF an int is 4 bytes and IF the compiler hasn't padded the structure and IF you or someone else hasn't changed the contents of the class, then &test1+0x04
really means "&test1
plus 4*sizeof(test)
bytes", it's another way of reaching (&test1)[4]
in terms of pointer-array-equivalence.
Also, you can't memcpy
over classes in general and expect meaningful results, unless they are POD.
If you want to compare instances of a class, you should write a function which compares each of the members in turn.
You can't write a general-purpose method for this because C++ is not a reflective language. That means you can't write code which magically knows the names and types of the members of a class.
So, if you want to compare and patch data like this, you will need to do something like this:
struct Foo {
int a;
int b;
void export_differences (std :: ostream & o, const Foo & f) {
if (a != f.a) o << "a " << f.a << " ";
if (b != f.b) o << "b " << f.b << " ";
o << ";";
}
void import_differences (std :: istream & i) {
std :: string s;
while (i >> s) {
if (s == "a") i >> a;
else if (s == "b") i >> b;
else if (s == ";") break;
else throw std :: runtime_error ("bad input");
}
}
};
You will have to write something like this for each class you want to patch.
Upvotes: 2