A C++ Macro to Stub Interface Methods
In my previous column, I showed how to create a class library in C++ that can be called from a macro, and presented a very simple function in the class library that adds braces for you, suitable for use after typing an if or for statement.
In this column, I show you a somewhat meatier piece of code: a macro for use with a class that implements one or more interfaces. When you run the macro, it adds empty function bodies for all the functions in the interface. I developed this as another function in the class library from the last column, so I won't repeat the instructions for setting up your projects and your macros, associating the macro with a toolbar button, or closing Visual Studio each time you rebuild the class library code.
Elements, Classes, Interfaces, and Bases
When you write a class in Visual C++, the Design Time Environment thinks of it as several things, including a class and a CodeElement. It has base classes, each of which is also a CodeElement. Your class, its bases, and even its functions can all be represented as objects with properties such as Name and Type—and your macros can manipulate those objects. For example, an object that represents a class has a method called AddFunction(), which actually adds functions to one of your classes, right from the macro. That's the heart of this macro: a loop that calls AddFunction() repeatedly to add each function that's in the interface your class implements. This generates the code inside your class, and it's quite fun to see.
I found the code pretty hard to write, and you're likely to find it hard to read, for three reasons:
- It contains parallel object hierarchies: one under the EnvDTE namespace with classes such as CodeElement, and one under the Microsoft::VisualStudio::VCCodeModel namespace with classes such as VCCodeElement. The functionality is not quite the same in the two hierarchies.
- Most of the functions return quite general types, or the properties have quite general types. For example, the Namespaces property of a code model is not a CodeNamespace or a VCCodeNamespace, but rather a CodeElement. That means the code contains a lot of static_cast<> and dynamic_cast<>. (Option Strict Off in Visual Basic hides a lot, doesn't it?) I cast to a Microsoft::VisualStudio::VCCodeModel class only when the methods or properties I want aren't in the equivalent class from the EnvDTE namespace.
- The arrays and collections returned from these functions are all one-based, and the Count property tells you how many items are in a collection.
Bear with me, though, because going through this exercise gives you more than just the ability to write C++ macros in C++. You can also learn how Visual Studio and its wizards think of your code, and how they work internally.
The general structure of the InsertMethods() function is, in pseudo-code:
Get a collection of the classes in the file you have open. For each class: * Get a collection of the bases, which includes interfaces. For each base: * Determine if it is an interface, and if so: * Get all the functions in the interface. For each function: * Determine if it is not yet implemented in this file, and if not: * Add the function to the class; * Get all the parameters to the function; * Add the parameters, one at a time, to the function.