10

在我的应用程序中,我正在处理一个较大的类(超过 50 个方法),每个类都相当复杂。我并不担心复杂性,因为它们在将功能块分离成更小的方法然后调用它们方面仍然很简单。这就是方法数量变大的原因(其中很多方法是私有的——特别是隔离功能块)。

然而,当我进入实施阶段时,我发现我忘记了哪些方法已经实施,哪些方法没有实施。然后在链接阶段,我收到未实现方法的错误。这很好,但是类之间有很多相互依赖关系,为了链接应用程序,我需要准备好一切。然而,我更愿意先上一堂课,然后再去下一堂课。

由于我无法控制的原因,我不能使用 IDE——只能使用纯文本编辑器和 g++ 编译器。有没有办法在一个类中找到未实现的方法而不进行完整链接?现在,我确实对每个方法的实现 cpp 文件中的方法签名进行文本搜索,但这非常耗时。

4

8 回答 8

3

您可以为您打算实现的每个方法添加一个存根,然后执行以下操作:

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

使用 gcc,这会为每个输出文件、行号和错误消息。因此,您可以只编译有问题的模块并 grep 为“未实现”,而无需运行链接器。

尽管您仍然需要将这些存根添加到实现文件中,这可能是您最初试图规避的部分内容。

于 2013-01-14T11:50:08.277 回答
0

尽管在不实际尝试链接的情况下看不到执行此操作的简单方法,但您可以 grep 链接器输出“未定义对 ClassInQuestion:: 的引用”,它应该只为您提供与给定类的方法的此错误相关的行.

这至少可以让您避免从整个链接过程中筛选所有错误消息,尽管它不会阻止必须通过完整链接。

于 2013-01-14T11:41:20.973 回答
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;        
}
于 2013-01-14T12:28:10.810 回答
0

这就是单元测试和测试覆盖工具的用途:预先为所有功能编写最少的测试。缺少功能的测试不会链接。测试覆盖率报告会告诉你是否所有的函数都被访问过。

当然,这只是在一定程度上有所帮助,它不是 100% 的傻瓜证明。不过,您的开发方法对我来说听起来有点狡猾:孤立地一个一个地开发类在实践中是行不通的:相互依赖的类(请记住:减少依赖关系!)需要在某种程度上同步开发。你不能为一个类完成一个完整的实现,然后再转移到下一个类,永不回头。

于 2013-01-14T12:41:47.010 回答
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

其他人将能够告诉您如何将其自动化到构建过程中,但这是快速检查哪些方法尚未实现的一种方法。

于 2013-01-14T23:08:06.307 回答
0

c++11的delete关键字可以解决问题

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() 函数。

于 2014-12-16T11:39:44.197 回答
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不关心你的构建配置,无论如何都会找到一切。你也可以去掉不推荐使用的限定符来清理你的编译器输出——上面的 hack 会产生很多不相关的噪音。根据您的优先级,这可能是一个劣势(例如,如果您当前正在实现特定于 Linux 的功能,则您可能不关心特定于 Windows 的功能,反之亦然。)

如果您使用 IDE,大多数都可以让您搜索整个项目,有些甚至可以让您右键单击一个符号并在任何使用它的地方查找。(但你说你不能使用一个,所以在你的情况下grep是你的朋友。)

于 2018-06-05T04:26:46.793 回答
-1

我看不到这样做的简单方法。拥有几个没有实现的类很容易导致在多成员团队中跟踪将是一场噩梦。

就个人而言,我想对我编写的每个类进行单元测试,而测试驱动开发是我的建议。但是,这涉及每次您要检查状态时链接代码。有关使用 TDD 的工具,请参阅此处的链接。

另一种选择是编写一段代码,可以解析源代码并检查要实现的功能。GCC_XML 是一个很好的起点。

于 2013-01-14T10:59:44.080 回答