2

I would like to link two libraries to my program. The first one, defines an object (of class ProducerObject) which emits a signal. The second library defines two classes: a slave class which contains several instances of ProducerObject and a master class which can subscribe to the signals of the ProducerObject throught the slave class.

The code of the first lib is:
ProducerObject.hpp:

#ifndef PRODUCEROBJECT_HPP_
#define PRODUCEROBJECT_HPP_

#include <boost/shared_ptr.hpp>
#include <boost/signals2.hpp>
#include <boost/function.hpp> // shouldn't be needed


namespace testnm {

class ProducerObject {
public:
    typedef boost::signals2::signal<void (boost::shared_ptr<int>)> broadcast_signal_t;
    typedef broadcast_signal_t::slot_type broadcast_slot_t;

private:
    broadcast_signal_t  sig_;

public:
    ProducerObject();
    virtual ~ProducerObject();

    boost::signals2::connection connect(const broadcast_slot_t& slot);
    boost::signals2::connection connect2(const boost::function<void(boost::shared_ptr<int>)>& slot); // shouldn't be needed
    void sendMessage(boost::shared_ptr<int> msg);

};

} /* namespace testnm */
#endif /* PRODUCEROBJECT_HPP_ */

ProducerObject.cpp:

#include "ProducerObject.hpp"

namespace testnm {

ProducerObject::ProducerObject() {

}

ProducerObject::~ProducerObject() {

}

boost::signals2::connection ProducerObject::connect(const broadcast_slot_t& slot) {
    return sig_.connect(slot);
}

// shouldn't be needed
boost::signals2::connection ProducerObject::connect2(const boost::function<void(boost::shared_ptr<int>)>& slot) {
    return connect(slot);
}

void ProducerObject::sendMessage(boost::shared_ptr<int> msg) {
    sig_(msg);
}

} /* namespace testnm */

Compile this in a Debug directory which is at the same level as the code with the command:

g++ -O0 -g3 -Wall -c -fmessage-length=0 -fPIC -MMD -MP -MF"ProducerObject.d" -MT"ProducerObject.d" -o "ProducerObject.o" "../ProducerObject.cpp"

g++ -shared -o "libProducerObject.so" ./ProducerObject.o

The code of the second library is:
MasterAPI.hpp:

#ifndef MASTERAPI_HPP_
#define MASTERAPI_HPP_

#include "SlaveAPI.hpp"

#include <ProducerObject.hpp>

#include <iostream>

#include <boost/shared_ptr.hpp>
#include <boost/bind.hpp>


namespace testnm {

class MasterAPI {
private:
    int id_;

public:
    MasterAPI(const int& id);
    virtual ~MasterAPI();

    void subscribeToProducer(boost::shared_ptr<SlaveAPI> slave, const unsigned long int& producerid);

    virtual void onMessage(boost::shared_ptr<int> msg);

};

} /* namespace testnm */
#endif /* MASTERAPI_HPP_ */

SlaveAPI.hpp:

#ifndef SLAVEAPI_HPP_
#define SLAVEAPI_HPP_

#include <ProducerObject.hpp>

#include <boost/shared_ptr.hpp>

namespace testnm {

class MasterAPI; // forward declaration

class SlaveAPI {
public:
    SlaveAPI();
    virtual ~SlaveAPI();

    virtual boost::shared_ptr<ProducerObject> getProducer(const unsigned long int& producerid) const = 0;

    void sendDedicatedMessage(const boost::shared_ptr<MasterAPI> master, boost::shared_ptr<int> msg);

};

} /* namespace testnm */

#include "MasterAPI.hpp" // full declaration of the 'MasterAPI' class
                         // (used in the implementation of the method 'sendDedicatedMessage')

#endif /* SLAVEAPI_HPP_ */

SlaveBase.hpp:

#ifndef SLAVEBASE_HPP_
#define SLAVEBASE_HPP_

#include "SlaveAPI.hpp"

#include <ProducerObject.hpp>

#include <unordered_map>

#include <boost/shared_ptr.hpp>


namespace testnm {

class SlaveBase : public SlaveAPI {
private:
    std::unordered_map<unsigned long int, boost::shared_ptr<ProducerObject> >   producers_;

public:
    SlaveBase();
    virtual ~SlaveBase();

