Reputation: 1891
I am trying to add a print() member function that would output memory content of an object, as below:
#include <iostream>
#include <string>
class A {
public:
virtual std::string print() {
std::string s;
s.append(reinterpret_cast<char*>(this), 0, sizeof(A));
return s;
}
};
class B : public A {
public:
B() : a('a') {}
char a;
};
int main() {
A a;
B b;
std::cout << "A \"" << a.print() << "\"\n";
std::cout << "B \"" << b.print() << "\"\n";
return 0;
}
How can I print the entire length of B, the listing above prints only the A portion of the class B.
Upvotes: 0
Views: 102
Reputation: 36617
Use a templated base with non-virtual function.
template<class T> class PrintMe
{
public:
std::string print() const
{
std::string s;
s.append(reinterpret_cast<char*>(this), 0, sizeof(T));
return s;
};
};
class A: public PrintMe<A>
{
// whatever
};
class B: public PrintMe<B>
{
};
// and in code which uses it
std::cout << some_b.print();
Essentially the rule is that any class, X
, which needs ability to print itself inherits from PrintMe<X>
.
Personally, I wouldn't use inheritance or do this as a member function of the class at all, instead doing
template<class T> std::string print(const T &x)
{
std::string s;
s.append(reinterpret_cast<char*>(&x), 0, sizeof(x));
return s;
}
// and in some code which needs this
std::cout << print(some_b);
// or, more explicitly
std::cout << print<B>(some_b);
Note that this avoids virtual function dispatch completely, instead relying on the type of the object being identified at compile time.
Upvotes: 1
Reputation: 5566
How can I print the entire length of B...
You might consider 'chaining' your print methods within the class hierarchy.
Your plan fails to handle non-printable values. I don't see a 'hex' conversion either. But, presuming your cast to char* does what you want ...
class A {
public:
virtual std::string print() {
std::string s;
s.append(reinterpret_cast<char*>(this), 0, sizeof(A));
return s;
}
};
class B : public A {
public:
B() : a('a') {}
char a;
virtual std::string print() {
std::string s = A::print(); // get A contents
s.append(reinterpret_cast<char*>(this), 0, sizeof(B));
return s;
}
};
Not tested.
Also, these issue are implementation dependent.
It has been my experience (almost entirely with g++) that an instance of B contains all the data of both A and B (and I think A is in front i.e. lower address). An instance of A has only data of A, and can not tell, does not know anything about any derived classes.
If in your implementation, B has all of A and B data (easy to tell, just print the sizeof(A) and sizeof(B) and compare to expectations.), you need not invoke A::print() inside of B::print().
Also note - if either class uses containers, the container data you wish to print is likely not in the stack space of your class instance. Containers (vector, heap, list, etc.) use heap. So you would find pointers on the stack, and the data elsewhere.
update --
Here is a small attempt to reveal parts of the g++ implementation details.
My interpretation is that it shows that a class B instance contains the data attributes of a class A instance in front (at a lower address) of the class B data attributes. And that B is twice the size of A.
For this code I removed the virtual keywords. Virtual affects the size of these objects in the way I expected it to. But without keyword, the size is exactly what I expect for the uint64_t .. 8 (in A) and 16 (in B) bytes.
class A
{
public:
A() :
aData(0x3132333435363738)
{
}
~A(){ }
std::string dump()
{
std::stringstream ss;
ss << " this: " << &(*this);
ss << " aData: " << &aData << std::endl;
return(ss.str());
}
std::string show()
{
std::stringstream ss;
ss << std::hex << "0X" << aData << std::endl;
return ss.str();
}
uint64_t aData; // 8 bytes
};
class B : public A
{
public:
B() : bData(0x3837363534333231)
{
}
~B(){ }
uint64_t bData; // 8 bytes
std::string dump()
{
std::stringstream ss;
ss << " this: " << &(*this);
ss << " A::aData: " << &(A::aData) << " bData:" << &bData
<< std::endl;
return(ss.str());
}
std::string show()
{
std::stringstream ss;
ss << std::hex << "0x" << A::aData << " 0x" << bData
<< std::endl;
return ss.str();
}
};
int t405(void)
{
A a;
B b;
std::cout << "\nsizeof(a): " << sizeof(a) << std::endl;
std::cout << "sizeof(b): " << sizeof(b) << std::endl;
std::cout << "\ninstance a: " << &a << std::endl;
std::cout << "instance b: " << &b << std::endl;
std::cout << "\ninstance a - aData: " << a.dump() << std::flush;
std::cout << "\ninstance b - bData: " << b.dump() << std::flush;
std::cout << "\ninstance a show(): " << a.show() << std::flush;
std::cout << "\ninstance b show(): " << b.show() << std::flush;
return(0);
}
The output should look something like this:
sizeof(a): 8 sizeof(b): 16
instance a: 0x7ffe73f5b5d0 instance b: 0x7ffe73f5b5e0
instance a - aData: this: 0x7ffe73f5b5d0 aData: 0x7ffe73f5b5d0
instance b - bData: this: 0x7ffe73f5b5e0 A::aData: 0x7ffe73f5b5e0 bData:0x7ffe73f5b5e8
instance a show(): 0X3132333435363738
instance b show(): 0x3132333435363738 0x3837363534333231
Upvotes: 0
Reputation: 1
First, you can
#include <stdint.h>
now you're able to cast your object to uint8_t, and iterating it by for-loop, (size = sizeof(object)).
Printing its hex value to stdin by printf:
printf("%hu", val[i]);
Upvotes: -1
Reputation: 73465
To do this safely, you'd have to override the virtual function for B:
std::string print() override {
std::string s;
s.append(reinterpret_cast<char*>(this), 0, sizeof(B));
return s;
}
Why ? Because you can't be sure that A and B have the same address (for instance if there would be multiple base classes).
If you're fond of such dumping function, in order to reduce code, you could use a template and invoke the template in each override.
Upvotes: 2