-1

UPDATE2: Why everybody downvotes this but no one explains why complier does not complains this problem but chatter at a unused variable. It's like a C++ bug.

UPDATE3: It seems possible using G++ with nm function to compare

Below is a one minute sketch for a simple comparison(just a proven of concept)

~/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>

~~~~~~~~~~~~~~~~~~~~

Original question: I noticed some of my declared class functions are not defined, since my project is a module. It's found by the user later on.

Is there any method(or function in VC++) to do the integrity check?

UPDATE: To be more specific:

#include "stdafx.h"
class foo{
public:
    void aaa();   //<---This function is not defined and no warnings
};

int _tmain(int argc, _TCHAR* argv[])
{
    return 0;

}

Why C++ can find a "defined but unused variable", but not able to find a "declared but undefined function"?

My Opinion:

it seems there are some decorations for DLL function in VC++ so the compiler knows about the function is implemented somewhere else. By using static lib with own code or external code, the linker shall be able to traverse all symbols and find the missing.

Update:

By reading Peter's answer I can accept the fact that linker does not do the job. But my opinion is unit test is not for this task. the symbol check is kind of string compare, not at function check level yet. May I ask one more question, can I export the symbol tables and do some string check. 5-10MB binary file(maybe smaller when exported to tables) is not a heavy task for scripts using regular expression tools

Thanks.

Boying
  • 1,404
  • 13
  • 20
  • @chris how to avoid missing functions in unit test? – Boying May 22 '16 at 15:17
  • 2
    @Boying: Have your code reviewers check for test coverage. (Coverage instrumentation can be done automatically.) – Kerrek SB May 22 '16 at 15:19
  • Compiler will not complain if a function is declared, not implemented but not used. That's it and there is nothing you can do to avoid that. But, as chris mentioned, if a unit test calls the missing function, it will spot you that it's missing because the test won't compile. – jpo38 May 22 '16 at 15:20
  • @KerrekSB I myself is the reviewer and I'm not able to find the problem – Boying May 22 '16 at 15:28
  • 1
    Regarding your edit: Why should anyone explain *why* the compiler doesn't complain when you never asked for that? Generally, if you have a question, you should ask it if you want to get answers, and not expect the answer as part of a *different* question. – Kerrek SB May 22 '16 at 15:33
  • @Boying: OK then, same thing: Write unit tests, and monitor test coverage to see whether you missed anything. – Kerrek SB May 22 '16 at 15:33
  • @KerrekSB if you want to teach me how to program you can simply tell me to be more careful, since the testcase is also written by me, I don't think I can avoid the same problem. – Boying May 22 '16 at 15:39
  • 1
    Sorry I do not have the time to check, but I think `objdump` may be able to display whether or not it was defined. Might be worth looking into:) – tkellehe May 22 '16 at 17:53
  • @tkellehe not sure about objdump, I tried to use g++ and nm to get started, (in update3) – Boying May 25 '16 at 05:09
  • 1
    @Boying `objdump` has the same `-C` as `nm`. I thought that you might have to search... Once again I don't have time to be of any real help, but I found [this](http://stackoverflow.com/questions/21878260/get-list-of-methods-in-class-using-clang) which seems as though someone was able to create some kind of parser to get all of the methods (in this case just virtual) from files. Now, I guess the next thing would be to find a way to get just methods from `nm`'s output. Sorry for not giving any real help, but I hope you can go somewhere with this:) – tkellehe May 27 '16 at 13:00
  • @tkellehe Thanks a lot!! That's really appreciated! – Boying May 27 '16 at 13:19
  • @Boying when you get the chance look over the update to my answer and see if you can get it working for you. Also, does it give you the precision you were looking for? – tkellehe Jun 10 '16 at 09:44

4 Answers4

4

Some compilers sometimes do. Try this:

static int f();

then don't define it.

The reason the compiler can diagnose this is that static int f() declares a function that can only be used in the current translation unit and, further, can only be defined in the current translation unit. That's like declaring a local variable and not using it.

Both of these are different from declaring an external variable that isn't used; it could be defined in some other translation unit, but since it's not used, why put in the extra effort to figure that out? Recognizing it would require putting information about every function declaration into every object file, and then having the linker check that information. You really don't want to have unused declarations of every function in the standard library stuffed into each of your object files just so the linker can tell you that you didn't use most of them.

