Click here to Skip to main content
Click here to Skip to main content
Technical Blog

A macro to create a C++ implementation from a header declaration

, 19 Dec 2011 CPOL
Rate this:
Please Sign up or sign in to vote.
From the header declaration, this macro opens up the .cpp file and creates the skeleton so you can begin to add the implementation. This makes adding functions a breeze, so now there really is no excuse for having horribly unfactored code.

The second macro in this mini-series is a highly useful one. From the header declaration, it opens up the .cpp file and creates the skeleton so you can begin to add the implementation. This makes adding functions a breeze, so now there really is no excuse for having horribly unfactored code. It uses the macro to flip to the source file, so make sure you check out that article if you want to know how that one works.

How this macro will work is like this:

  1. You type the declaration into your header file
  2. You run the macro
  3. The function skeleton will be created and added to your .cpp file, read for you to begin typing into the body

First, we’re going to write a function to query the code model and get the function the cursor is on.

Private Function GetFunction() As EnvDTE.CodeFunction

    Dim textSelection As EnvDTE.TextSelection
    Dim codeFunction As EnvDTE.CodeFunction

    textSelection = DTE.ActiveWindow.Selection

    codeFunction = textSelection.ActivePoint.CodeElement(vsCMElement.vsCMElementFunction)

    Return codeFunction

End Function

Here, we’re just finding the position of the cursor in the window, and finding the function that is under it. CodeFunction is a Visual Studio COM object that allows us to explore the code we have written.

I’ll show you the entire macro first if you just want to copy and paste, and then I’ll go through it step-by-step.

Sub CreateImplementation()
    Dim func As EnvDTE.CodeFunction = GetFunction()

    Dim params As StringBuilder = New StringBuilder()

    Dim item As CodeParameter
    For Each item In func.Parameters
        If (params.Length > 0) Then
            params.Append(", ")
        End If

        params.AppendFormat("{0} {1}", item.Type.AsFullName(), item.FullName)
    Next

    ' Build up the line from the function
    Dim funcBody As StringBuilder
    funcBody = New StringBuilder()

    funcBody.AppendFormat("{0} {1}({2})", _
         func.Type.AsFullName(), func.FullName, params.ToString())

    If (func.FunctionKind And vsCMFunction.vsCMFunctionConstant) Then
        funcBody.Append(" const")
    End If

    funcBody.AppendLine()
    funcBody.AppendLine("{")
    funcBody.Append("}")

    ' Move to cpp file
    ToggleBetweenHeaderAndSource()

    ' Move to end of document
    DTE.ActiveDocument.Selection.EndOfDocument()
    DTE.ActiveDocument.Selection.NewLine(2)

    ' Insert the function
    DTE.ActiveDocument.Selection.Text = funcBody.ToString()

    ' Move cursor into function
    DTE.ActiveDocument.Selection.LineUp()
    DTE.ActiveDocument.Selection.NewLine(1)
End Sub

You can stop copying now. Here’s the analysis:

Sub CreateImplementation()
Dim func As EnvDTE.CodeFunction = GetFunction()

Here we create the function to get a function, as mentioned above. Stop me if I’m going to fast.

Dim params As StringBuilder = New StringBuilder()

Dim item As CodeParameter
For Each item In func.Parameters
    If (params.Length > 0) Then
        params.Append(", ")
    End If

    params.AppendFormat("{0} {1}", item.Type.AsFullName(), item.FullName)
Next

Here is an interesting bit. We then query the CodeFunction object. We peel each parameter from the Parameters collection, and build up a string based on the type and the name of each one.

So if our function prototype is void EatBiscuits(int amount, bool unwrapFirst);, we’re currently building up the int amount, bool unwrapFirst part.

Dim funcBody As StringBuilder
funcBody = New StringBuilder()

funcBody.AppendFormat("{0} {1}({2})", _
  func.Type.AsFullName(), func.FullName, params.ToString())

Next we find the function name and its return type, and build that part of the string up. Then we add the parameters that were discovered earlier. The key part to this is the func.FullName. This will find the class name(s) and namespace(s) so we are fully qualifying the implementation function name.

We can also discard qualifiers suck as virtual and static.

If (func.FunctionKind And vsCMFunction.vsCMFunctionConstant) Then
   funcBody.Append(" const")
End If

But what we do need to check for is for const member functions, and append const to it if that if needed.

funcBody.AppendLine()
funcBody.AppendLine("{")
funcBody.Append("}")

And here the function body is created. You can tweak this if you want the { at the end of your function declaration instead of on a new line, but that all depends on if you’re strange or not. I like to follow the ethereal WebBiscuit Coding Standards and give the curly braces a line of their own.

ToggleBetweenHeaderAndSource()

DTE.ActiveDocument.Selection.EndOfDocument()
DTE.ActiveDocument.Selection.NewLine(2)

Now we call the macro to flip to cpp definition, move to the end of the document and pop in a couple of blank lines. I admit the two here is completely arbitrary and maybe flawed. If you have 20 blank lines at the end of your file this is going to start 22 lines down. Maybe you can improve this.

DTE.ActiveDocument.Selection.Text = funcBody.ToString()

DTE.ActiveDocument.Selection.LineUp()
DTE.ActiveDocument.Selection.NewLine(1)

Last bit now, and the most satisfying. We place our function body string into the current location in the cpp file. We then move the cursor up one line (because the cursor is currently at one character past the closing brace). And then one blank line is inserted, so typing can begin directly into this body.

Time saved:  at least 10 seconds
Time spent writing macro and blog article:  ages
Biscuits eaten:  lots

I am not complaining. I learned a lot and this macro is seriously useful. Those few seconds can be crucial when you’re in the programming zone!

Things I don’t like

If you have a class in a namespace, generating the full name will qualify it as: namespace::classname::functionname. I see that Visual Assist does the same though, so maybe that is okay.

Limitations

  • It doesn’t yet work cleanly for constructors or destructors. It puts a void at the beginning. Ideas welcome.
  • The macro does not work if you are writing a stand-alone function. Currently the function definition has to be in a class. We’ll fix that in a later installment. 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

WebBiscuit
Software Developer Web Biscuit
United Kingdom United Kingdom
At Web Biscuit, you can find software, articles, a good dollop of quality and an unhealthy obsession over biscuits.
Website: http://www.webbiscuit.co.uk
Twitter Watch: http://twitter.com/WebBiscuitCoUk

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.141216.1 | Last Updated 19 Dec 2011
Article Copyright 2011 by WebBiscuit
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid