Проблема перехвата и сбора деталей / выделения объекта, когда пользователь вызывает оператора new - PullRequest
4 голосов
/ 24 ноября 2010

Я работаю над небольшим инструментом памяти, который отслеживает распределение и освобождение, размеры объектов, типы объектов и так далее.Метод, который я использую для отслеживания исходных файлов, номеров строк и типов объектов, работает следующим образом:

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

SourcePacket - это просто небольшой класс, который принимает const char * и int во время построения.Эти значения заполняются с помощью макросов __FILE__ и __LINE__.Тип объекта получается следующим образом:

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

p - указатель на вновь выделенный объект, тип которого обнаружен с использованием RTTI.При перегрузке оператора информация берется и сохраняется в трассировщике, а указатель передается программе.Дополнительная информация, такая как размер и адрес, указана в перегруженном операторе new.

Теперь эта настройка очень хорошо работает для меня.Он не работает для кода, который я не компилирую, естественно, но одна из лучших вещей - это то, что он хорошо работает с размещением новых вызовов, сделанных пользователем, которые не работали с использованием часто встречающихся

#define new new(__FILE__, __LINE__)

метод.Проблема, с которой я столкнулся, заключается в том, что если пользователь вызывает оператор new, программа просто не сможет скомпилироваться.Конечно, это потому, что макрос расширяется примерно так:

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

вместо

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

Я действительно не вижу никакого способа обойти это.Я мог бы, конечно, просто удалить новую процедуру SourcePacket * и просто позволить моему перегруженному оператору new собирать размер и адрес, но это побеждает большую часть назначения инструмента.

(Также, как примечание, я не пытаюсь создать Valgrind или что-то еще, и я знаю, что перегрузка глобальных операций может быть довольно хитрой. Это в основном для образовательных целей. Кроме того, я знаю,что специфичные для ОС функции могут быть использованы для обнаружения некоторой этой информации, но я бы хотел использовать стандарт C ++ только для того, чтобы он был кроссплатформенным и бит-независимым (x86, x64 и т. д.). Пока это работаетбезупречно для меня как в Linux, так и в Windows, выпусках обоих версий.)

К сожалению, нет никакого способа условно использовать тот или иной путь, в зависимости от того, является ли он просто новым (илиразмещение нового) или оператора нового.Не критично, что я заставлю это работать, но мне было бы интересно узнать, нашел ли кто-нибудь способ обойти это ограничение.

1 Ответ

1 голос
/ 25 ноября 2010

Нам нужно выражение, которое является допустимым, если с префиксом «operator», а когда нет.Это означает, что нам нужно определить оператор, который принимает SourcePacket.Могут быть и другие аргументы, но оказывается, что в этом нет необходимости.Унарный operator * будет работать хорошо:

const SourcePacket& operator *(const SourcePacket& sp)  {
    return sp;
}

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

Поскольку мы не можем полностью заключить оператор в скобки, существует вероятность ошибок при использовании во всех, кроме простейших выражений.* Чтобы решить эту проблему, нам нужно определить соответствующий оператор.Для возвращаемого типа вы можете определить иерархию шаблонных классов, которые соединяют левый аргумент с SourcePacket и являются коммутативными в правом аргументе ((a:A ⊙ b:SourcePacket) * c:C) = (a:A ⊙ c:C) * b:SourcePacket, где ⊙ - некоторый двоичный оператор C ++).Нечто похожее на следующее, но без ошибок, которыми оно, несомненно, обладаетчем в паре.Например:

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); }
};
...