Подтвердить что ты не робот

Макросменить оператор С++ new

Можно ли создавать макросы, чтобы заменить все формы operator new на перегрузки, содержащие дополнительные аргументы... скажем __FILE__ и __LINE__?

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

  • объектно-ориентированные макросы:

    #define new new(__FILE__, __LINE__)
    

    заменит объявления типа:

    A* a = new A();
    
  • и макрос функций:

    #define new(A) new (A, __FILE__, __LINE__)
    

    заменит объявления типа:

    A* a = new(std::nothrow) A();
    

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

#define new new(__FILE__, __LINE__)
#define new(A) new (A, __FILE__, __LINE__) // Error: "new" already defined

Поскольку я использую g++, я надеялся, что использование их синтаксиса variadic macros даст успех, но, к сожалению, нет. Следующее:

#define new(...) new(__FILE__, __LINE__, ## __VA_ARGS__)

соответствует только new(xyx) A(), а не new A().

Я знаю, что были написаны эссе о том, почему это невозможно, но я чувствую, что я настолько близок, что должно быть путь. Есть ли что-то очевидное, что мне не хватает?

4b9b3361

Ответ 1

Вот что я использую:

В файле new.cpp

const char* __file__ = "unknown";
size_t __line__ = 0;

void* operator new(size_t size) {
    void *ptr = malloc(size);
    record_alloc(ptr,__file__,__line__);
    __file__ = "unknown";
    __line__ = 0;
    return ptr;
}

void delete(void *ptr)
{
   unrecord_alloc(ptr);
   free(ptr);
}

Для компактности я оставляю другие определения new и delete. "record_alloc" и "unrecord_alloc" - это функции, которые поддерживают связанный список структуры, содержащий ptr, строку и файл).

в new.hpp

extern const char* __file__;
extern size_t __line__;
#define new (__file__=__FILE__,__line__=__LINE__) && 0 ? NULL : new

Для g++ "новый" расширяется только один раз. Ключом является "& & 0", что делает его ложным и приводит к использованию нового нового. Например,

char *str = new char[100];

расширяется препроцессором до

char *str = (__file__="somefile.c",__line__=some_number) && 0 ? NULL : new char [100];

Таким образом, записываются номер файла и строки и вызывается ваша пользовательская новая функция.

Это работает для любой формы new - при наличии соответствующей формы в new.cpp

Ответ 2

Вы должны проверить эту замечательную запись в блоге моим коллегой Кальвином. Недавно у нас была ситуация, когда мы хотели включить этот тип исправлений, чтобы связать утечки памяти с линией, выделенной ими в сборках диагностики/отладки. Это интересный трюк

http://blogs.msdn.com/calvin_hsia/archive/2009/01/19/9341632.aspx

Ответ 3

3.7.4 Динамическая длительность хранения

2 Библиотека предоставляет определения по умолчанию для глобальных функций распределения и освобождения. Некоторые функции глобального распределения и освобождения являются сменными (18.5.1). Программа С++ должна предоставлять не более одного определения сменной функции распределения или освобождения. Любое такое определение функции заменяет версию по умолчанию, предоставленную в библиотеке (17.6.4.6) [...]

17.6.4.6 Функции замены

  1. Программа на С++ может предоставить определение для любого из восьми распределений динамической памяти сигнатуры функций, объявленные в заголовке (3.7.4, раздел 18):

    • operator new (std:: size_t)
    • operator new (std:: size_t, const std:: nothrow_t &)
    • operator new [] (std:: size_t)
    • operator new [] (std:: size_t, const std:: nothrow_t &)
    • оператор delete (void *)
    • оператор delete (void *, const std:: nothrow_t &)
    • оператор delete [] (void *)
    • оператор delete [] (void *, const std:: nothrow_t &)

Надеюсь, что это разъясняет, что такое законная перегрузка, а что нет.

Это может представлять интерес для нескольких здесь:

#define delete cout <<  "delete called at: " << __LINE__ << " of " << __FILE__  << endl, delete 

using namespace std;

void *operator new(size_t size, ostream& o, char *f, unsigned l) {
    o << "new called at: " << l << " of " << f << endl;
    return ::new char[size];
}

int main() {
    int *a = new(cout, __FILE__, __LINE__) int;
    delete a;
}

Caveat Lector: то, что я делаю здесь, - это Bad Thing (TM) - перегрузка нового/удалить глобально.

Ответ 4

Вы не говорите, какой компилятор вы используете, но, по крайней мере, с GCC, вы можете переопределить новый и зарегистрировать адрес вызывающего абонента, а затем перевести его в файл/строку с помощью addr2line (или использовать библиотеку BFD для этого немедленно).

Ответ 5

Я нашел следующую библиотеку " nvwa", очень полезная для отслеживания утечек памяти new/delete - взгляните на файл "debug_new" для примеров или просто используйте его как есть.

Ответ 6

Что вы можете сделать, это перегрузить оператор new и получить там трассировку стека (конкретную платформу) и использовать информацию стека для вывода из того, откуда был вызван новый.

Ответ 7

Нет, нет способа.

Вы можете сделать это в плохие старые дни malloc()/free(), но не для new.

Вы можете заменить распределитель памяти глобальным переопределением оператора new, но вы не можете вводить специальные переменные, о которых вы говорите.