Isolation / problem of object granularity and problem of data collection when the user calls the new operator

I am working on a small memory tool that tracks allocations and releases, object sizes, object types, etc. The method that I use to track source files, line numbers, and object types works as follows:

#define DEBUG_NEW SourcePacket(__FILE__, __LINE__) * new #define new DEBUG_NEW 

SourcePacket is just a small class that accepts const char * and int at build time. These values ​​are populated with the __FILE__ and __LINE__ . The type of object is acquired as follows:

 template<typename T> T* operator*(const SourcePacket& packet, T* p); 

p - pointer to a newly selected object whose type was detected using RTTI. In operator overloading, information is taken and stored in the tracer, and the pointer is passed to the program. Additional information, such as size and address, is captured in the overloaded new operator.

Now this setup worked very well for me. This does not work for code that I am not compiling, of course, but one of the best things is that it does a great job of posting new calls made by the user that do not work using the often quoted

 #define new new(__FILE__, __LINE__) 

method. The problem I ran into is that if the user calls the new operator, the program simply cannot compile. Of course, this is because the macro expands so much.

 return operator SourcePacket("blahblah.cpp", 20) * new(size); 

instead

 return SourcePacket("blahblah.cpp", 20) * new(size); 

I do not see anything like it. I could, of course, just delete the new SourcePacket * procedure and just let my overloaded operator collect the size and address, but this view affects a significant part of the purpose of the tool.

(Also, as a note, I am not trying to create Valgrind or anything else, and I know that overloading global ops can be pretty tricky. This is mainly for educational purposes. In addition, I know that OS- in order to to detect some of this information, you can use certain functions, but I would like to use only standard C ++ so that it is cross-platform and bit-independent (x86, x64, etc.). Until now, it has worked flawlessly for me on both Linux and Windows, releases of both bit tastes.)

Unfortunately, there is no way to conditionally use one way or another, depending on whether it is just a new (or new or new) or new operator. It is not critical that I get this to work, but I would be interested to hear if anyone has found a way to this restriction.

+4
source share
1 answer

We need an expression that acts if the prefix is ​​"operator" and when not. This means that we need to define the operator that takes the SourcePacket. This may lead to other arguments, but it turns out that this is not necessary. A unary operator * will do nicely:

 const SourcePacket& operator *(const SourcePacket& sp) { return sp; } #define DEBUG_NEW *(SourcePacket(__FILE__, __LINE__)) * new #define new DEBUG_NEW 

Since we cannot completely bracket the instruction, there is still the possibility of errors if they are used in all but the simplest.

 struct Chain { Chain() : next(0) {} Chain(Chain *n) : next(n) {} ~Chain() {delete next;} Chain* next; Chain& operator *(Chain* b); }; Chain& Chain::operator *(Chain* b) { if (b != next) { if (next) { delete next; } next = b; } return *this; } int main() { Chain fetters; /* since * is left associative, it tries to call operator*(Chain&, const SourcePacket&) */ fetters * new Chain(); // This compiles fetters * (new Chain()); } 

To solve this problem, we need to define the appropriate operator. For the return type, you can define a hierarchy of template classes that connect the left argument to SourcePacket and are commutative in the correct argument ( (a:A βŠ™ b:SourcePacket) * c:C) = (a:A βŠ™ c:C) * b:SourcePacket , where βŠ™ is some binary C ++ operator). Something like the following, but without the mistakes that he undoubtedly possesses.

 template <typename L, typename Rslt=L, typename R=const SourcePacket> struct PairedTraits { typedef L Left; typedef R Right; typedef Rslt Result; typedef PairedTraits<Result> ResultTraits; }; template <typename L, typename Traits = PairedTraits<L> > struct Paired { typedef typename Traits::Left Left; typedef typename Traits::Right Right; typedef typename Traits::Result Result; typedef Paired<typename Traits::Result, typename Traits::ResultTraits> ResultPaired; Left& left; Right& right; Paired(Left& l, Right& r) : left(l), right(r) {} operator Left&() {return left;} template <typename A> ResultPaired operator*(const C& c) const {return ResultPaired(this->left * c, this->right); } }; template <typename L, typename Traits = PairedTraits<L> > struct MultPaired : Paired<L, Traits> { typedef Paired<L, Traits> Base; typedef Paired<typename Traits::Result, typename Traits::ResultTraits> ResultPaired; MultPaired(typename Traits::Left& l, typename Traits::Right& r) : Base(l, r) {} template <typename A> ResultPaired operator*(const C& c) const {return ResultPaired(this->left * c, this->right); } }; template <typename L, typename Traits = PairedTraits<L> > struct ModPaired : Paired<L, Traits> { typedef Paired<L, Traits> Base; typedef Paired<typename Traits::Result, typename Traits::ResultTraits> ResultPaired; ModPaired(typename Traits::Left& l, typename Traits::Right& r) : Base(l, r) {} template <typename A> ResultPaired operator*(const C& c) {return ResultPaired(this->left % c, this->right); } }; template <typename L, typename Traits = PairedTraits<L> > struct DivPaired : Paired<L, Traits> { typedef Paired<Traits> Base; typedef Paired<typename Traits::Result, typename Traits::ResultTraits> ResultPaired; DivPaired(typename Traits::Left& l, typename Traits::Right& r) : Base(l, r) {} template <typename A> ResultPaired operator*(const C& c) const {return ResultPaired(this->left / c, this->right); } }; 

Paired children can return a result ( left βŠ™ c or left βŠ™ (right * c) , which basically makes *(const SourcePacket&, T) right associative), not paired. For instance:

 template <typename L, typename Traits = PairedTraits<L> > struct DivPaired : Paired<L, Traits> { typedef Paired<L, Traits> Base; MultPaired(typename Traits::Left& l, typename Traits::Right& r) : Base(l, r) {} template <typename A> Result operator*(const C& c) const {return this->left / (this->right * c); } }; 
+1
source

All Articles