Last active
February 8, 2023 22:51
-
-
Save N-Dekker/738d28b8d7528a32c12e7a129160b00f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
// Script to add in-class {} member initializers to ITK classes that have one or more virtual member function, | |
// itkNewMacro, itkSimpleNewMacro, or itkCreateAnotherMacro calls. | |
// | |
// Only tested with Visual Studio 2019 (C++14). | |
// | |
// Needs to have the directory path to the ITK sources as command-line argument, for example: | |
// | |
// AddEmptyDefaultMemberInitializers.exe D:\src\ITK\Modules | |
// | |
// Initial version by Niels Dekker, LKEB, Leiden University Medical Center, 2023 | |
#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING | |
#include <cassert> | |
#include <cctype> | |
#include <cstring> | |
#include <deque> | |
#include <experimental/filesystem> | |
#include <fstream> | |
#include <iostream> | |
#include <regex> | |
#include <sstream> | |
#include <string> | |
using namespace std::experimental::filesystem::v1; | |
namespace | |
{ | |
using Lines = std::deque<std::string>; | |
auto | |
ReadFile(const path & filePath) | |
{ | |
Lines result; | |
std::ifstream inputFileStream{ filePath }; | |
std::string line; | |
while (std::getline(inputFileStream, line)) | |
{ | |
result.push_back(line); | |
} | |
return result; | |
} | |
void | |
WriteFile(const path & filePath, const Lines & lines) | |
{ | |
std::ofstream outputFileStream{ filePath }; | |
for (const auto & line : lines) | |
{ | |
outputFileStream << line << '\n'; | |
} | |
} | |
template <unsigned int VLength> | |
bool | |
StringStartsWith(const std::string& str, const char (&substr)[VLength]) | |
{ | |
assert(substr[VLength - 1] == '\0'); | |
return str.compare(0, VLength - 1, substr) == 0; | |
} | |
template <unsigned int VLength> | |
bool | |
StringEndsWith(const std::string & str, const char (&substr)[VLength]) | |
{ | |
assert(substr[VLength - 1] == '\0'); | |
return str.size() >= VLength && str.compare(str.size() + 1 - VLength, VLength - 1, substr) == 0; | |
} | |
unsigned int | |
AddInitializers(Lines & lines) | |
{ | |
auto numberOfAddedInitializers = 0u; | |
bool virtualMemberFunctionEncountered{ false }; | |
for (auto & line : lines) | |
{ | |
const auto lineStartsWith = [&line](const auto & substr) { return StringStartsWith(line, substr); }; | |
const auto lineEndsWith = [&line](const auto & substr) { return StringEndsWith(line, substr); }; | |
const auto numberOfChars = line.size(); | |
if (numberOfChars > 0) | |
{ | |
if (lineStartsWith(" itkNewMacro(") || lineStartsWith(" itkSimpleNewMacro(") || | |
lineStartsWith(" itkCreateAnotherMacro(") || lineStartsWith(" virtual ") || lineEndsWith(" override") || | |
lineEndsWith(" override;") || lineEndsWith(" override = default;")) | |
{ | |
virtualMemberFunctionEncountered = true; | |
} | |
else | |
{ | |
if (virtualMemberFunctionEncountered) | |
{ | |
if (lineStartsWith("};")) | |
{ | |
// Class definition ended here. | |
virtualMemberFunctionEncountered = false; | |
} | |
else | |
{ | |
if (line.back() == ';' && lineStartsWith(" ") && | |
line.find(" static ") == std::string::npos && | |
line.find(" std::unique_ptr") == std::string::npos) | |
{ | |
// The regular expression explicitly skips lines of code that have a `)` at the end, like: | |
// itkSetVectorMacro(NumberOfIterations, unsigned int, m_NumberOfLevels); | |
const std::regex expression{ R"( \w.* m_\w[^{}=]*[^{}=\)];)" }; | |
// Regular expression that matches a C++ reference (`&`). | |
const std::regex expressionReference{ R"( \w.* & m_\w[^{}=]+;)" }; | |
if (std::regex_match(line, expression) && !std::regex_match(line, expressionReference)) | |
{ | |
line.pop_back(); | |
line.append("{};"); | |
std::cout << line << '\n'; | |
++numberOfAddedInitializers; | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
return numberOfAddedInitializers; | |
} | |
auto | |
ProcessFile(const path & filePath) | |
{ | |
auto lines = ReadFile(filePath); | |
const auto numberOfAddedInitializers = AddInitializers(lines); | |
if (numberOfAddedInitializers > 0) | |
{ | |
std::cout << "Added " << numberOfAddedInitializers << " initializer(s) to " << filePath.string() << std::endl; | |
WriteFile(filePath, lines); | |
} | |
return numberOfAddedInitializers; | |
} | |
void | |
ProcessDirectory(const path & directoryPath) | |
{ | |
auto numberOfAddedInitializers = 0u; | |
for (const auto & entry : recursive_directory_iterator{ directoryPath }) | |
{ | |
const auto & path = entry.path(); | |
const auto & extension = path.extension(); | |
if ((!extension.empty()) && extension.string() == ".h" && StringStartsWith(path.stem().string(), "itk") && | |
is_regular_file(path)) | |
{ | |
numberOfAddedInitializers += ProcessFile(path); | |
} | |
} | |
std::cout << "Added in total " << numberOfAddedInitializers << " initializer(s) to " << directoryPath.string() | |
<< std::endl; | |
} | |
} // namespace | |
int | |
main(int argc, char ** argv) | |
{ | |
if (argc != 2) | |
{ | |
std::cout << "Please specify the source directory path as command-line argument." | |
"\nNote: This program will modify the source files in-place!!!" | |
<< std::endl; | |
} | |
else | |
{ | |
if (argv == nullptr) | |
{ | |
return EXIT_FAILURE; | |
} | |
const char * const arg = argv[1]; | |
if (arg == nullptr) | |
{ | |
return EXIT_FAILURE; | |
} | |
ProcessDirectory(arg); | |
} | |
std::cout << "Press anything to continue" << std::endl; | |
std::cin.get(); | |
return EXIT_SUCCESS; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment