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

时间:2016-05-22 15:13:16

标签: c++ visual-c++

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二进制文件(导出到表时可能更小)对于使用正则表达式工具的脚本来说不是一项繁重的任务

感谢。

4 个答案:

答案 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 ++ 不会发出警告的最佳答案。所以,这个答案主要是试图给你一些方向来获得所有未定义的函数。

获取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

声明

现在,这不是一个完整的解决方案,可以处理你可以抛出的所有内容,也不是最快的,但它确实为你提供了一个很好的起点。 (这也适用于更新。)

更新

找到了带有 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"

注意:我没有时间和环境来测试上面的代码。所以,如果你发现一个愚蠢的请尝试解决它。另外,我知道代码没有经过测试很糟糕,但我想给你一些我非常有信心会处理很多方法的东西。

希望这有帮助!