As others have said, the way to detect this is to write unit tests. Which you should be doing anyway, so this check comes for free.

Pete Becker
  • 74,985
  • 8
  • 76
  • 165
  • Thanks Peter, you answer is more promising. I can accept the fact that linker does not do the job. But my opinion is unit test is not for this task. the symbol check is kind of string compare, not at function check level yet. May I ask you one more question, can I export the symbol tables and do some string check. 5-10MB binary file(maybe smaller when exported to tables) is not a heavy task for scripts using regular expression tools. – Boying May 22 '16 at 16:32
  • @Boying - it's not at all unreasonable for a project to have several hundred object files. If every one of them has 5-10MB of extra stuff that's an awful lot of disk space, and if you're building across a network it's an awful lot of traffic. This just isn't worth the cost. And, yes, unit tests are the way to go. – Pete Becker May 22 '16 at 16:34
  • @Boying - I just realized you were asking something different. I don't know of a way to get the compiler to create a list of symbols that are declared in a translation unit but not used in that translation unit. It just doesn't care. I suppose if you're really ambitious you could do that with llvm, but I don't think there will be much interest in such a tool. – Pete Becker May 22 '16 at 20:50
1

you're missing the point. If you created a module/plugin/library with some methods/functions, C++ has no way to know who/where/how will use your library.

It's not a problem of your library, it's a problem of caller software.

If you're developing some, say "server/main app" and want to be sure that plugins have functions they declare, you should go level up - and test if your main application can be linked against your plugin. C++ has nothing to do with this process by itself.

p.s. before crying about c++ bugs, google://"linker c++" or something like this.

strangeqargo
  • 1,276
  • 1
  • 15
  • 23
  • the question is why C++ can find a "defined but unused variable", but not able to find a "declared but undefined function"? it seems missing basic function. – Boying May 22 '16 at 15:34
  • you're not calling this function, so it does not need a declaration (body). To be correct, compiler needs just a definition only. http://stackoverflow.com/questions/1410563/what-is-the-difference-between-a-definition-and-a-declaration – strangeqargo May 22 '16 at 15:38
  • read about linking process. You need function definition to compile a program, but real function could be linked afterwards, from a library. – strangeqargo May 22 '16 at 15:42
  • 1
    @strangeqargo, You got those backward. A definition has the body of the function. – chris May 22 '16 at 16:04
1

Declared-undefined function are perfectly allowed for many reasons. First, sometimes the programmer would want to declare the function and define it in another file (have definitions in .hpp for example and implementations in .cpp the files). The application will only look for an implementation - if its used - during the linking stage. Second, sometimes implementations of functions are put in dlls (in case its windows), and that can be quite convenient.

Aiman Al-Eryani
  • 709
  • 4
  • 19
  • it seems there are some decorations for DLL function in VC++ so the compiler knows about the function is implemented somewhere else. By using static lib with own code or external code, the linker shall be able to traverse all symbols and find the missing. – Boying May 22 '16 at 15:50
1

Updated the answer with something that may have more promise.


Sorry this is not a full answer, but I wanted to get something to you...


To begin, @Pete Becker has the best answer pertaining to why C++ does not throw warnings. So, this answer will mainly attempt to give you some kind of direction to get all of the undefined functions.


Getting Def Methods

I have found the following command to produce an output of all of the functions within a given .o file that have a definition. It does include the namespaces, but this is not designed to be the whole solution.

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

The first grep just returns all of the lines that contain functions, then the second actually grabs the functions.


First Attempt

Combining this with some Python I was able to produce the following:

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

Disclaimer

Now, this is not a full solution that handles everything you can throw at it nor is this the fastest, but it does give you a good starting point. (This applies to the update as well.)


Update

Found the library CastXML that takes C++ and spits out an XML file pertaining to the code.

Using that and an XML parser in Python I came up with the following that should handle a lot more cases.

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

Example

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"

NOTE: I have not had the time nor the environment to test the above code. So, if you find a stupid please attempt to fix it. Also, I know that is bad that the code has not been tested, but I wanted to give you something that I am pretty confident will handle a lot of methods.


Hope this helps!

tkellehe
  • 659
  • 5
  • 11