Friday, December 31, 2010

The .NET Preprocessor - Part 2


The .NET Pre-Processor may be downloaded here.

The first debate was which language it should be written in. The candidates were C/C++, C# and Python. I really wanted to write it in C/C++, but the conveniences of C# and the .NET platform was just scoring too many points. No, I will not write managed C++ in Visual C++. I do not like the syntax. As for Python, the code would probably have been shorter, but it’s not a common install, and not as commonly used, I think. Related to that, Python got a boost when an actual Python came to my house a week ago. No, seriously. It’s the second one to come by in 6 months. I told my good friend Walter (Developer Security MVP) that it might have been a sign. But alas, it was C#.

DOTNETPP is a command-line tool that is largely designed to work with Visual Studio’s pre and post-build facilities. The settings are expected to be in an XML file, a sample of which is included in the download. Following is the usage:

Usage: DOTNETPP /p:"<config file path>" [/process|/cleanup]

/process: Process macro expansion

/cleanup: Cleanup expanded macro files

The config file path is specified by the /p: parameter. /process and /cleanup are actually mutually exclusive. If they both appear, /process will take priority. So basically, you will use /process in your Visual Studio .NET Project’s pre-build event, and /cleanup in your post-build event. Of course you can also manually run it to process and cleanup.

Macro Definition File

The macro definition file is basically a text file where you define your C Preprocessor macros. I will post some examples subsequent posts. DOTNETPP supports the following macro/preprocessor features:

- Non-parameterized macros

- Parameterized macros

- Token concatenation (##)

- Stringizing Operator (#)

- C++ style comments (//)

Since the macro definitions are in its own file, the line-continuation backslash ‘\’ is not required at the end of each line of the token list.

Source Files

In the source files which you want to use macros and have DOTNETPP process them, include the following syntax in said source file:

#include DOTNETPP

Since this syntax is not processed, it must be commented or excluded from compilation for the build to succeed. In the case of C#, you may use the following:

//#include DOTNETPP


#if false

#include DOTNETPP


The Catch

There is a catch. This is because this tool is NOT integrated into Visual Studio. This is what the problem is… the way DOTNETPP works is that it (obviously) expands the macros, and then writes it back into the source file. So the catch is, if any of the source files that are processed by DOTNETPP are open when the build runs (with DOTNETPP running in the pre/post-build events), the build WILL FAIL.

Why? This is because Visual Studio will ask if you want to reload the file as it has been edited outside of the source editor. The compilation would have started way before you’re able to say you want to reload the files. So if you reload and save the changed files, you will LOSE all your macros, and have the expanded code instead. This would be equivalent to setting <RestoreMacrosInSource> to FALSE in the config file.

Here’s the scenario:

1. Build project/solution

2. Build fails

3. Visual Studio asks if you want to reload the files

4. Reload the files and save

5. Build project/solution (this time it will succeed assuming you did not screw up the macro definitions)

This is a clearly a serious inconvenience to have to close all the source files with the macros before doing a build every time. This is a major con. Well, it’s not part of Visual Studio, tough. So maybe there is a way to work around this if the tool is integrated into Visual Studio. That would be something to look into for the future.

However, having thought about it, this problem probably isn’t necessarily a bad thing. I probably don’t actually want the macros to remain in the source as it can be cryptic. So, as a potentially fortunate consequence of the way Visual Studio works, DOTNETPP could work as a one-time-no-return macro expander. After all, one of the config settings is <RestoreMacrosInSource>true/false</RestoreMacrosInSource>. OK, so maybe this is a less ‘evil’ way. Expand the macros so they become the source code, and no more macros in the source.

Next I will write about the config file, but the elements should be quite obvious.

Here’s one of the main reasons I started this project:

#define FOR(a) for (int i = 0; i < a.Count; i++)

No comments:

Post a Comment