[Solved] QtTest project Linker errors on VS2010



  • Hey guys,

    I've been having a problem trying to integrate QtTests into an existing project, and it's kindly been responding with ridiculous linker errors. I'm probably doing something wrong, but i can't even begin to know what.
    We're using Vs2010 on x64 versions of windows.

    Visual studio actually finds the appropriate header files, and the dependencies should be setup properly (project dependencies and add the SurprisedWhale). and yet, it keeps throwing these errors:

    @1>------ Build started: Project: SurprisedWhale, Configuration: Debug Win32 ------
    1> SurprisedWhale.vcxproj -> H:\Programming\SurprisedWhale\SurprisedWhale.exe
    2>------ Build started: Project: SurprisedQtTest, Configuration: Debug Win32 ------
    2>GateTest.obj : error LNK2019: unresolved external symbol "public: __thiscall Gate::~Gate(void)" (??1Gate@@QAE@XZ) referenced in function "private: void __thiscall GateTest::TypeTest(void)" (?TypeTest@GateTest@@AAEXXZ)
    2>GateTest.obj : error LNK2019: unresolved external symbol "public: __thiscall Gate::Gate(enum Gate::Type)" (??0Gate@@QAE@W4Type@0@@Z) referenced in function "private: void __thiscall GateTest::TypeTest(void)" (?TypeTest@GateTest@@AAEXXZ)
    2>H:\Programming\SurprisedWhale\SurprisedQtTest.exe : fatal error LNK1120: 2 unresolved externals
    ========== Build: 1 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========@

    The files involved are:

    GateTest.h
    @#ifndef GATETEST_H
    #define GATETEST_H

    #include <QtTest>
    class GateTest : public QObject
    {
    Q_OBJECT
    private slots:
    void toUpper();
    void TypeTest();
    };

    #endif@

    GateTest.cpp

    @#include "GateTest.h"
    #include "../SurprisedWhale/Gate.h"

    void GateTest::TypeTest(){
    Gate gate = Gate(Gate::NOT);
    QCOMPARE((int)gate.getType(),0);
    }
    void GateTest::toUpper()
    {
    QString str = "Hello";
    QCOMPARE(str.toUpper(), QString("HELLO"));
    };@

    If anyone has a clever idea, i'm dying to hear it.


  • Moderators

    welcome to devnet

    I am wondering, if the cause is that you do not have a public section. Anyhow, you are using a constructor in line 6 of GateTest.cpp. You need to declare and implement this constructor, which will solve also the missing public section question.



  • I doubt that's the entire problem. The constructor i'm calling is defined in an external project, which i've included in the .cpp file. Logically, you'd say i'd be able to call it, and test it as required.

    Note that the second test actually ran fine, so it's definitely related to the constructor.


  • Moderators

    Do you compile Gate.cpp and link with Gate.obj ?



  • You didn't told us in which way your Gate was used in this project, so I think you did something wrong.

    • Is your Gate in another shared or static library?

    If yes, seems you have forgot to link to the library.

    If no, seems you have forgot to some files such as Gate.cpp to your project.



  • It should be, and it seems linked. I've added the entire project as a dependency, and VS's options claims to link them. The files even show up in the external dependency list.

    These are the exact steps i've performed in VS2010 to get here:

    Write the original project, including Gate.cpp and Gate.h

    Add a new test project to the original project's solution.

    Added the QtTest module to the test project.

    Add the original project's main folder as a dependency

    Write the toUpper() test and successfully build.

    Write TypeTest(), and get the linker errors.

    So anyway, im probably missing a step. I'm just not entirely sure which.


  • Moderators

    The link output says that Gate constructor and Gate ~destructor are missing. As suggested before this looks like you are not linking the Gate.obj to your exe and is missing. If the project is relatively small and has only a couple of file, just do a recompile of GateTest project and see, if Gate.cpp is also complied.



  • I just did, it does compile Gate.cpp. Hence the confusion.


  • Moderators

    Did you check also the whole syntax of constructor and destructor?
    May be you should post header and source file of Gate.



  • They compile and function just fine, but i'll post them.

    Gate.h
    @#ifndef GATE_H
    #define GATE_H

    #include "Element.h"
    #include "Connector.h"
    #include "globals.h"
    #include "Protein.h"

    #include <vector>

    using namespace std;

    class Connector;

    //Gate represents a certain gate-type, which works with proteins.
    class Gate : public Element {
    //Gate Types Available.
    public:
    enum Type {
    AND,
    NOT,
    OR,
    NOR,
    XOR,
    NAND
    };

    protected:
    int status;

    vector<Connector*> input;
    vector<Connector*> output;
    Type type;

    public:
    explicit Gate(enum Type);
    Gate(int);
    Gate(enum Type, Element*);
    Gate(enum Type, Element*, Element*);
    ~Gate();

    void init(enum Type t);

    bool eval();
    void connectInput0(Element*);
    void connectInput1(Element*);
    void connectBoth(Element*, Element*);
    void disconnectInput0();
    void disconnectInput1();
    void disconnectBoth();
    Connector* getInput(int);
    Connector* getOutput(int);

    vector<Connector*> getInputVector(){return input;};
    vector<Connector*> getOutputVector(){return output;};

    Element* getInput0Elem();
    Element* getInput1Elem();

    int getType(){return type;};
    int getId() {return id;};
    virtual int getStatus() {return status;};
    };

    #endif
    @

    Gate.cpp
    @#include "Gate.h"
    #include "globals.h"
    #include "Connector.h"
    #include "Element.h"

    //Create Gate with type alone.
    Gate::Gate(enum Type t) {
    init(t);
    type = t;
    this->evaled = false;
    }
    Gate::Gate(int i) {
    Type t = (Type) i;
    init(t);
    type = t;
    this->evaled = false;
    }

    //Create Gate with type and 1 element to connect to.
    Gate::Gate(enum Type t, Element* l) {
    init(t);
    if(l) {
    connectInput0(l);
    } else {
    throw error();
    }
    type = t;
    this->evaled = this->eval();
    }

    //Create Gate with type and 2 elements to connect to.
    Gate::Gate(enum Type t, Element* l, Element* r) {
    //Not gate is not allowed to have two connections made.
    if((type = t) == Gate::NOT) {
    throw error();
    }

    init(t);

    //nullptr checks.
    if(l) {
    connectInput0(l);
    } else {
    throw error();
    }

    if(r) {
    connectInput1(r);
    } else {
    throw error();
    }

    //TODO remove debug value.
    this->evaled = this->eval();
    }

    Gate::~Gate() {
    for(int i = 0; i < input.size(); i++) {
    delete input.at(i);
    }

    for(int i = 0; i < output.size(); i++) {
    delete output.at(i);
    }
    }

    // This function is used to evaluate if this gate is true,
    // according to the gate-type and connections.
    bool Gate::eval() {
    // If nothing connected, default to false else call eval on the connection.
    bool l = this->getInput(0)->isConnected() ?
    this->getInput(0)->eval() : false;

    bool r = false;
    if(this->type != Gate::NOT) {
    r = this->getInput(1)->isConnected() ?
    this->getInput(1)->eval() : false;
    }

    bool result = false;

    //eval according to Gate::Type
    switch(this->type) {
    case AND :
    result = (l && r);break;
    case NOT :
    result = !(l);break;
    case OR :
    result = (l || r);break;
    case NOR :
    result = !(l || r);break;
    case XOR :
    result = (!l && r || l && !r);break;
    case NAND :
    result = !(l && r);break;
    default :
    break;
    }

    //set the gate status accordingly, used for GUI representation.
    result ? this->status = 1 : this->status = -1;
    return result;
    }

    // function used to connect p to the left connector.
    // if nullptr, disconnect.
    void Gate::connectInput0(Element *e) {
    if(e == nullptr) {
    this->getInput(0)->disconnect();
    } else {
    this->getInput(0)->connect(e);
    }
    }

    // function used to connect p to the right connector.
    void Gate::connectInput1(Element *e) {
    if(e == nullptr) {
    this->getInput(1)->disconnect();
    } else {
    this->getInput(1)->connect(e);
    }
    }

    // function used to connect both connectors at once.
    // warning, passing any of the Elements as a nullptr
    // will disconnect any existing connection.
    void Gate::connectBoth(Element* e0, Element *e1) {
    connectInput0(e0);
    connectInput1(e1);
    }

    // disconnects left.
    void Gate::disconnectInput0() {
    this->getInput(0)->disconnect();
    }

    // disconnects right.
    void Gate::disconnectInput1() {
    if(this->type != Gate::NOT) {
    this->getInput(1)->disconnect();
    } else {
    throw error();
    }
    }

    // disconnects both.
    void Gate::disconnectBoth() {
    disconnectInput0();
    disconnectInput1();
    }

    Connector* Gate::getInput(int i) {
    if(i == 1) {
    if (this->type == Gate::NOT) {
    throw error();
    }
    }
    return this->input.at(i);
    }
    //returns the Element connected to the left connector.
    Element* Gate::getInput0Elem() {
    if(this->getInput(0)->isConnected()) {
    return this->getInput(0)->traverse();
    }
    //think of throwing error here..
    return nullptr;
    }

    //returns the Element connected to the right connector.
    Element* Gate::getInput1Elem() {
    if(this->type == Gate::NOT) {
    throw error();
    } else {
    if(this->getInput(1)->isConnected()) {
    return this->getInput(1)->traverse();
    }
    //think of throwing error here..
    }
    return nullptr;
    }

    //initilize all needed variables
    void Gate::init(enum Type t) {
    this->id = currElementId++;
    this->evaled = 0;

    if(t == Gate::NOT) {
    this->input = vector<Connector*>(1);
    } else {
    this->input = vector<Connector*>(2);
    }
    this->output = vector<Connector*>(1);

    for(int i = 0; i < input.size(); i++) {
    this->input.at(i) = new Connector(this);
    }

    for(int i = 0; i < output.size(); i++) {
    this->output.at(i) = new Connector(this);
    }
    }@


  • Moderators

    You got 2 constructors with almost the same parameter list. Both parameters are basically int.
    @public:
    explicit Gate(enum Type);
    Gate(int);
    @

    Together with explicit, that could be the source of a problem. However, that is more poking in the fog and I would not do it this way. Therefore, my recommendation would be to simplify and make it homogeneous with either constructor.

    However, this should not be a problem for the destructor. So, it is a bit fuzzy, but may be you should go step by step.



  • That was actually a stab in the dark on my end, back when i thought enumerators were to blame. without it the same thing happened.

    Anyway, i've changed the test to a notably less complex class, and the same linker errors still occur.

    new file in question:
    QConnector.h
    @#ifndef CONNECTORITEM_H
    #define CONNECTORITEM_H

    #include "qgraphicsitem.h"

    class QConnector: public QGraphicsItem
    {
    public:
    QConnector(QGraphicsItem*);
    ~QConnector(void);
    void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*);
    QRectF boundingRect() const;
    int parX, parY, parW, parH;

    };

    #endif@

    and the test:
    @void GateTest::connTest(){
    QConnector* conn = new QConnector(0);
    QCOMPARE(1,1);
    }@

    That said, which steps do you mean by step by step.


  • Moderators

    [quote author="Link0" date="1337076512"]
    That said, which steps do you mean by step by step. [/quote]

    Removing the duality of constructors and seeing what the next issue is.

    If you the same (should be a different now, because you changed classes) linking error, it has something to do with the linking process. Use a small example and pack everything into one source and one header file. This should work. After this separate the files. This may help to localize your problem.



  • [quote author="Link0" date="1337068365"]

    Write the original project, including Gate.cpp and Gate.h

    Add a new test project to the original project's solution.

    Added the QtTest module to the test project.

    Add the original project's main folder as a dependency

    Write the toUpper() test and successfully build.

    Write TypeTest(), and get the linker errors.

    [/quote]

    Seems you did something basicly wrong.

    As I said before, if your original project is library, you should link the library to your test project. if not, you should add the source files to your test project.

    "dependency" don't help you to solve the problem. They are still two dependent projects, the only side effect is that when you build your test project, original project will be build first.

    Debao



  • Right you are! Had to add the folder under additional include directories. Apparently no one ever did. Thank you for both of your time.

    (they really need to make VS more straightforward)

    EDIT: wait. dang. i had the relevant code commented in this version. problem persists.



  • Some testing later.

    Putting all code into a header file and then including that worked, as soon as i pull the relevant code back into a cpp file, the linker errors return.

    As for the other idea, It's not a library, so i can't link to it statically.
    Attempting to add the .cpp file to the project did not work either, surprisingly, and i'm not entirely sure if i should do that in any case, considering the code changes and such.


  • Moderators

    [quote author="Link0" date="1337068365"]
    These are the exact steps i've performed in VS2010 to get here:

    Write the original project, including Gate.cpp and Gate.h

    Add a new test project to the original project's solution.

    Added the QtTest module to the test project.

    Add the original project's main folder as a dependency

    Write the toUpper() test and successfully build.

    Write TypeTest(), and get the linker errors.

    [/quote]

    What do you actually mean with:

    Add the original project's main folder as a dependency

    ??

    Are you adding the files of your original project as "existing items" ?



  • Visual Studio allows me to define projects as "project dependencies". It also allows me to add them as references, but the point of that is still eluding me as well. (linking errors in that case too)


  • Moderators

    The project dependency is for projects with libs. You got 3 projects lib1, lib2 and a main. You change main, it will compile main and link the lib1 and lib2 to main to get exe. You change lib2, lib2 will be compiled and linked with lib1 and main to exe. And whatever mutations.

    However, you have libs and the outcome, the libs, has to be linked to your project. To my understanding you have no libs, but you got two exe in two projects. For this it does not work. You need to add the source and header files as "existing items" to your test applications. They will be compiled again and linked to your test application.

    Otherwise you can create a lib project and two application projects. However, you need to link to both application projects the lib. Otherwise it will not work.



  • Yeah, I've been trying to split the code into a library for the last hour or so. Getting some rather profound linker errors on a specific class so I'll need to look into that.



  • There, fixed them. Some weird or nonstandard includes throwing a fit. The tests now work as they should, and there's no more linker errors. This time for real, i double checked.

    Thank you again for your support, both of you.


  • Moderators

    Good to know that your problem has been solved.

    I marked your post as [Solved]. Please do next time.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.