¿Herramienta para mantener sincronizado el código C++ ramificado y refactorizado?

Estoy buscando una herramienta de línea de comandos/GUI de Windows o un complemento de Visual Studio donde pueda automatizar la extracción de bloques de código C++, agregar algunos pasos de refactorización/cambio de nombre e insertarlo en algún otro archivo C++. Una vez que esto esté configurado, quiero mantener la fuente/destino de los bloques de código sincronizados y/o verificar las diferencias.

EDITAR : Estoy tratando de mantener las cosas en mi proyecto lo más simples posible. Así que espero una solución con un bajo nivel de complejidad. Preferiría una solución que utilice varias herramientas de una sola tarea (tal vez ya incluidas con mi sistema operativo/IDE/SCM) en lugar de una herramienta multitarea rica en funciones que está diseñada para manejar mucho más.

Un ejemplo

Para dar un ejemplo muy simple, y no estoy seguro de si la herramienta en cuestión necesitaría comprender la sintaxis de C++ para hacer algo como esto:

OldClass.cpp

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

NuevaClase.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
       ...
   }
};

Las posibilidades que estoy viendo actualmente

Veo cuatro formas posibles de hacer esto:

  1. Extraiga manualmente el código que quiero reutilizar en plantillas/clases/funciones de C++
  2. Utilice las herramientas de refactorización de C++ con una función de grabación de macros
  3. Use un transpiler con un conjunto de reglas para convertir OldClassaNewClass
  4. Un enfoque basado únicamente en una herramienta de texto que busca palabras clave en los comentarios (copiar/renombrar/diferenciar/insertar)

Lo racional

Tengo un proyecto C++ de modelo/vista/controlador de larga duración en el que la parte de la vista ahora se intercambiará/moverá a una aplicación separada. El antiguo código base es reutilizable en un 80%, pero no puedo tomar las clases tal como están actualmente. Así que ahora estoy en un punto en el que me ramifico de mi antiguo proyecto para comenzar con el nuevo código.

Pero el antiguo proyecto aún se mantendrá durante varios años. Entonces, si empiezo con los cambios necesarios para/refactorización del nuevo proyecto, estoy buscando algo para mantener sincronizados ambos flujos de código/ramas (principalmente para corregir errores).

Y los grandes cambios en el producto anterior son solo el último recurso, porque significaría, incluso con una buena cobertura de código con pruebas unitarias, volver a probar partes importantes del producto que se envía actualmente.

Lo que he investigado hasta ahora

Las listas de características de

(Soy el tipo detrás de DMS). ¿Por qué crees que estos son "sobredimensionados"? C++ es un lenguaje increíblemente complejo. Si quieres manipularlo mecánicamente, el mecanismo tiene que manejar de alguna manera toda la complejidad, o no hará lo que quieres, o lo hará mal. ¿Cómo propondría que existiera un mecanismo más simple? [PD: DMS le permite escribir reglas de transformación de fuente a fuente y ejecutarlas en un orden repetible; eso es esencialmente lo que está buscando como "grabación macro"]
@IraBaxter Admito que parte de mi pregunta es filosófica. Y soy un seguidor del principio KISS (actualizaré la pregunta en consecuencia). Dicho esto, me da vergüenza usar DMS debido a su impresionante conjunto de funciones. La introducción de herramientas complejas en su proyecto literalmente tiene un precio (compra, capacitación, mantenimiento). Entonces, en el caso de un problema complejo, tiendo a dividir el problema en partes menos complejas en lugar de buscar herramientas más complejas para manejar todo.
Estoy absolutamente de acuerdo en que usar una herramienta compleja tiene un precio, y es bueno que lo entiendas. En igualdad de condiciones, desea evitarlo. La pregunta fundamental es si tiene un problema correspondientemente difícil con suficiente justificación. Siéntete libre de explorar las alternativas. Si su problema implica una manipulación de código explícita, dudo que pueda evitar algo como DMS, por lo que lo que queda es decidir qué tan importante es implementar su visión.
@IraBaxter Gracias por el consejo. Y sí, creo que al final, si quiero mantener los pasos de manipulación simples, todo dependerá de tener solo cambios de código localizados/mínimos.
Suena como algo que no deberías hacer. Mantenga la implementación y escriba una clase de adaptador o similar en su lugar.

Respuestas (1)

Terminé usando el precompilador de Visual Studio para traducir mis archivos fuente a través de definiciones de macros. En un segundo paso, estoy usando PowerShell para reemplazar/agregar algunos caracteres de formato especiales que el precompilador eliminaría/ignoraría.

Como estoy usando CMake, aquí está el script que probé:

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}
)

Combinado con

TraducciónMacros.h

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

Traduce

OldClass.cpp

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

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

   int mVarOldName;
};

En

NuevaClase.cpp

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

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

   int mVarNewName;
};

Referencias

Estudio visual

Potencia Shell

También puede aceptar su propia respuesta, lo que ayudará a otros que lean esta pregunta en el futuro.