Вот мой пример кода https://godbolt.org/z/VKgKik
#define delete MyCustomDelete(__FILE__, __LINE__), delete
#define CAT(X,Y) CAT2(X,Y)
#define CAT2(X,Y) X##Y
#define CAT_3(X,Y,Z) CAT(X,CAT(Y,Z))
class A {
A() = CAT_3(de,le,te);
};
Пример godbolt настроен на отображение вывода препроцессора. Цель состоит в том, чтобы в конце прохода препроцессора я хотел, чтобы выходной код был
class A {
A() = delete;
};
в настоящее время вместо этого отображается «ThisShouldNotshowUp». Я думал, что использование оператора ## остановит повторное расширение препроцессора, но это не так.
Я понимаю, что удаление «#define delete» решит проблему, но мне нужно это определить. Причина, по которой я создал макрос с тем же именем, что и для удаления, заключается в том, что я хочу иметь возможность отслеживать новости и удаления, а в случае утечки памяти я могу увидеть, какая строка кода ее выделила. Таким образом, этот макрос означает, что я могу продолжать использовать ключевое слово delete в своем коде, а номера файлов и строк заполняются бесплатно. Насколько я знаю, нет другого способа добиться этой функциональности, кроме как определить макрос удаления. В этом суть проблемы. Макрос удаления дал мне мощный инструмент отладки, однако он удалил полезную языковую функцию, которую я мог использовать.
Я знаю, что это технически противоречит стандарту, но есть ли способ, который я могу сделать, чтобы заставить его работать? Предположим, удаление #define delete невозможно.
Мне кажется, что это проблема XY. зачем вам это?
обновлено, чтобы, надеюсь, решить вашу проблему
"Я понимаю, что удаление «#define delete» решит проблему, но мне нужно это определить там" - почему? Есть ли причина, по которой вы просто не перегружаете новый оператор и удалить оператор вместо этого?
@RemyLebeau Я предполагаю, что причина в том, что, оказавшись внутри operator delete, у вас больше нет доступа к исходному файлу и строке, где находится выражение delete.
@Angew, вероятно, да, хотя это можно решить, если оператор извлечет адрес возврата из стека вызовов, а затем сравнит его с выводом отладки компилятора, чтобы определить исходный файл и номер строки.
@RemyLebeau извините за поздний ответ, но ваше предположение верно. Проблема с решением, которое вы предлагаете, заключается в том, что я не могу распечатать номер файла и строки во время выполнения. У меня есть такая настройка, что когда я закрываю свою программу, она запускает средство проверки утечек памяти и, если обнаруживает какие-либо утечки, распечатывает номер файла/строки нового вызова. Я уже перегрузил оператор new/delete для ввода номера файла/строки, я использую макрос, чтобы гарантировать, что пользователю не нужно добавлять ЛИНИЯ, ФАЙЛ к каждому вызову new/delete
@ lufthansa747 «Проблема с решением, которое вы предлагаете, заключается в том, что я не могу распечатать номер файла и строки во время выполнения.» - вы можете, если ваш компилятор сохраняет отладочную информацию в файле, который вы можете загрузить во время выполнения.
честно, пытался избежать такого решения. Но это правильный подход. Макроподход показался мне немного чище и проще





У вас нет возможности создать токен предварительной обработки, который является именем объектно-подобного макроса из расширения макроса. Соответствующий раздел n3337 — [cpp.rescan]. Я цитирую в нем сокращенную часть первого абзаца.
After all parameters in the replacement list have been substituted and
#and##processing has taken place [...]. Then the resulting preprocessing token sequence is rescanned [...] for more macro names to replace.
Несмотря на проблему, что delete технически запрещено быть именем макроса, нет способа предотвратить распознавание имени макроса при повторном сканировании.
Вероятно, вы перепутали тот факт, что оператор ## использует свой параметры без расширения, с идеей, что результат оператора ## не подвергается макрорасширению.
макросы, включающие собственное имя, не раскрывают его самостоятельно. они делают это здесь, потому что он передает это через ряд других расширений макросов во вложенных CAT. его макрос не расширяется до «ThisShouldNotshowUp», а «MyCustomDelete («main.c», 10), удалить». если бы макрос был «#define delete delete», он не зацикливался бы никоим образом, а просто оставил бы «удалить». Однако он, конечно, не может в особом случае макрос токена расширяться по-другому. Если бы он использовал такую функцию, как макрос, что-то было бы возможно, но это не зафиксировало бы удаление массива.
@MichaelSpeer Даже это delete не расширяется повторно; проблема не в каком-либо цикле, а в том, что MyCustomDelete не является синтаксически допустимым, где delete используется для обозначения удаленной функции.
Вероятнее всего. Я просто хотел убедиться, что информация о том, как расширяются макросы, была точной.
То, что вы пытаетесь сделать, невозможно, поскольку Ответ Майкла Керхера утверждает: #define delete уже делает программу неправильной, и нельзя избежать расширения объектно-подобного макроса (за пределами его собственного расширения).
Однако для вашего конкретного варианта использования, подробно описанного в вопросе, возможен обходной путь. Вы можете поместить свой #define delete в заголовочный файл (назовем его debug_delete.hxx), например:
#ifdef delete
# undef delete
#endif
#define delete MyCustomDelete(__FILE__, __LINE__), delete
Затем создайте еще один заголовочный файл (назовем его normal_delete.hxx):
#ifdef delete
# undef delete
#endif
Обратите внимание, в частности, что в этих заголовках нет механизма для предотвращения множественного включения; на самом деле, мы хотим, чтобы они включались произвольное количество раз.
Затем оберните код, который должен использовать = delete;, в соответствующие директивы #include:
class A {
#include "normal_delete.hxx"
A() = delete;
#include "debug_delete.hxx"
~A() { delete p; }
};
(Да, это уродливо, но то, что вы делаете, в первую очередь уродливо, поэтому для его работы может потребоваться уродливый код).
Предположительно, вы хотите использовать макрос, чтобы включать и выключать отслеживание удаления. Если вы используете это только в своем исходном коде и не пытаетесь настроить его для преобразования существующего C++, вы можете использовать макрос, похожий на функцию, чтобы выполнить дополнительное отслеживание, которое вы хотите.
#define TRACK_DELETES 0
#if TRACK_DELETES
#define DELETE( a ) \
do { MyCustomDelete( __FILE__, __LINE__ ); delete (a); } while (0)
#define DELETEALL( a ) \
do { MyCustomDelete( __FILE__, __LINE__ ); delete [] (a); } while (0)
#else
#define DELETE( a ) do { delete (a) ; } while(0)
#define DELETEALL( a ) do { delete [] (a) ; } while(0)
#endif
int main(){
DELETE( A );
DELETEALL( B );
return 0;
}
Посмотрите, делает ли это то, что вы хотите, с TRACK_DELETES, установленным на 0 или 1 под gcc -E.
Вы можете оставить голое ключевое слово delete в покое, чтобы его можно было использовать надлежащим образом.
Проблема с этим заключается в том, что кодировщик должен помнить об использовании этого макроса удаления.
1.
CAT_2и_helperникогда не используются. 2.#define deleteделает программу некорректной; определение ключевых слов как имен макросов не допускается.