3

I'm searching for a Windows GUI/command line tool or Visual Studio plug-in where I can automate the extraction of C++ code blocks, add some refactoring/renaming steps and insert it into some other C++ file. Once this is setup I want to keep the code blocks source/destination in sync and/or check for diffs.

EDIT: I'm trying to keep things in my project as simple as possible. So I'm hoping for a solution with a low level of complexity. I would prefer a solution utilizing several single task tools (maybe already shipped with my OS/IDE/SCM) over a feature-rich multi-task tool which is build to handle much more.

An Example

To give a very simple example - and I'm not sure if the tool in question would even need to understand the C++ syntax for doing something like this:

OldClass.cpp

class OldClass
{
public:
   ...
   void MyFunctionToReuse()
   {
       ...
       // Start OldClass::MyFunctionToReuse() block
       mVarOldName = ...;
       // End OldClass::MyFunctionToReuse() block
       ...
   }
};

NewClass.cpp

class NewClass
{
public:
   ...
   void SomeFunction()
   {
       ...
       // Start OldClass::MyFunctionToReuse() block
       // file OldClass.cpp, line 100 - 110
       // renamed mVarOldName to mVarNewName
       mVarNewName = ...;
       // End OldClass::MyFunctionToReuse() block
       ...
   }
};

The possibilities I'm currently seeing

I see four possible ways of doing this:

  1. Manually extract the code I want to reuse into C++ templates/classes/functions
  2. Use C++ refactoring tools with a macro recording feature
  3. Use a transpiler with a set of rules to convert OldClass to NewClass
  4. A text tool only based approach searching for keywords in comments (copy/rename/diff/insert)

The Rationale

I have a long lasting model/view/controller C++ project where the view part is now to be exchanged/moved into a separate application. The old code base is to 80% reusable, but I can't take the classes as they currently are. So I'm now at a point where I branch from my old project to start on the new code.

But the old project will still be to maintained for several years. So if I start with the necessary changes for / refactoring of the new project, I'm searching for something to keep both code streams/branches in sync (mainly for bugfixes).

And huge changes to the old product are only the last resort, because it would mean - even with having a good code coverage with unit tests - retesting major parts of the currently shipping product.

What I've looked into so far

