The Design of Software (CLOSED)

A public forum for discussing the design of software, from the user interface to the code architecture. Now closed.

The "Design of Software" discussion group has been merged with the main Joel on Software discussion group.

The archives will remain online indefinitely.

Find C++ method that are defined but not declared

Over the past few months (with much help from this board) I wrote a library in C++.  This was my first foray into C++ from Java.  The library is in the hands of the clients now and today one of them found an interesting bug.  Apparently in the header file for an object I defined a bunch of methods, but in the associated cpp file I failed to define them.  The C++ compiler (GCC 3.4.5) never produced so much as a warning alerting me to this condition.  A client found it today when he got a link error trying to link to a method that was declared in the header but not defined in the libXXX.a file I provided.

As far as I know I've got all compiler warnings turned on (-Wall) so my question - is there an automated way to catch methods that have been declared, but not defined?

Thanks in advance,
Uggy
Ugnonimous
Friday, July 06, 2007
 
 
You are aware, aren't you, that declared but undefined methods are sometimes required, or at least very useful.  For example, declaring but not implementing a private copy constructor and assignment operator keeps the compiler from creating default ones, which could be disastrous.  See Meyers "Effective C++" Item 11. (Sorry, I know that doesn't help.)
Will Dowling Send private email
Friday, July 06, 2007
 
 
I'm aware that its legal (if for no other reason than the compiler lets me do it).  You provide a good example of why that hadn't occured to me.

But just because its legal doesn't mean its desired.  I can switch() on an enum and not create a case for each value of the enum.  This is perfectly legal, and may in some circumstance be desirable, but often its not and so the compiler warns me if I do it.  I then smack myself in the head for being such a doofus and say "Thanks Mr. Compiler for helping me out."

Is there a way I can get Mr. Compiler (or another tool) to bring to my attention places where I've done this so I can decide if its what I meant to do or not?
Ugnonimous
Friday, July 06, 2007
 
 
I'm not aware of anything that'll do that. It's such a common technique (think about forward-declaring stuff that's in another library, for instance) that a tool like that wouldn't be useful to a lot of people. It would also be pretty hard to write. To get the list of declarations you'd need to embed a complete C++ compiler front end (preprocessor and parser). To get the list of functions defined, you'd need to compile all the translation units that go into the library, link them, and then get a list of the objects defined in the library. Alternately, you could make your program analyze the parse trees of all the translation units making up the library and get the list of defined functions that way.

Any way you slice it, you end up needing a significant chunk of a C++ compiler.

IMHO the correct tool for this job is a set of unit tests that exercise every function declared in your header. If you really need a tool to list functions that are declared but not defined, you could build one from g++ and a lot of free time.
clcr
Friday, July 06, 2007
 
 
The only trick I can think of is to declare such functions as virtual. Virtual functions cannot be dropped by the compiler/linker because they are referenced from the vtable. If you don't actually want these functions to be virtual you can add a macro to the declaration that becomes either "virtual" or blank based on compilation configuration.

I would also add that as part of your QA process for your library you should have executable that unit test each and every method. Assuming you create such tests, forgetting definitions will cause a particular test to fail - not link in fact.
Dan Shappir Send private email
Friday, July 06, 2007
 
 
I've been mulling this over (as it's an interesting question) and I think you can get the right effect using by piping ctags, grep and sed. It's a bit gross, but what can you do?

The first step is to get the prototypes in the header:

  ctags --c++-kinds=p -f - *.h

Then filter out only member function declarations:

  |grep "class:"

Then fiddle the result so that it's of the form "CLASS::FUNCTION":

  |sed -r -e "s/([^\t]+).*class\:([^ ]+)/\2::\1/"

Replace spaces with "[[:space:]]*", and escape colons, so that the result is a valid regexp:

  |sed -r -e "s/\:/\\:/g"
  |sed -r -e "s/[[:space:]]+/[[:space:]]\*/g"

Then pipe the result to a file. I called mine tmp.txt:

  ctags --c++-kinds=p -f - *.h |grep "class:" |sed -r -e "s/([^\t]+).*class\:([^ ]+)/\2::\1/" |sed -r -e "s/\:/\\:/g" |sed -r -e "s/[[:space:]]+/[[:space:]]\*/g" >tmp.txt

The result is then a rough list of the regexps that need to be searched for to find the definition of each declared function. Now what you need to do is search through the .cpp files for each regexp in turn, printing it out if it wasn't found anywhere. I'm not sure how you'd do this in Unix, but for Windows at least you can do it with the following batch file:

  @echo off
  for /f %%i in (tmp.txt) do (
  grep -e %%i *.cpp >NUL
  if errorlevel 1 echo %%i
  )

I call it "f.bat". Then run it:

  cmd /c f.bat

The result on some random code I was testing this with:

BaEx\:\:operator[[:space:]]*=
XmlWriter\:\:operator[[:space:]]*=

Which is as you'd expect, since BaEx and XmlWriter have barred assignment operators.

(They also have blocked copy constructors, which highlights one of the flaws of this script: it doesn't check signature, going as it does entirely by name. The reason is that there's too many ways for the signature to change in format between declaration and definition, for whatever reason, so you'd likely end up with too many false positives.)

This is hardly the greatest thing in the world, but it may help.
Tom_
Friday, July 06, 2007
 
 
An obvious flaw is that if you put syntactically-unnecessary spacing between "operator" and the operator symbol the above won't generate the right result.

I suppose that can be an exercise for the reader or something.
Tom_
Friday, July 06, 2007
 
 
GCC has a abundance of warnings that you can turn on or off with specific flags. First try compiling with -Wall, which should turn on all possible warnings. Next, you can try wading through the GCC manpage to see what other warnings are available (but not part of -Wall).
Jeffrey Dutky Send private email
Saturday, July 07, 2007
 
 
Here's my highly scientific approach:

Replace, in all .h files, the string:

);

with

) {}

This will give all functions a body, and when the compiler sees the actual function body elsewhere in a .cpp, it will think it's a "redefinition" and die with an error. MSVC's error is

main.cpp(5) : error C2084: function 'int A::foo(void)' already has a body

You can then grep the compiler output for "already has a body".

The interesting this is that it works even with functions that return a value! (Again, MSVC.) Apparently inline member functions that declare a return value don't have to actually return one, i.e.

struct A
{
  int foo() {}
};

won't even cause a warning, even with /Wall.
blink
Sunday, July 08, 2007
 
 
PC-Lint (http://www.gimpel.com) will do this if you perform a whole project analysis (i.e. not single file or "unit checkout").

I believe the relevant message is 714 (Information -- Symbol 'Symbol' (Location) not referenced). The description in the menual reads as follows:

"The named external variable or external function was defined but not referenced. This message is suppressed for unit checkout (-u option)."
Anna-Jayne Metcalfe Send private email
Monday, July 09, 2007
 
 
As others have mentioned, unit tests are probably the way to go here. It's not automatic like you asked for, but if you do a quick sanity-check before release, and make sure that you have (at least) one unit test for each public function, you'd be most of the way there.

On a more structural level, consider why you're declaring first, then implementing. That seems like an invitation to over-design. Just implement what you need, and then declare it.
Mark Bessey Send private email
Thursday, July 12, 2007
 
 

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics
 
Powered by FogBugz