1

For a source level analysis, I need to expand macros except those #include, is it possible?

For example, in the following snippet, I only hope assert to be expanded as __assert_fail, rather than including assert.h and expand assert.

#include <assert.h>

int main(void) {
  assert(0); // expand this
  return 0;
}
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Hongxu Chen
  • 5,240
  • 2
  • 45
  • 85
  • Why do you want to do that? – edtheprogrammerguy Dec 02 '13 at 01:37
  • Since the definition for assert is in assert.h how do you propose to expand assert without including assert.h? – shf301 Dec 02 '13 at 01:41
  • @shf301: That's what he's asking. – Benjamin Lindley Dec 02 '13 at 01:42
  • I think he's saying he only wants what's from the includes if it's used in the application. Compilers and linkers do that already, right? – Fiddling Bits Dec 02 '13 at 01:42
  • @self. I hope to do some transforms firstly for the source code, but I hope to expand some `special` macros and keep others. – Hongxu Chen Dec 02 '13 at 01:45
  • Are you trying to inject custom definitions of macros that should be used instead of the included definitions for a set of chosen ones? – Till Dec 02 '13 at 01:46
  • @BitFiddlingCodeMonkey Exactly. I will still adapt the regular process of compiling and linking later after some transformations for the source code. – Hongxu Chen Dec 02 '13 at 01:46
  • There is no way to do that as far as I know. Maybe possible if you do it the other way around. – Xiaolei Zhu Dec 02 '13 at 01:47
  • Write your own C preprocessor that just expands the bits you want to expand. The standard one won't do what you want. – Joe Z Dec 02 '13 at 01:49
  • @Till I just don't need to expand "#include"s, but keep other macros expanded. – Hongxu Chen Dec 02 '13 at 01:51
  • I don't fully understand what you want, but you may be looking for [unifdef](http://dotat.at/prog/unifdef/). – zwol Dec 02 '13 at 01:51
  • @JoeZ Ok, seems that I have to do that. – Hongxu Chen Dec 02 '13 at 01:52
  • @Zack Thanks for showing me the link, I think it's illuminating. – Hongxu Chen Dec 02 '13 at 01:54
  • 1
    Ow - so what you want is partial expansion. That sounds like http://stackoverflow.com/questions/6482321/partially-preprocess-a-c-or-c-source-file – Till Dec 02 '13 at 01:55
  • @JoeZ "Write your own C preprocessor..." That's not easy to do. Check my bio for tools that have a full controllable preprocessor; you can tell it which specific items to expand. – Ira Baxter Dec 02 '13 at 01:59
  • @IraBaxter: That's true for a full preprocessor. I guess it depends on what the goal is. If it's just to do a controlled expansion of a few macros in a known way, it could be a simple perl script with a handful of regexps. Otherwise, the tools in your profile may be useful. – Joe Z Dec 02 '13 at 02:05

2 Answers2

5

If you are using gcc you use -E as a switch to just run the preprocessor on the file. This will expand out the all macros that are found within the file, but leave the rest of the source untouched. Further, you can then unifdef any macros that get expanded within your source using -U. See GCC preprocessor.

So, in this:

#include <assert.h>

int main(int argc, char * argv[])
{
  assert(0);
  return 0;
}

Invoking:

g++ -E assert_test.cpp -o assert_test.ii

Returns assert_test.ii:

# 1 "assert_test.cpp"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 30 "/usr/include/stdc-predef.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/predefs.h" 1 3 4
# 31 "/usr/include/stdc-predef.h" 2 3 4
# 1 "<command-line>" 2
# 1 "assert_test.cpp"
# 1 "/usr/include/assert.h" 1 3 4
# 36 "/usr/include/assert.h" 3 4
# 1 "/usr/include/features.h" 1 3 4
# 371 "/usr/include/features.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 1 3 4
# 385 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/wordsize.h" 1 3 4
# 386 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 2 3 4
# 372 "/usr/include/features.h" 2 3 4
# 395 "/usr/include/features.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/gnu/stubs.h" 1 3 4
# 10 "/usr/include/x86_64-linux-gnu/gnu/stubs.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/gnu/stubs-64.h" 1 3 4
# 11 "/usr/include/x86_64-linux-gnu/gnu/stubs.h" 2 3 4
# 396 "/usr/include/features.h" 2 3 4
# 37 "/usr/include/assert.h" 2 3 4
# 67 "/usr/include/assert.h" 3 4
extern "C" {


extern void __assert_fail (const char *__assertion, const char *__file,
      unsigned int __line, const char *__function)
     throw () __attribute__ ((__noreturn__));


extern void __assert_perror_fail (int __errnum, const char *__file,
      unsigned int __line, const char *__function)
     throw () __attribute__ ((__noreturn__));




extern void __assert (const char *__assertion, const char *__file, int __line)
     throw () __attribute__ ((__noreturn__));


}
# 2 "assert_test.cpp" 2

int main(int argc, char * argv[])
{
  ((0) ? static_cast<void> (0) : __assert_fail ("0", "assert_test.cpp", 5, __PRETTY_FUNCTION__));
  return 0;
}

So, at least on my system:

assert(0);

expands out to the following:

((0) ? static_cast<void> (0) : __assert_fail ("0", "assert_test.cpp", 5, __PRETTY_FUNCTION__));

where __assert_fail is an externally defined function (and usually contained with libc); So to answer your question you can than just use your favourite text replace (sed/awk/perl) utility and expand out assert(xyz); into ((xyz) ? static_cast<void> (xyz) : __assert_fail ("xyz", "<file_name>", 5, __PRETTY_FUNCTION__));

Community
  • 1
  • 1
kingtorus
  • 953
  • 12
  • 15
1

You'll have to write your own pre-preprocessor. If you follow the simple rule (which most people do) of keeping your includes at the top, it shouldn't be too difficult.

  • Read the source file in, line by line, and figure out where the last include directive is.
  • Create a temporary file which is an exact duplicate, except with an extra marker line to identify where the include directives end, and where the rest of the code begins.
  • Send that temporary file through the C preprocessor.
  • Find the marker line in the preprocessed file, and create a new file replacing everything before it with the include directives that were in the original file.

In fact, this should all be fairly easy to do in a shell script with command line tools that come with your OS.

If you don't follow the "includes at the top rule", it will only be a little more difficult. You can insert marker lines before and after each include directive (or each set of consecutive include directives), keeping track of which directives were between which markers.

Benjamin Lindley
  • 101,917
  • 9
  • 204
  • 274