Search code examples
c++regexmfcautomated-refactoringclang-cl

Refactor MFC message maps to include fully qualified member function pointers


I have a codebase where MFC message maps are written in this form:

BEGIN_MESSAGE_MAP(SomeForm, BaseForm)
    ON_COMMAND(CID_ButtonAction, OnButtonAction)
END_MESSAGE_MAP()

This compiles just fine in MSVC. When I want to compile the same code in Clang I get a call to non-static member function without an object argument error because OnButtonAction is not a correct form for specifying a member function pointer. The code can be easily fixed:

    ON_COMMAND(CID_ButtonAction, &SomeForm::OnButtonAction)

or we can use ThisClass typedef from the BEGIN_MESSAGE_MAP() macro:

    ON_COMMAND(CID_ButtonAction, &ThisClass::OnButtonAction)

So far so good...the only problem is that I have hundreds of these message map entries in a lot of separate files. Is there any tool that can fix this? Some obscure Visual Studio magic? Or is it possible to use replacement via regex here?


Solution

  • In the end I came up with a sed command that I ran from MinGW:

    sed -b -i -re '/^BEGIN_MESSAGE_MAP/,/^END_MESSAGE_MAP/{/(BEGIN_MESSAGE_MAP|\/\/)/!s/(.*),\s{0,}/\1, \&ThisClass::/;}' *.cpp
    

    To explain what it does:

    • -b treat files as binary (optional, to keep line endings in Windows)*
    • -re support extended regular expressions
    • -i in-place substitution
    • /^BEGIN_MESSAGE_MAP/,/^END_MESSAGE_MAP/ match only text between these two strings
    • /!s substitution command that will ignore whatever you match before it
    • /\(BEGIN_MESSAGE_MAP\|\/\/\)/ matches line beginnings to ignore (either the first line of the message map or commented-out lines)
    • /(.*),\s{0,}/\1, \&ThisClass::/ substitutes the last comma on a line followed by 0+ whitespaces with , &ThisClass::

    Sample input:

    BEGIN_MESSAGE_MAP(SomeForm, BaseForm)
        ON_COMMAND(CID_ButtonAction, OnButtonAction)
        ON_NOTIFY_EX(CID_Notify, 0, OnNotify)
    END_MESSAGE_MAP()
    

    Output:

    BEGIN_MESSAGE_MAP(SomeForm, BaseForm)
        ON_COMMAND(CID_ButtonAction, &ThisClass::OnButtonAction)
        ON_NOTIFY_EX(CID_Notify, 0, &ThisClass::OnNotify)
    END_MESSAGE_MAP()
    

    This worked nicely, for ~500 files I only had to make two manual adjustments where the class method membership notation was already used. The sed command could be adjusted to account for this (e.g. check if the last comma on the line was followed by &) but this was just good enough for my purposes.

    EDIT - added -b option. This treats the files as binary. On Windows this prevents replacing original newline characters by Unix ones - without this option enabled the git diff for any processed file will look like the whole file has been deleted and added again.