If you wonder why a function can't be found, one thing to check is whether the function exists in the first place


One of my colleagues was frustrated trying to get some code to build. "Is there something strange about linking variadic functions? Because I keep getting an unresolved external error for the function, but if I move the function definition to the declaration point, then everything works fine."

// blahblah.h

... other declarations ...

void LogWidget(Widget* widget, const char* format, ...);

...

// widgetstuff.cpp
...
#include "blahblah.h"
...

// some code that calls LogWidget
void foo(Widget* widget)
{
 LogWidget(widget, "starting foo");
 ...
}

// and then near the end of the file

void LogWidget(Widget* widget, const char* format, ...)
{
    ... implementation ...
}

...

"With the above code, the linker complains that Log­Widget cannot be found. But if I move the implementation of Log­Widget to the top of the file, then everything builds fine."

// widgetstuff.cpp
...
#include "blahblah.h"
...

// move the code up here
void LogWidget(Widget* widget, const char* format, ...)
{
    ... implementation ...
}

// some code that calls LogWidget
void foo(Widget* widget)
{
 LogWidget(widget, "starting foo");
 ...
}

...

"I tried putting an explicit calling convention in the declaration, I tried using extern "C", nothing seems to help."

We looked at the resulting object file and observed that in the case where the error occurred, there was an external reference to Log­Widget but no definition. I asked, "Is the definition of the function #ifdef'd out by mistake? You can use this technique to find out."

That was indeed the problem. The definition of the function was inside some sort of #ifdef that prevented it from being compiled.

Sometimes, the reason a function cannot be found is that it doesn't exist in the first place.

Comments (10)
  1. George says:

    I think that this code, as written, would cause a compile error first, and not get to the link phase?  At least that has been my experience in such situations.

    [I don't see what the error would be. The prototype is in the header file, and the cpp file includes the header file. -Raymond]
  2. Brian_EE says:

    @George: Some compilers (at least the embedded one I use) have a setting to automatically generate prototype declarations for functions. So what happens is you get the compiler's best guess at what type the parameters are. If you then have the function defined later in the unit, with a different parameter list, it doesn't match, and the linker finds that. Raymond also specifically mentioned that the definition was a variadac function and the use only used the first two parameters.

  3. George says:

    @Brian EE: Point well taken about some non-Windows environments and toolsets.

    However, if you were to take this in the context of Windows programming (this is Raymond's blog, after all), and make a small Win32 console app (C++, in VS2013) that does basically what Raymond outlined, You get "error C3861: 'Log Widget': identifier not found" as a compiler error, and no linking occurs.  Perhaps I should have been more clear about what I was attempting to put onto the bit-page.

    [Worked for me, once I added a class Widget; to the top of blahblah.h. Did you remember the #include "blahblah.h" at the top of widgetstuff.cpp? -Raymond]
  4. alegr1 says:

    It's hard to find a black cat in a dark room, especially if it's not there.

  5. George says:

    @Raymond: I guess I misunderstood what was in the exclusionary ifdef.  Despite decades, I can't always keep a definition separate from a declaration.  If you attempt to refer to LogWidget before it is known what the function definition or declaration is, the compile error does seem to occur.

  6. mh says:

    ...and this is why we use syntax highlighting in Visual Studio, so that we can see that LogWidget is greyed-out, and therefore #ifdef'ed out.

  7. Adam Rosenfield says:

    @George: Raymond is talking about an exclusionary ifdef in the *source implementation file*, NOT the header file, i.e. this:

    // widgetstuff.cpp

    #include "blahblah.h"  // This includes the declaration of the LogWidget prototype

    // some code that calls LogWidget

    void foo(Widget* widget)

    {

    LogWidget(widget, "starting foo");

    ...

    }

    ...

    #ifdef SOME_MACRO_WHICH_IS_NOT_ACTUALLY_DEFINED

    void LogWidget(Widget* widget, const char* format, ...)

    {

       ... implementation ...

    }

    #endif

  8. Rick C says:

    @mh, that's assuming Visual Studio is right.  It's not always.

  9. Mark says:

    Depending on your build environment/compiler, asking for the post-preprocessor spew may also be an option - that way you see what the compiler sees instead of trying to infer what it sees.

  10. @mh says:

    >...why we use syntax highlighting in Visual Stud

    Yea. It works if you account for _DEBUG (hehe) and if the highlighted project in the solution tree is the one you know it is. At any rate, you're lame.

Comments are closed.

Skip to main content