找到未实现的类方法

时间:2013-01-14 10:37:58

标签: c++ methods interface-implementation

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

然而,当我进入实施阶段时,我发现我已经忘记了哪些方法已经实现,哪些方法没有实现。然后在链接阶段,我收到未实现方法的错误。这样会很好,但是类之间存在很多相互依赖关系,为了链接应用程序,我需要准备好一切。然而,在进入下一个课程之前,我宁愿让我们学习一门课程。

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

8 个答案:

答案 0 :(得分:3)

您可以为要实施的每个方法添加存根,并执行以下操作:

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

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

虽然你仍然需要将这些存根添加到实现文件中,这可能是你试图首先避开的部分。

答案 1 :(得分:0)

虽然在没有实际尝试链接的情况下我看不到这样做的简单方法,但您可以将链接器输出grep为“对ClassInQuestion ::”的未定义引用,这应该只为您提供与此错误相关的行给定的类。

这至少可以让您避免筛选整个链接过程中的所有错误消息,但这并不妨碍必须通过完整链接。

答案 2 :(得分: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;        
}

答案 3 :(得分:0)

这就是单元测试和测试覆盖工具的用途:预先为所有功能编写最小测试。丢失功能的测试不会链接。测试覆盖率报告将告诉您是否已访问所有功能。

当然,这只是在某种程度上有所帮助,它不是100%的傻瓜证明。你的开发方法对我来说听起来有点狡猾:单独逐个开发类在实践中不起作用:依赖于彼此的类(并记住:减少依赖性!)需要在某种程度上以锁步方式开发。你不能为一个课程制作一个完整的实现,然后转到下一个课程,永远不要回头。

答案 4 :(得分: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

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

答案 5 :(得分: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()函数。

答案 6 :(得分: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就是你的朋友。)

答案 7 :(得分:-1)

我看不出这样做的简单方法。有几个没有实施的课程很容易导致在多个成员团队中保持跟踪将成为一场噩梦。

我个人想要单独测试我编写的每个类,测试驱动开发是我的建议。但是,这涉及每次要检查状态时链接代码。 有关使用TDD的工具,请参阅链接here

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