Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Stop Preprocessor Macro Expansion

Here is my example code 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);
};

The godbolt example is setup to display the preprocessor output. The goal is that at the end of the preprocessor pass i want the output code to be

class A {
    A() = delete;
};

currently "ThisShouldNotshowUp" is displayed there instead. I thought the use of the ## operator would stop the preprocessor from reexpanding but it did not.

I realize removing the "#define delete" would solve the problem but I need this define there. The reason I have created a macro with the same name as delete is because I want to be able to track the news and deletes, and If a memory leak occurs I can see what line of code aloced it. This macro thus means I can continue to use the keyword delete in my code and the File and line numbers get filled in for free. As far as i know there is no other way to achieve this functionailty except by defined a delete macro. This is the crux of the problem. The delete macro has given me a powerful debug tool however it has removed a useful language feature for me to use.

like image 696
lufthansa747 Avatar asked May 08 '19 16:05

lufthansa747


People also ask

What is macro expansion in preprocessor?

Macro expansion overviewThe preprocessor maintains a context stack, implemented as a linked list of cpp_context structures, which together represent the macro expansion state at any one time. The struct cpp_reader member variable context points to the current top of this stack.

What does ## mean in C preprocessor?

This is called token pasting or token concatenation. The ' ## ' preprocessing operator performs token pasting.

Are preprocessor directives used for macro expansion?

All preprocessor directives are interpreted before macro expansion begins, so no, you cannot have a macro expand into an #include directive and have it be interpreted as such. Instead, it will be interpreted as (erroneous) C++ code.

What is ## in C macro?

The double-number-sign or token-pasting operator (##), which is sometimes called the merging or combining operator, is used in both object-like and function-like macros. It permits separate tokens to be joined into a single token, and therefore, can't be the first or last token in the macro definition.


2 Answers

You have no chance in creating a preprocessing token that is the name of an object-like macro from expanding a macro. The relevant section of n3337 is [cpp.rescan]. I quote a shortened part of the first paragraph in it.

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.

Nonwithstanding the problem, that delete is technically forbidden to be a macro name, there is no way to prevent the macro name to be recognized while rescanning.

You probably mixed up the fact that ## operator does use it's parameters without expansion with the idea that the result of ## doesn't undergo macro expansion.

like image 180
Michael Karcher Avatar answered Sep 23 '22 07:09

Michael Karcher


What you're trying to do is not possible, as Michael Karcher's answer states: #define delete already makes the program ill-formed, and expanding an object-like macro (outside its own expansion) cannot be avoided.

However, for your particular use case detailed in the question, a workaround is possible. You could put your #define delete into a header file (let's call it debug_delete.hxx), like this:

#ifdef delete
# undef delete
#endif
#define delete MyCustomDelete(__FILE__, __LINE__), delete

Then, create another header file (let's call it normal_delete.hxx):

#ifdef delete
# undef delete
#endif

Note in particular that there is no mechanism in these headers to prevent multiple inclusion; in fact, we want them includable an arbitrary number of times.

Then, wrap code which must use = delete; in appropriate #include directives:

class A {
#include "normal_delete.hxx"
    A() = delete;
#include "debug_delete.hxx"
    ~A() { delete p; }
};

(Yes, it's ugly, but what you're doing is sort of ugly in the first place, so ugly code may be required to make it work).

like image 36
Angew is no longer proud of SO Avatar answered Sep 22 '22 07:09

Angew is no longer proud of SO