    virtual boost::shared_ptr<ProducerObject> getProducer(const unsigned long int& producerid) const;

};

} /* namespace testnm */
#endif /* SLAVEBASE_HPP_ */

MasterAPI.cpp:

#include "MasterAPI.hpp"

namespace testnm {

MasterAPI::MasterAPI(const int& id) :id_(id) {

}

MasterAPI::~MasterAPI() {

}

void MasterAPI::subscribeToProducer(boost::shared_ptr<SlaveAPI> slave, const unsigned long int& producerid) {
    boost::shared_ptr<ProducerObject> producer = slave->getProducer(producerid);
    //producer->connect((ProducerObject::broadcast_slot_t)(boost::bind(&MasterAPI::onMessage, this, _1))); // shouldn't be commented
    producer->connect2(boost::bind(&MasterAPI::onMessage, this, _1)); // shouldn't be needed
}

void MasterAPI::onMessage(boost::shared_ptr<int> msg) {
    std::cout << "Message received in MasterAPI(" << id_ << "): value = " << *msg << std::endl;
}

} /* namespace testnm */

SlaveAPI.cpp:

#include "SlaveAPI.hpp"

namespace testnm {

SlaveAPI::SlaveAPI() {

}

SlaveAPI::~SlaveAPI() {

}

void SlaveAPI::sendDedicatedMessage(const boost::shared_ptr<MasterAPI> master, boost::shared_ptr<int> msg) {
    master->onMessage(msg);
}

} /* namespace testnm */

SlaveBase.cpp:

#include "SlaveBase.hpp"

namespace testnm {

SlaveBase::SlaveBase() {
    boost::shared_ptr<ProducerObject> producer(new ProducerObject);
    producers_[1] = producer;
    producer.reset(new ProducerObject);
    producers_[2] = producer;
}

SlaveBase::~SlaveBase() {

}

boost::shared_ptr<ProducerObject> SlaveBase::getProducer(const unsigned long int& producerid) const {
    std::unordered_map<unsigned long int, boost::shared_ptr<ProducerObject> >::const_iterator prod_it = producers_.find(producerid);
    if (prod_it == producers_.end())
        return boost::shared_ptr<ProducerObject>();
    return prod_it->second;
}

} /* namespace testnm */

Compile this code with:

g++ -I"/home/chris/workspace/ProducerObject" -O0 -g3 -Wall -c -fmessage-length=0 -std=c++0x -fPIC -MMD -MP -MF"MasterAPI.d" -MT"MasterAPI.d" -o "MasterAPI.o" "../MasterAPI.cpp"

g++ -I"/home/chris/workspace/ProducerObject" -O0 -g3 -Wall -c -fmessage-length=0 -std=c++0x -fPIC -MMD -MP -MF"SlaveAPI.d" -MT"SlaveAPI.d" -o "SlaveAPI.o" "../SlaveAPI.cpp"

g++ -I"/home/chris/workspace/ProducerObject" -O0 -g3 -Wall -c -fmessage-length=0 -std=c++0x -fPIC -MMD -MP -MF"SlaveBase.d" -MT"SlaveBase.d" -o "SlaveBase.o" "../SlaveBase.cpp"

g++ -L"/home/chris/workspace/ProducerObject/Debug" -shared -o "libObjectAPI.so" ./MasterAPI.o ./SlaveAPI.o ./SlaveBase.o -lProducerObject

Finally, the programm itself:
ObjectTest.cpp:

#include <ProducerObject.hpp>
#include <SlaveBase.hpp>
#include <MasterAPI.hpp>

#include <iostream>

#include <boost/shared_ptr.hpp>


using namespace std;
using namespace testnm;

