Найти не реализованные методы класса

В моем приложении я имею дело с классами большего размера (более 50 методов), каждый из которых достаточно сложен. Меня не беспокоит сложность, поскольку они все еще просты с точки зрения выделения частей функциональности в меньшие методы и последующего их вызова. Таким образом, число методов становится большим (многие из этих методов являются частными, в частности изолируя части функциональности).

Однако когда я добираюсь до стадии реализации, я обнаруживаю, что теряю счет, какие методы были реализованы, а какие нет. Тогда на этапе связывания я получаю ошибки для нереализованных методов. Это было бы хорошо, но между классами существует много взаимозависимостей, и для того, чтобы связать приложение, мне нужно подготовить ВСЕ. Тем не менее, я бы предпочел пройти один курс, прежде чем перейти к следующему.

По независящим от меня причинам я не могу использовать IDE — только простой текстовый редактор и компилятор g ++. Есть ли способ найти нереализованные методы в одном классе без полной ссылки? Прямо сейчас я буквально выполняю текстовый поиск по сигнатурам методов в файле cpp реализации для каждого из методов, но это занимает очень много времени.

10

Решение

Хотя я не вижу простого способа сделать это без фактической попытки связывания, вы можете получить вывод компоновщика для «неопределенной ссылки на ClassInQuestion ::», которая должна давать вам только строки, связанные с этой ошибкой, для методов данного класса ,

Это, по крайней мере, позволяет избежать просеивания всех сообщений об ошибках всего процесса компоновки, хотя и не препятствует выполнению полной компоновки.

0

Другие решения

Вы можете добавить заглушку для каждого метода, который вы собираетесь реализовать, и сделать:

void SomeClass::someMethod() {
#error Not implemented
}

С gcc это выводит файл, номер строки и сообщение об ошибке для каждого из них. Таким образом, вы можете просто скомпилировать рассматриваемый модуль и выполнить команду grep для «Не реализовано», не требуя запуска компоновщика.

Хотя вам все равно нужно добавить эти заглушки в файлы реализации, что может быть частью того, что вы пытались обойти в первую очередь.

3

Вот для чего предназначены модульные тесты и инструменты покрытия тестов: напишите минимальные тесты для всех функций заранее. Тесты на отсутствующие функции не будут ссылаться. Отчет о тестовом покрытии покажет, все ли функции были посещены.

Конечно, это только помогает в некоторой степени, это не 100% доказательство дурака. Хотя ваша методология разработки звучит немного странно: разработка классов один за другим в отдельности не работает на практике: классы, которые зависят друг от друга (и помните: уменьшайте зависимости!), Должны быть в некоторой степени разработаны в слежку. Вы не можете создать полную реализацию для одного класса и перейти к следующему, никогда не оглядываясь назад.

0

В прошлом я создавал исполняемый файл для каждого класса:

#include "klass.h"int main() {
Klass object;
return 0;
}

Это сокращает время сборки, позволяет сосредоточиться на одном занятии, ускоряет цикл обратной связи.

Это может быть легко автоматизировано.

Я действительно хотел бы уменьшить размер этого класса!

редактировать

Если есть препятствия, вы можете пойти грубой силой:

#include "klass.h"
Klass createObject() {
return *reinterpret_cast<Klass>(0);
}

int main() {
Klass object = createObject();
return 0;
}
0

Вы можете написать небольшой скрипт, который анализирует файл заголовка для реализаций метода (регулярные выражения сделают это очень просто), а затем сканирует файл реализации для тех же реализаций метода.

Например, в Ruby (для модуля компиляции C ++):

className = "" # Either hard-code or Regex /class \w+/
allMethods = []

# Scan header file for methods
File.open(<headerFile>, "r") do |file|
allLines = file.map { |line| line }
allLines.each do |line|
if (line =~ /(\);)$/) # Finds lines ending in ");" (end of method decl.)
allMethods << line.strip!
end
end
end

implementedMethods = []
yetToImplement = []

# Scan implementation file for same methods
File.open(<implementationFile>, "r") do |file|
contents = file.read
allMethods.each do |method|
if (contents.include?(method)) # Or (className + "::" + method)
implementedMethods << method
else
yetToImplement << method
end
end
end

# Print the results (may need to scroll the code window)
print "Yet to implement:\n"yetToImplement.each do |method|
print (method + "\n")
end

print "\nAlready implemented:\n"implementedMethods.each do |method
print (method + "\n")
end

Кто-то еще сможет рассказать вам, как автоматизировать это в процессе сборки, но это один из способов быстро проверить, какие методы еще не были реализованы.

