0

I'm getting this error from the linker:

1>PACBalancesTest.obj : error LNK2001: unresolved external symbol "public: bool __thiscall PAC::BalChgKeyComparator::operator()(class PAC::BalChgKey const &,class PAC::BalChgKey const &)const " (??RBalChgKeyComparator@PAC@@QBE_NABVBalChgKey@1@0@Z)

I must be missing something really obvious, because I've looked at the definition of the "missing" symbol repeatedly and can't see any problem.

The symbol's definition is in a .lib file. I see the following in the output from dumpbin /symbols on that .lib file:

2F0 00000000 SECTFD notype ()    External     | ??RBalChgKeyComparator@PAC@@QBE_NABVBalChgKey@1@0@Z (public: bool __thiscall PAC::BalChgKeyComparator::operator()(class PAC::BalChgKey const &,class PAC::BalChgKey const &)const )

And there are other symbols being resolved successfully from that .lib file! (In fact, from the same .obj.) [Update: I no longer think the preceding statement is true. This may be my first attempt to access any function not defined in a .h file.]

WHAT FOLLOWS IS NOT RELEVANT TO THE PROBLEM -- SO PLEASE DON'T SPEND TIME STUDYING IT!

Here's the declaration of the function (in PACBalances.h):

namespace PAC {
    class BalChgKey {
    public:
        ...
    };

    struct BalChgKeyComparator {
        bool operator()(const BalChgKey& lhs, const BalChgKey& rhs) const;
    };

    typedef std::multimap<BalChgKey, long, BalChgKeyComparator> BalChgKeyLongMMap;
};

Note that I've tried changing 'struct' above to 'class', with no effect.

Here is the calling code (in a cpputest test file):

#include "CppUTest/TestHarness.h"

#include <utility>
#include <map>
#include "PACBalances.h"

using namespace PAC;

...

TEST_GROUP(PACBalanceCUMap)
{
    BalChgKeyLongMMap empty;
    BalChgKeyLongMMap onesy;

    void setup()
    {
        // **Adding the following line caused this error to start to occur.**
        onesy.insert(std::pair<BalChgKey, long>(BalChgKey(BOPCAT_FEE, PAYMTYPE_OVERDRAFT_FEE, 4321, 41100, 1, 17), 17));
    }
    void breakdown()
    {
    }
};

And here is the definition of the operator function itself:

bool PAC::BalChgKeyComparator::operator()(
    const BalChgKey& thing1, 
    const BalChgKey& thing2
    ) const
{
    if (thing1.m_balKey.m_balCat < thing2.m_balKey.m_balCat)        return true;
    else if (thing1.m_balKey.m_balCat > thing2.m_balKey.m_balCat)   return false;
    // Fall thru if balCats are equal
    ...
    return false;
}

Please note that:

  1. The comparator, and the typedef'd multimap, works beautifully in lots of code (not shown above).
  2. The test file calls lots of other functions declared and defined in that .h file, but this is the first time I've tried calling a function defined in a separate .cpp file.
  3. My question is not why does the 'onesy.insert' call requires the comparator function. I understand that. It just happens to be the first multimap operation that I've coded in the test set that actually uses the comparator.

I have various hunches, but I'm running out of them, so if someone who knows about this stuff can give me any leads I would be very grateful.

Norm

  • Is the source file in which the `operator()` is defined part of the MSVC project? That is to say, does MSVC know it should compile and link it? – Wintermute Dec 15 '14 at 16:49
  • 1
    And did you actually link that compilation unit that contains the `PAC::BalChgKeyComparator::operator()` definition? – πάντα ῥεῖ Dec 15 '14 at 16:49
  • @Wintermute: Yes, and it does in fact compile it and produces a .lib file containing its object. – Norman Birkett Dec 15 '14 at 16:55
  • @πάνταῥεῖ: I'm beginning to think that may be my problem. I just turned on /VERBOSE:Lib for the link step, and my .lib file is missing from the list. My ignorance of the VS configuration is probably entering in here. My solution has a dependency of my test runner on the project that builds the .lib, but I suspect I also need to tell the testrunner project about the .lib. – Norman Birkett Dec 15 '14 at 16:58

1 Answers1

0

@panta rei: You provided the key in your comment. (Sorry, can't figure out how to type Greek letters here.)

The problem was basically that I didn't know how to tell Visual Studio what objects to link in. I had told my solution that AnalyticsUTest depended on AnalysticsUTested, but the linking step is performed by the project (AnalyticsUTest), not the solution, so I needed to tell the project to include this .lib file.

So I went to the project's properties sheet and created two new macros, one giving the folder where VS was putting my .lib file (ANALYTICSUTESTED_LIB_PATHS), the other giving the name of my .lib file (ANALYTICSUTESTED_LIB_DEPENDENCIES) -- both by analogy with the CPPUTEST_LIB* macros.

And then I added $(ANALYTICSUTESTED_LIB_PATHS) to Linker > General > Additional Library Directories. And I added $(ANALYTICSUTESTED_LIB_DEPENDENCIES) to Linker > Input > Additional Dependencies.

And that fixed my problem! (I've spelled it out here in case another newbie like me comes along and needs it.)

Thank you, panta rei. How do I give you points for an answer provided via a comment?