The feature lists of

  • (I'm the guy behind DMS). Why do you think these are "oversized"? C++ is an incredibly complex language. If you want to manipulate it mechanically, the mechanism has to somehow handle all the complexity, or it won't do what you want, or it will do it wrong. How would you propose that a simpler mechanism exist? [PS: DMS allows you to write source-to-source transformation rules and run them in an repeateable order; that's essentially what you are looking for as "macro recording"] – Ira Baxter Nov 11 '15 at 20:28
  • @IraBaxter I admit that part of my question is philosophical. And I'm a follower of the KISS principle (I will update the question accordingly). That said I'm shying at using DMS because of it's very impressive feature-set. The introduction of complex tools to your project do literally come with a price (purchase, train, maintain). So in case of a complex problem I tend to split the problem into less complex parts rather then going for more complex tools to handle everything. – Florian Nov 12 '15 at 9:31
  • I absolutely agree that using a complex tool has a price, and it is good that you understand that. All other things being equal you want to avoid it. The bottom line question is whether you have a correspondingly difficult problem with sufficient justification. Feel free to explore the alternatives. If your problem involves any explicit code manipulation I doubt will be able to avoid something like DMS, and so what is left is deciding how important it is to implement your vision. – Ira Baxter Nov 12 '15 at 10:17
  • @IraBaxter Thanks for the advice. And yes, I think in the end - if I want to keep the manipulation steps simple - everything will depend on having only localized/minimal code changes. – Florian Nov 12 '15 at 11:54
  • It sounds like something you should not do. Keep the implementation and write an adapter class or similar instead. – Thomas Weller Aug 16 '16 at 18:39
1

I ended up using Visual Studio's pre-compiler to translate my source files via macro definitions. In a second step I'm using PowerShell to replace/add some special formatting characters that the pre-compiler would remove/ignore.

Since I'm using CMake, here is the script I've tested:

cmake_minimum_required(VERSION 2.8.9)

project(CppTranslationDemo)

if(NOT MSVC_VERSION GREATER 1699)
    message(FATAL_ERROR "Only tested with VS2012 or above")
endif()

set(
    inFiles
    OldClass.cpp
)
unset(outFiles)

find_program(
    POWERSHELL_EXE
        NAMES powershell.exe
        PATHS C:/Windows/System32
        PATH_SUFFIXES WindowsPowerShell/v1.0
)
if (NOT POWERSHELL_EXE)
    message(FATAL_ERROR "Couldn't find powershell.exe")
endif()

foreach(_file IN ITEMS ${inFiles})
    file(TO_NATIVE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" _file_native_path)
    file(TO_NATIVE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/TranslationMacros.h" _macro_file_native_path)
    file(TO_NATIVE_PATH "${CMAKE_CXX_COMPILER}" _compiler_native_path)

    get_filename_component(_file_name "${_file}" NAME)
    get_filename_component(_file_ext "${_file}" EXT)

    string(REPLACE "Old" "New" _file_name "${_file_name}")
    set(_out_file "${CMAKE_BINARY_DIR}/${_file_name}")

    set(
        _powershell_cmd_list
        "$i = 1;"
        # NOTE: increase column width to prevent word wrap after 80 chararcters
        "$Host.UI.RawUI.BufferSize = New-Object Management.Automation.Host.Size (500, 25);"
        "$nl = [Environment]::NewLine;"
        "& '${_compiler_native_path}' /nologo /EP /C /FI${_macro_file_native_path} ${_file_native_path}"
        "|"
        "Foreach-Object"
        "{"
            "$_"
            "-replace '@NL@', $nl"
            "-replace '@TAB@', '    '"
            "-replace '@H@', '#'"
            "-replace '@C@', '//'"
            "-replace '@CB@', '/*'"
            "-replace '@CE@', '*/'"
            "-replace '@.*@', ''"
        "}"
        "|"
        "where"
        "{"
            "if ($_ -eq '')"
            "{" 
                "$i++;" 
            "}" 
            "else"
            "{" 
                "$i = 0;" 
            "}"
            "($i -lt 2)"
        "};"
        "exit $LASTEXITCODE"
    )
    string(REGEX REPLACE ";([^;])" " \\1" _powershell_cmd "${_powershell_cmd_list}")

    add_custom_command(
        OUTPUT          ${_out_file} 
        COMMAND         "${POWERSHELL_EXE}" -Command "${_powershell_cmd}" 1> "${_out_file}"
        DEPENDS         ${_file}
                        TranslationMacros.h
        COMMENT         "${_out_file}"
    )

    list(APPEND outFiles "${_out_file}")
endforeach() 

add_custom_target(
    ${PROJECT_NAME}
        DEPENDS ${outFiles}
        SOURCES 
            TranslationMacros.h
            ${inFiles}
)
set_source_files_properties(${outFiles} PROPERTIES GENERATED 1)

add_library(
    ${PROJECT_NAME}Test
    ${outFiles} 
)
add_dependencies(
    ${PROJECT_NAME}Test
    ${PROJECT_NAME}
)

Combined with

TranslationMacros.h

#define OldClass            NewClass
#define MyFunctionToReuse   SomeFunction
#define mVarOldName         mVarNewName 

Does translate

OldClass.cpp

class OldClass
{
public:
   OldClass() : mVarOldName(0) {}

   void MyFunctionToReuse()
   {
       // Some test comment
       mVarOldName = 1;
   }

   int mVarOldName;
};

Into

NewClass.cpp

class NewClass
{
public:
   NewClass() : mVarNewName(0) {}

   void SomeFunction()
   {
       // Some test comment
       mVarNewName = 1;
   }

   int mVarNewName;
};

References

Visual Studio

PowerShell

  • You can also accept your own answer, which will help others who read this question in future – Mawg Jun 13 '17 at 11:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.