int main(int argc, char* argv[]) {
    boost::shared_ptr<SlaveBase> slave(new SlaveBase);
    boost::shared_ptr<MasterAPI> master1(new MasterAPI(1)), master2(new MasterAPI(2));

    master1->subscribeToProducer(slave, 1);
    master1->subscribeToProducer(slave, 2);
    master2->subscribeToProducer(slave, 1);

    cout << "--> sending a dedicated message to master1:" << endl;
    boost::shared_ptr<int> msg(new int(11));
    slave->sendDedicatedMessage(master1, msg);

    cout << "\n--> sending a broadcast message from procuder1:" << endl;
    boost::shared_ptr<ProducerObject> producer1 = slave->getProducer(1);
    msg.reset(new int(22));
    producer1->sendMessage(msg);

    cout << "\n--> sending a broadcast message from procuder2:" << endl;
    boost::shared_ptr<ProducerObject> producer2 = slave->getProducer(2);
    msg.reset(new int(33));
    producer2->sendMessage(msg);

    return 0;
}

Compile with:

g++ -I"/home/chris/workspace/ProducerObject" -I"/home/chris/workspace/ObjectAPI" -O0 -g3 -Wall -c -fmessage-length=0 -std=c++0x -MMD -MP -MF"ObjectTest.d" -MT"ObjectTest.d" -o "ObjectTest.o" "../ObjectTest.cpp"

g++ -L"/home/chris/workspace/ProducerObject/Debug" -L"/home/chris/workspace/ObjectAPI/Debug" -o "ObjectTest" ./ObjectTest.o -lObjectAPI -lProducerObject

This program produces the following output, which is exactly what is wanted:

--> sending a dedicated message to master1:
Message received in MasterAPI(1): value = 11

--> sending a broadcast message from procuder1:
Message received in MasterAPI(1): value = 22
Message received in MasterAPI(2): value = 22

--> sending a broadcast message from procuder2:
Message received in MasterAPI(1): value = 33

To produce this output, I had to rely on the auxiliary method connect2 which takes a boost::function as argument. I would like to avoid this as the signature of the slot should already be contained in the *broadcast_slot_t* type. So, the version of the code I would like to achieve would not contain the connect2 method. But if I switch from connect2 to connect in the implementation of the method MasterAPI::subscribeToProducer (simply change the comments to test), the last compilation phase (where everything is linked together) produces the following error:

> /home/chris/workspace/ObjectAPI/Debug/libObjectAPI.so: undefined reference to `testnm::ProducerObject::connect(boost::signals2::slot<void (boost::shared_ptr<int>), boost::function<void (boost::shared_ptr<int>)> > const&)'  
> collect2: error: ld returned 1 exit status  
> make: *** [ObjectTest] Error 1

Why and how to avoid it?

curiousguy
  • 8,038
  • 2
  • 40
  • 58
Chris
  • 23
  • 4
  • Why do you use `-L` to add directories to the search path of `-l`, but not `-l`? – curiousguy Jun 25 '13 at 01:45
  • Well, I just created a sample based on my program to illustrate my question. I used Eclipse (CDT) and the commands to compile are those generated by Eclipse. But if you look well, you will see that `-l` is used to include the libraries. – Chris Jun 25 '13 at 16:26
  • Why do you have a `(const int& id)` argument and not just `int`? – curiousguy Jun 25 '13 at 18:37
  • I guess that you talk about the constructor of `MasterAPI`. It is a practice. If it is wrong, help me improve my skills. I usually do it this way when I just want to read information in a method or function. I pass the parameters as reference in order to avoid the copy step which can be quite heavy if the parameter is an object and not an integer, and in order to be sure that I do not modify the object, I block it with the `const` qualifier. – Chris Jun 25 '13 at 19:45
  • Avoiding an unneeded copy of a `vector` is often a good idea. Avoiding an unneeded copy of an `int`, not so much. Passing a reference probably has the same cost as passing an `int`, and then the indirection has a cost. – curiousguy Jun 26 '13 at 02:32
  • Thanks curiousguy for you input. Your comments are useful and appreciated. – Chris Jun 26 '13 at 16:53

1 Answers1

1

To cut a long story short, you cannot combine objects compiled with -stdc=c++0x and those compiled without, ever. You are lucky to get a linker error instead of a mysterious crash.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
  • This is simply this flag, thanks! This is a tricky one when you don't know. You indeed don't always get the linker error (see the version which works simply fine). Good lesson, thanks again :-) – Chris Jun 26 '13 at 16:58