如何检查是否实现了每个声明的类函数

How to check if every declared class function is implemented

本文关键字:声明 类函数 实现 是否 何检查 检查      更新时间:2023-10-16

更新2:为什么每个人都对此投了反对票,但没有人解释为什么编译器不抱怨这个问题,而是对一个未使用的变量喋喋不休。这就像一个C++错误。

更新3:使用G++和nm函数来比较似乎是可能的

以下是一分钟的草图,用于简单比较(只是一个概念验证)

~/testcpp> cat a.cpp
class foo {
public:
foo();
foo(int);
};
foo::foo(int i){
}
int main(){
return 0;
}
~/testcpp> g++ -std=c++0x -c a.cpp
~/testcpp> g++ -E a.cpp | perl -ne 'push @x,$_ if /class foo/../};/;END{@x=map {"foo::$_" } grep {/);/} map{s/[ t]*//g;$_}@x;print @x}' | tee head.txt
foo::foo();
foo::foo(int);
~/testcpp> nm -C a.o | grep foo:: | cut -b'20-' | uniq | tee body.txt
foo::foo(int)
~/testcpp> diff head.txt body.txt
1,2c1
< foo::foo();
< foo::foo(int);
---
> foo::foo(int)
~/testcpp>

~~~~~~

最初的问题:我注意到我声明的一些类函数没有定义,因为我的项目是一个模块。用户稍后会找到它。

是否有任何方法(或VC++中的函数)来进行完整性检查?

更新:更具体地说:

#include "stdafx.h"
class foo{
public:
void aaa();   //<---This function is not defined and no warnings
};
int _tmain(int argc, _TCHAR* argv[])
{
return 0;
}

为什么C++可以找到"已定义但未使用的变量",却找不到"已声明但未定义的函数"?

我的看法:

VC++中的DLL函数似乎有一些修饰,所以编译器知道该函数是在其他地方实现的。通过将静态库与自己的代码或外部代码一起使用,链接器应能够遍历所有符号并找到缺失的符号。

更新:

通过阅读彼得的答案我可以接受链接器不起作用的事实。但我认为单元测试不适合这个任务。符号检查是一种字符串比较,还不是函数检查级别。我可以再问一个问题吗?我可以导出符号表并进行一些字符串检查吗。对于使用正则表达式工具的脚本来说,5-10MB二进制文件(导出到表时可能更小)不是一项繁重的任务

谢谢。

有些编译器有时会这样做。试试这个:

static int f();

然后不要定义它。

编译器能够诊断出这一点的原因是static int f()声明了一个只能在当前翻译单元中使用的函数,而且只能在当前转换单元中定义。这就像声明一个局部变量而不使用它

这两者都不同于声明一个未使用的外部变量;它可以在其他翻译单元中定义,但既然它没有被使用,为什么要付出额外的努力来弄清楚呢?要识别它,需要将有关每个函数声明的信息放入每一个对象文件中,然后让链接器检查该信息。你真的不想把标准库中每个函数的未使用声明都塞进每个对象文件中,这样链接器就可以告诉你你没有使用其中的大部分。

正如其他人所说,检测这种情况的方法是编写单元测试。这是你应该做的,所以这张支票是免费的。

用可能更有希望的东西更新了答案。


对不起,这不是一个完整的答案,但我想给你一些东西。。。


首先,@Pete Becker给出了关于C++不抛出警告的最佳答案。所以,这个答案主要是试图给你一些方向,让你得到所有未定义的函数。


获取Def方法

我找到了以下命令来生成给定.o文件中具有定义的所有函数的输出。它确实包括名称空间,但这并不是设计为完整的解决方案。

nm -C <object file> | grep '(.*)' | grep -oP '(?<= w ).*'

第一个grep只返回包含函数的所有行,然后第二个实际获取函数。


第一次尝试

将其与一些Python相结合,我能够生成以下内容:

import os
import re
def getDefMethods(object_file):
file = os.popen("nm -C %s | grep '(.*)' | grep -oP '(?<= w ).*'"%object_file)
lines = file.readlines()
result = []
for i in lines:
# Removes the namespaces to some degree...
result.append(re.search("([^:]*(.*))", i).groups[0])
return result
def getMethods(header):
file = open(header, 'r')
lines = file.readlines()
result = []
for i in lines:
match = re.search(".*s(w*(.*))", i)
if match != None:
result.append(match.groups()[0])
return result
def getUndMethods(object_file, header):
defs = getDefMethods(object_file)
fs = getMethods(header)
result = []
for i in fs:
found = False
for j in defs:
if i == j:
found = True
defs.remove(j)
break
if not found:
result.append(i)
return result

免责声明

现在,这不是一个完整的解决方案,可以处理你能扔给它的一切,也不是最快的,但它确实给了你一个很好的起点。(这也适用于更新。)


更新

找到了库CastXML,该库接受C++并吐出与代码相关的XML文件。

使用它和Python中的XML解析器,我得到了以下内容,应该可以处理更多的情况。

import os
import xml.etree.ElementTree as ET
def_methods = []
all_methods = []
und_methods = []
def getDefMethods(object_file):
file = os.popen("nm -C %s | grep '(.*)' | grep -oP '(?<= w ).*'"%object_file)
result = file.readlines()
result = [line.rstrip('n') for line in result]
def_methods.extend(result);
return result
def getID(root, id):
if id != None:
for elem in root.iter():
if elem.get('id') == id:
return elem
return None
def buildNamespace(root, elem, first):
if elem == None or elem.get('name') == "::":
return ""
id = None
if elem.get('context') != None:
id = elem.get('context')
elif elem.get('type') != None:
id = elem.get('type')
if first:
namespace = elem.get('name')
else:
namespace = elem.get('name') + "::"
namespace = buildNamespace(root,getID(root,id),false) + namespace
return namespace
def buildArgs(root, function):
result = "("
args = function.findall('Argument')
for arg in args:
name = arg.get('name')
type = buildNamespace(root, getID(root,arg.get('type')),true)
result += type + " " + name + ", "
if result.endswith(", "):
result = result[:-2]
return result + ")"
def getMethods(file_name):
xml = "%s.xml"%file_name
os.system(("gccxml %s.cpp -fxml="%file_name) + xml)
result = []
tree = ET.parse(xml)
root = tree.getroot()
functions = root.findall('Function')
for function in functions:
f = function.get('name')
f += buildArgs(root,function)
f = buildNamespace(root,getID(root,function.get('context')),false) + f
result.append(f)
methods.extend(result)
return result
def getUndMethods(object_file, file_name):
defs = getDefMethods(object_file)
fs = getMethods(file_name)
result = []
for i in fs:
found = False
for j in defs:
if i == j:
found = True
defs.remove(j)
break
if not found:
result.append(i)
und_methods.extend(result)
return result

示例

import test as methodFinder
methodFinder.getUndMethods("a.o", "a")
print "All Methods defined in object filen"
for method in methodFinder.def_methods:
print method, "n"
print "All Methods in filen"
for method in methodFinder.methods:
print method, "n"
print "Undefined Methodsn"
for method in methodFinder.und_methods:
print method, "n"

注意:我没有时间也没有环境来测试上面的代码。所以,如果你发现了一个愚蠢的问题,请尝试修复它。此外,我知道代码没有经过测试是很糟糕的,但我想给你一些我很有信心能处理很多方法的东西。


希望这能有所帮助!

您没有抓住要点。如果你用一些方法/函数创建了一个模块/插件/库,C++就无法知道谁/在哪里/如何使用你的库。

这不是库的问题,而是调用程序软件的问题。

如果你正在开发一些插件,比如"服务器/主应用程序",并希望确保插件具有它们声明的功能,你应该升级,并测试你的主应用程序是否可以与你的插件链接。C++本身与这个过程无关。

在哭诉c++错误之前,请先谷歌://"链接器c++"或类似的东西。

由于多种原因,完全允许使用声明的未定义函数。首先,有时程序员想要声明函数并在另一个文件中定义它(例如,在.hpp中有定义,在.cpp中有实现)。应用程序只会在链接阶段查找实现(如果已使用)。其次,有时函数的实现被放在dlls中(如果是windows),这可能非常方便。