0

Ключевое слово delete в c ++ 11 делает свое дело

struct S{
void f()=delete; //unimplemented
};

Если C ++ 11 недоступен, вы можете использовать private как обходной путь

struct S{
private: //unimplemented
void f();
};

С помощью этих двух методов вы можете написать тестовый код в файле .cpp

//test_S.cpp
#include "S.hpp"namespace{
void test(){
S* s;
s->f(); //will trigger a compilation error
}
}

Обратите внимание, что ваш тестовый код никогда не будет выполнен. Пространство имен {} сообщает компоновщику, что этот код никогда не используется вне текущего модуля компиляции (т.е. test_S.cpp) и поэтому будет отброшен сразу после проверки компиляции.

Поскольку этот код никогда не выполняется, вам на самом деле не нужно создавать настоящий S-объект в тестовой функции. Вы просто хотите обмануть компилятор, чтобы проверить, имеет ли объект S вызываемую функцию f ().

0

Вы можете создать собственное исключение и выбросить его так, чтобы:

  • Вызов невыполненной функции завершит приложение, а не оставит его в неожиданном состоянии.
  • Код все еще может быть скомпилирован, даже без необходимых функций
  • Вы можете легко найти нереализованные функции, просматривая предупреждения компилятора (используя некоторые, возможно, противные приемы) или просматривая каталог вашего проекта.
  • Вы можете при желании удалить исключение из сборок выпуска, что приведет к ошибкам сборки, если есть какие-либо функции, которые пытаются вызвать исключение
#if defined(DEBUG)

#if defined(__GNUC__)
#define DEPRECATED(f, m) f __attribute__((deprecated(m)))
#elif defined(_MSC_VER)
#define DEPRECATED(f, m) __declspec(deprecated(m)) f
#else
#define DEPRECATED(f, m) f
#endif

class not_implemented : public std::logic_error {
public:
DEPRECATED(not_implemented(), "\nUnimplemented function") : logic_error("Not implemented.") { }
}

#endif // DEBUG

Не реализованные функции выглядят так:

void doComplexTask() {
throw not_implemented();
}

Вы можете искать эти невыполненные функции несколькими способами. В GCC вывод для отладочных сборок:

main.cpp: In function ‘void doComplexTask()’:
main.cpp:21:27: warning: ‘not_implemented::not_implemented()’ is deprecated:
Unimplemented function [-Wdeprecated-declarations]
throw not_implemented();
^
main.cpp:15:16: note: declared here
DEPRECATED(not_implemented(), "\nUnimplemented function") : logic_error("Not implemented.") { }
^~~~~~~~~~~~~~~
main.cpp:6:26: note: in definition of macro ‘DEPRECATED’
#define DEPRECATED(f, m) f __attribute__((deprecated(m)))

Выпуск билдов:

main.cpp: In function ‘void doComplexTask()’:
main.cpp:21:11: error: ‘not_implemented’ was not declared in this scope
throw not_implemented;
^~~~~~~~~~~~~~~

Вы можете искать исключения с grep:

$ grep -Enr "\bthrow\s+not_implemented\b"main.cpp:21:    throw not_implemented();

Преимущество использования grep в том, что grep не заботится о вашей конфигурации сборки и найдет все независимо. Вы также можете избавиться от устаревшего квалификатора, чтобы очистить вывод компилятора — вышеупомянутый хак генерирует много несущественного шума. В зависимости от ваших приоритетов это может быть недостатком (например, вас могут не волновать функции, специфичные для Windows, если вы в настоящее время реализуете функции, специфичные для Linux, или наоборот).

Если вы используете IDE, большинство из них позволит вам искать весь ваш проект, а некоторые даже позволят вам щелкнуть правой кнопкой мыши по символу и найти его везде, где он используется. (Но вы сказали, что не можете использовать один так в вашем случае grep твой друг.)

0

Я не вижу простой способ сделать это. Наличие нескольких классов без реализации легко приведет к ситуации, когда отслеживание в команде с несколькими членами будет кошмаром.

Лично я хотел бы провести модульное тестирование каждого класса, который я пишу, и тестирование, основанное на разработке, — моя рекомендация Однако это подразумевает связывание кода каждый раз, когда вы хотите проверить статус.
Для инструментов для использования TDD обратитесь к ссылке Вот.

Другой вариант заключается в написании фрагмента кода, который может анализировать исходный код и проверять его функциональность. GCC_XML — хорошая отправная точка.

-1
По вопросам рекламы [email protected]