UPDATE2: 为什么每个人都投票支持这一点,但没有人解释为什么编译器不会抱怨这个问题,而是对未使用的变量进行喋喋不休。它就像一个C ++错误。
UPDATE3: 似乎可以使用带有nm函数的G ++进行比较
下面是一个简单比较的一分钟草图(只是经过验证的概念)
~/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 ++可以找到&#34;已定义但未使用的变量&#34;,但无法找到&#34;声明但未定义的函数&#34;?
我的意见:
似乎VC ++中有一些DLL函数的装饰,所以编译器知道该函数是在其他地方实现的。通过使用带有自己的代码或外部代码的静态库,链接器应该能够遍历所有符号并找到丢失的符号。
更新
阅读彼得的回答 我可以接受链接器不能完成这项工作的事实。但我的意见是单元测试不适合这项任务。符号检查是一种字符串比较,而不是功能检查级别。我可以再问一个问题,我可以导出符号表并进行一些字符串检查。 5-10MB二进制文件(导出到表时可能更小)对于使用正则表达式工具的脚本来说不是一项繁重的任务
感谢。
答案 0 :(得分:4)
有些编译器有时会这样做。试试这个:
static int f();
然后不要定义它。
编译器可以诊断出这一点的原因是static int f()
声明了一个只能在当前翻译单元中使用的函数,而且,在当前翻译单元中只能定义 。这就像声明一个局部变量而不是使用它。
这两者与声明未使用的外部变量不同;它可以在其他一些翻译单元中定义,但由于它没有被使用,为什么要付出额外的努力来解决这个问题呢?认识到需要将每个函数声明的信息放入每个目标文件中,然后让链接器检查该信息。你真的不希望标准库中的每个函数都有未使用的声明,这些声明只是填充到每个目标文件中,只是链接器可以告诉你没有使用它们中的大多数。
正如其他人所说,检测这种情况的方法是编写单元测试。无论如何你应该做什么,所以这个检查是免费的。
答案 1 :(得分:1)
你错过了这一点。如果您使用某些方法/函数创建了模块/插件/库,则C ++无法知道使用库的人/位置/方式。
这不是你的图书馆的问题,这是来电软件的问题。
如果你正在开发一些,比如“服务器/主应用程序”,并且想要确保插件具有他们声明的功能,你应该升级 - 并测试你的主应用程序是否可以与你的插件链接。 C ++本身与此过程无关。
P.S。在哭泣c ++ bug之前,谷歌://“链接器c ++”或类似的东西。
答案 2 :(得分:1)
声明未定义的函数完全被允许有很多原因。首先,有时程序员会想要声明该函数并在另一个文件中定义它(例如在.hpp中有定义,在.cpp文件中有实现)。应用程序将仅在链接阶段查找实现(如果已使用)。其次,有时候函数的实现会放在 dll 中(以防它的窗口),这非常方便。
答案 3 :(得分:1)
用可能更有希望的东西更新答案。
对不起,这不是一个完整的答案,但我想得到一些东西......
首先,@ Pee Becker有一个关于为什么 C ++ 不会发出警告的最佳答案。所以,这个答案主要是试图给你一些方向来获得所有未定义的函数。
我找到了以下命令来生成给定.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
现在,这不是一个完整的解决方案,可以处理你可以抛出的所有内容,也不是最快的,但它确实为你提供了一个很好的起点。 (这也适用于更新。)
找到了带有 C ++ 的库CastXML,并发出了与该代码相关的 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 file\n"
for method in methodFinder.def_methods:
print method, "\n"
print "All Methods in file\n"
for method in methodFinder.methods:
print method, "\n"
print "Undefined Methods\n"
for method in methodFinder.und_methods:
print method, "\n"
注意:我没有时间和环境来测试上面的代码。所以,如果你发现一个愚蠢的请尝试解决它。另外,我知道代码没有经过测试很糟糕,但我想给你一些我非常有信心会处理很多方法的东西。
希望这有帮助!