Расширение макроса препроцессора остановки C++

Вот мой пример кода 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 в своем коде, а номера файлов и строк заполняются бесплатно. Насколько я знаю, нет другого способа добиться этой функциональности, кроме как определить макрос удаления. В этом суть проблемы. Макрос удаления дал мне мощный инструмент отладки, однако он удалил полезную языковую функцию, которую я мог использовать.

1. CAT_2 и _helper никогда не используются. 2. #define delete делает программу некорректной; определение ключевых слов как имен макросов не допускается.

Angew is no longer proud of SO 08.05.2019 19:07

Я знаю, что это технически противоречит стандарту, но есть ли способ, который я могу сделать, чтобы заставить его работать? Предположим, удаление #define delete невозможно.

lufthansa747 08.05.2019 19:15

Мне кажется, что это проблема XY. зачем вам это?

HolyBlackCat 08.05.2019 19:17

обновлено, чтобы, надеюсь, решить вашу проблему

lufthansa747 08.05.2019 19:22

"Я понимаю, что удаление «#define delete» решит проблему, но мне нужно это определить там" - почему? Есть ли причина, по которой вы просто не перегружаете новый оператор и удалить оператор вместо этого?

Remy Lebeau 08.05.2019 20:41

@RemyLebeau Я предполагаю, что причина в том, что, оказавшись внутри operator delete, у вас больше нет доступа к исходному файлу и строке, где находится выражение delete.

Angew is no longer proud of SO 08.05.2019 21:13

@Angew, вероятно, да, хотя это можно решить, если оператор извлечет адрес возврата из стека вызовов, а затем сравнит его с выводом отладки компилятора, чтобы определить исходный файл и номер строки.

Remy Lebeau 08.05.2019 21:17

@RemyLebeau извините за поздний ответ, но ваше предположение верно. Проблема с решением, которое вы предлагаете, заключается в том, что я не могу распечатать номер файла и строки во время выполнения. У меня есть такая настройка, что когда я закрываю свою программу, она запускает средство проверки утечек памяти и, если обнаруживает какие-либо утечки, распечатывает номер файла/строки нового вызова. Я уже перегрузил оператор new/delete для ввода номера файла/строки, я использую макрос, чтобы гарантировать, что пользователю не нужно добавлять ЛИНИЯ, ФАЙЛ к каждому вызову new/delete

lufthansa747 10.09.2020 07:34

@ lufthansa747 «Проблема с решением, которое вы предлагаете, заключается в том, что я не могу распечатать номер файла и строки во время выполнения.» - вы можете, если ваш компилятор сохраняет отладочную информацию в файле, который вы можете загрузить во время выполнения.

Remy Lebeau 10.09.2020 07:56

честно, пытался избежать такого решения. Но это правильный подход. Макроподход показался мне немного чище и проще

lufthansa747 10.09.2020 08:12
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
6
10
1 141
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

У вас нет возможности создать токен предварительной обработки, который является именем объектно-подобного макроса из расширения макроса. Соответствующий раздел 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», он не зацикливался бы никоим образом, а просто оставил бы «удалить». Однако он, конечно, не может в особом случае макрос токена расширяться по-другому. Если бы он использовал такую ​​функцию, как макрос, что-то было бы возможно, но это не зафиксировало бы удаление массива.

Michael Speer 08.05.2019 19:42

@MichaelSpeer Даже это delete не расширяется повторно; проблема не в каком-либо цикле, а в том, что MyCustomDelete не является синтаксически допустимым, где delete используется для обозначения удаленной функции.

Angew is no longer proud of SO 08.05.2019 19:46

Вероятнее всего. Я просто хотел убедиться, что информация о том, как расширяются макросы, была точной.

Michael Speer 08.05.2019 19:56
Ответ принят как подходящий

То, что вы пытаетесь сделать, невозможно, поскольку Ответ Майкла Керхера утверждает: #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 в покое, чтобы его можно было использовать надлежащим образом.

Проблема с этим заключается в том, что кодировщик должен помнить об использовании этого макроса удаления.

lufthansa747 08.05.2019 21:49

Другие вопросы по теме