I'm just starting out with C++ and am learning how to use QT creator as the IDE so I suspect this might be really easy but I might be missing something. I installed apollo MQ and installed the apache CMS(C++ messaging service) so I can interact with the server in C++. The problem is, I have no clue what I'm doing :-) (yet), and am not sure why I'm getting the above error when using the examples.
I am pretty sure the examples are good because they are included in the source's example folder so it leads me to suspect that the configuration of the project is not correct(as its the only ting I really have an influence on).
Here's the .pro file:
TEMPLATE = app
CONFIG += console
CONFIG -= qt
SOURCES += main.cpp
INCLUDEPATH += /usr/local/include/activemq-cpp-3.4.2/
INCLUDEPATH += /opt/local/lib/
Here is the folder contents where I'm pointing to(the files I want are all within the subdirectories there):
drwxr-xr-x 13 root admin 442 May 11 19:09 activemq
drwxr-xr-x 48 root admin 1632 May 11 19:09 cms
drwxr-xr-x 9 root admin 306 May 11 19:09 decaf
Here's the error I get:
20:54:22: Running build steps for project stackexchangeexample...
20:54:22: Configuration unchanged, skipping qmake step.
20:54:22: Starting: "/usr/bin/make" -w
make: Entering directory `/Users/lostsoul/Dropbox/qt_cuda/stackexchangeexample-build-desktop-Desktop_Qt_4_8_1_for_GCC__Qt_SDK__Debug'
/Users/lostsoul/QtSDK/Desktop/Qt/4.8.1/gcc/bin/qmake -spec ../../../QtSDK/Desktop/Qt/4.8.1/gcc/mkspecs/macx-g++ CONFIG+=declarative_debug -o Makefile ../stackexchangeexample/stackexchangeexample.pro
make: Leaving directory `/Users/lostsoul/Dropbox/qt_cuda/stackexchangeexample-build-desktop-Desktop_Qt_4_8_1_for_GCC__Qt_SDK__Debug'
make: Entering directory `/Users/lostsoul/Dropbox/qt_cuda/stackexchangeexample-build-desktop-Desktop_Qt_4_8_1_for_GCC__Qt_SDK__Debug'
g++ -headerpad_max_install_names -arch x86_64 -Xarch_x86_64 -mmacosx-version-min=10.5 -o stackexchangeexample.app/Contents/MacOS/stackexchangeexample main.o
Undefined symbols for architecture x86_64:
"activemq::library::ActiveMQCPP::initializeLibrary()", referenced from:
_main in main.o
"activemq::library::ActiveMQCPP::shutdownLibrary()", referenced from:
_main in main.o
"activemq::core::ActiveMQConnectionFactory::ActiveMQConnectionFactory(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)", referenced from:
SimpleProducer::run() in main.o
"cms::CMSException::CMSException(cms::CMSException const&)", referenced from:
SimpleProducer::run() in main.o
"typeinfo for cms::CMSException", referenced from:
GCC_except_table15 in main.o
GCC_except_table16 in main.o
SimpleProducer::run() in main.o
"cms::CMSException::~CMSException()", referenced from:
SimpleProducer::run() in main.o
"decaf::lang::Thread::getId()", referenced from:
SimpleProducer::run() in main.o
"decaf::lang::Long::toString(long long)", referenced from:
SimpleProducer::run() in main.o
ld: symbol(s) not found for architecture x86_64
collect2: ld returned 1 exit status
make: *** [stackexchangeexample.app/Contents/MacOS/stackexchangeexample] Error 1
make: Leaving directory `/Users/lostsoul/Dropbox/qt_cuda/stackexchangeexample-build-desktop-Desktop_Qt_4_8_1_for_GCC__Qt_SDK__Debug'
20:54:22: The process "/usr/bin/make" exited with code 2.
Error while building project stackexchangeexample (target: Desktop)
When executing build step 'Make'
and if it helps, here's the code I run(it may not be useful to anyone for helping because you need to install a bunch of stuff to get it work, but just in case it helps):
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <decaf/lang/Thread.h>
#include <decaf/lang/Runnable.h>
#include <decaf/util/concurrent/CountDownLatch.h>
#include <decaf/lang/Long.h>
#include <decaf/util/Date.h>
#include <activemq/core/ActiveMQConnectionFactory.h>
#include <activemq/util/Config.h>
#include <activemq/library/ActiveMQCPP.h>
#include <cms/Connection.h>
#include <cms/Session.h>
#include <cms/TextMessage.h>
#include <cms/BytesMessage.h>
#include <cms/MapMessage.h>
#include <cms/ExceptionListener.h>
#include <cms/MessageListener.h>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <memory>
using namespace activemq;
using namespace activemq::core;
using namespace decaf;
using namespace decaf::lang;
using namespace decaf::util;
using namespace decaf::util::concurrent;
using namespace cms;
using namespace std;
////////////////////////////////////////////////////////////////////////////////
class SimpleProducer : public Runnable {
private:
Connection* connection;
Session* session;
Destination* destination;
MessageProducer* producer;
bool useTopic;
bool clientAck;
unsigned int numMessages;
std::string brokerURI;
std::string destURI;
private:
SimpleProducer( const SimpleProducer& );
SimpleProducer& operator= ( const SimpleProducer& );
public:
SimpleProducer( const std::string& brokerURI, unsigned int numMessages,
const std::string& destURI, bool useTopic = false, bool clientAck = false ) :
connection(NULL),
session(NULL),
destination(NULL),
producer(NULL),
useTopic(useTopic),
clientAck(clientAck),
numMessages(numMessages),
brokerURI(brokerURI),
destURI(destURI) {
}
virtual ~SimpleProducer(){
cleanup();
}
void close() {
this->cleanup();
}
virtual void run() {
try {
// Create a ConnectionFactory
auto_ptr<ActiveMQConnectionFactory> connectionFactory(
new ActiveMQConnectionFactory( brokerURI ) );
// Create a Connection
try{
connection = connectionFactory->createConnection();
connection->start();
} catch( CMSException& e ) {
e.printStackTrace();
throw e;
}
// Create a Session
if( clientAck ) {
session = connection->createSession( Session::CLIENT_ACKNOWLEDGE );
} else {
session = connection->createSession( Session::AUTO_ACKNOWLEDGE );
}
// Create the destination (Topic or Queue)
if( useTopic ) {
destination = session->createTopic( destURI );
} else {
destination = session->createQueue( destURI );
}
// Create a MessageProducer from the Session to the Topic or Queue
producer = session->createProducer( destination );
producer->setDeliveryMode( DeliveryMode::NON_PERSISTENT );
// Create the Thread Id String
string threadIdStr = Long::toString( Thread::getId() );
// Create a messages
string text = (string)"Hello world! from thread " + threadIdStr;
for( unsigned int ix=0; ix<numMessages; ++ix ){
TextMessage* message = session->createTextMessage( text );
message->setIntProperty( "Integer", ix );
// Tell the producer to send the message
printf( "Sent message #%d from thread %s\n", ix+1, threadIdStr.c_str() );
producer->send( message );
delete message;
}
}catch ( CMSException& e ) {
e.printStackTrace();
}
}
private:
void cleanup(){
// Destroy resources.
try{
if( destination != NULL ) delete destination;
}catch ( CMSException& e ) { e.printStackTrace(); }
destination = NULL;
try{
if( producer != NULL ) delete producer;
}catch ( CMSException& e ) { e.printStackTrace(); }
producer = NULL;
// Close open resources.
try{
if( session != NULL ) session->close();
if( connection != NULL ) connection->close();
}catch ( CMSException& e ) { e.printStackTrace(); }
try{
if( session != NULL ) delete session;
}catch ( CMSException& e ) { e.printStackTrace(); }
session = NULL;
try{
if( connection != NULL ) delete connection;
}catch ( CMSException& e ) { e.printStackTrace(); }
connection = NULL;
}
};
////////////////////////////////////////////////////////////////////////////////
int main(int argc AMQCPP_UNUSED, char* argv[] AMQCPP_UNUSED) {
activemq::library::ActiveMQCPP::initializeLibrary();
std::cout << "=====================================================\n";
std::cout << "Starting the example:" << std::endl;
std::cout << "-----------------------------------------------------\n";
// Set the URI to point to the IPAddress of your broker.
// add any optional params to the url to enable things like
// tightMarshalling or tcp logging etc. See the CMS web site for
// a full list of configuration options.
//
// http://activemq.apache.org/cms/
//
// Wire Format Options:
// =====================
// Use either stomp or openwire, the default ports are different for each
//
// Examples:
// tcp://127.0.0.1:61616 default to openwire
// tcp://127.0.0.1:61616?wireFormat=openwire same as above
// tcp://127.0.0.1:61613?wireFormat=stomp use stomp instead
//
std::string brokerURI =
"failover://(tcp://127.0.0.1:61616"
// "?wireFormat=openwire"
// "&connection.useAsyncSend=true"
// "&transport.commandTracingEnabled=true"
// "&transport.tcpTracingEnabled=true"
// "&wireFormat.tightEncodingEnabled=true"
")";
//============================================================
// Total number of messages for this producer to send.
//============================================================
unsigned int numMessages = 2000;
//============================================================
// This is the Destination Name and URI options. Use this to
// customize where the Producer produces, to have the producer
// use a topic or queue set the 'useTopics' flag.
//============================================================
std::string destURI = "TEST.FOO";
//============================================================
// set to true to use topics instead of queues
// Note in the code above that this causes createTopic or
// createQueue to be used in the producer.
//============================================================
bool useTopics = false;
// Create the producer and run it.
SimpleProducer producer( brokerURI, numMessages, destURI, useTopics );
// Publish the given number of Messages
producer.run();
// Before exiting we ensure that all CMS resources are closed.
producer.close();
std::cout << "-----------------------------------------------------\n";
std::cout << "Finished with the example." << std::endl;
std::cout << "=====================================================\n";
activemq::library::ActiveMQCPP::shutdownLibrary();
}
I just created a empty c++ project, modified the .pro file to include a path to the libraries and then copy/pasted the code into main.cpp. Any idea what causes this? and how I can prevent it?
It looks like you've forgotten the library paths; you define INCLUDEPATH with new values but don't add a corresponding LIBPATH or LIBRARYPATH or whatever the equivalent variable for libraries is named. Won't you also need the corresponding -l library parameters for the specific libraries?
Related
Working on a c++ school project. Can't seem to get catch2 testing to work correctly.
I have a makefile where I compile my project
CC=clang++
CFLAGS=--std=c++11
objects = Event.o Simulation.o ListItem.o Node.o OrderedItem.o PartArrival.o
PriorityQueue.o Queue.o PartOne.o PartTwo.o PartZero.o PartialProduct.o
ProductArrival.o StartAssembly.o StartFinishingAssembly.o EndFinishingAssembly.o
StartMainAssembly.o EndMainAssembly.o EndAssembly.o Test_PriorityQueue.o
# .. etc .. put a list of your .o files here
# this rule will build A2 as the executable from the object files
all : A2main.o $(objects)
$(CC) $(CFLAGS) -o A2 $< $(objects)
# this rule will build A2test -- our testfile for the PriorityQueue
all : Test_PriorityQueue.cpp
clang++ --std=c++11 -o A2test Test_PriorityQueue.cpp
# this rule will build a .o file from a .cpp file.
%.o: %.cpp
$(CC) -c -o $# $< $(CFLAGS)
When this line runs clang++ --std=c++11 -o A2test Test_PriorityQueue.cpp I get this error:
Undefined symbols for architecture x86_64:
"Simulation::Simulation()", referenced from:
____C_A_T_C_H____T_E_S_T____4() in Test_PriorityQueue-1f17a8.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [all] Error 1
Here's the code for the test file -- Test_PriorityQueue.cpp :
#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do
this in one cpp file
#include "catch.hpp"
#include "Simulation.h"
#include "PartArrival.h"
#include "PriorityQueue.h"
#include <iostream>
using namespace std;
unsigned int Factorial( unsigned int number ) {
return number <= 1 ? number : Factorial(number-1)*number;
}
TEST_CASE( "Factorials are computed", "[factorial]" ) {
REQUIRE( Factorial(1) == 1 );
REQUIRE( Factorial(2) == 2 );
REQUIRE( Factorial(3) == 6 );
REQUIRE( Factorial(10) == 3628800 );
}
// debugging test
TEST_CASE("CATCH TEST"){
REQUIRE(1 == 1);
}
TEST_CASE("create PQ") {
cout << "PQ testcase" << endl;
Simulation *sim = new Simulation();
// PriorityQueue *pQue = new PriorityQueue();
// REQUIRE(pQue->getSize() == 0);
}
The first few tests run just fine when I comment out the last one "create PQ"
Once I include that I start getting these undefined symbol errors.
Been bashing my head against this for awhile. Some help would be very appreciated!!
Edit:
Simulation.h
#ifndef START_FILES_SIMULATION_H
#define START_FILES_SIMULATION_H
#pragma once
#include <fstream>
using namespace std;
class PriorityQueue; // Priority Queue
class Queue; // Queue class - provided to you
class Event; // Event - given to you.
class PartZero; // p0
class PartOne; // p1
class PartTwo; // p2
class PartialProduct; // partially assembled product
class Simulation {
private:
ifstream ifile; // input file to read.
int simulationTime; // what is the current time of the simulation?
PriorityQueue *eventList; // priority queue of Events.
Queue* productQueue; // queue of partially assembled products (for finishing station).
Queue** partQueues; // *array* of queues of parts for the stations.
int mainAssemblyTime; // how long does the main station take?
int finishingAssemblyTime; // how long does the main station take?
bool mainBusy; // is the main station busy?
bool finishingBusy; // is the finishing station busy?
int completelyAssembledItems; // number of items made
int cumulativeBuildTime; // the total amount of time to build all items
float averageBuildTime; // avg time to build item
public:
Simulation(); //TODO: build the part Queues when simulation is built
// TODO: you need methods to manipulate product and part queues.
// [add them here.]
// add to the respective queues
void addPartZero(PartZero *part);
void addPartOne(PartOne *part);
void addPartTwo(PartTwo *part);
void addPartialProduct(PartialProduct * product);
// dequeue the respective queues
int popMainParts(); // pops p0 and p1 and returns the earliest of arrivalTimes
void popPartZero();
void popPartOne();
void popPartTwo();
PartialProduct* popPartialProduct();
// functions that check if queues have parts in
bool partsInZero();
bool partsInOne();
bool partsInTwo();
bool partsInProduct();
int getCompletelyAssembledItems(); // getter
void completeAssemblyOfItem(); // adds one to completelyAssembledItems var
void updateCumulativeBuildTime(int time);
float getAvgBuildTime();
// main method for driving the simulation
void runSimulation(char *fileName);
// add an event to event queue.
void addEvent (Event*);
// read next arrival from file and add it to the event queue.
bool getNextArrival(); //TODO: add to sim.cpp
// getter and setter for simulation time
int getSimulationTime();
void setSimulationTime(int time);
// getters for assembly times
int getMainTime();
int getFinishingTime();
// getters and setters for station statuses.
bool isMainBusy();
bool isFinishingBusy();
void setMainStatus(bool);
void setFinishingStatus(bool);
};// class Simulation
#endif //START_FILES_SIMULATION_H
Simulation.cpp constructor :
// constructor
Simulation::Simulation() {
// init part queues
partQueues = reinterpret_cast<Queue **>(new Queue[3]);
partQueues[0] = new Queue();
partQueues[1] = new Queue();
partQueues[2] = new Queue();
// init event list
eventList = new PriorityQueue();
// init product queue
productQueue = new Queue();
// station status
mainBusy = false;
finishingBusy = false;
}
what I want to do is a c++ code which utilize boost library and do a simple RS232 communication. I got the code like following:
#include <boost/asio.hpp> // include boost
using namespace::boost::asio; // save tons of typing
#include <iostream>
using std::cin;
// These are the values our port needs to connect
#ifdef _WIN32
// windows uses com ports, this depends on what com port your cable is plugged in to.
const char *PORT = "COM3";
#else
// Mac OS ports
const char *PORT = "/dev/tty.usbserial";
#endif
// Note: all the following except BAUD are the exact same as the default values
serial_port_base::baud_rate BAUD(19200);
serial_port_base::character_size C_SIZE( 8 );
serial_port_base::flow_control FLOW( serial_port_base::flow_control::none );
serial_port_base::parity PARITY( serial_port_base::parity::none );
serial_port_base::stop_bits STOP( serial_port_base::stop_bits::one );
int main()
{
io_service io;
serial_port port( io, PORT );
port.set_option( BAUD );
port.set_option( C_SIZE );
port.set_option( FLOW );
port.set_option( PARITY );
port.set_option( STOP );
unsigned char command[1] = {0};
// read in user value to be sent to device
int input;
cin >> input;
// The cast will convert too big numbers into range.
while( input >= 0 )
{
// convert our read in number into the target data type
command[0] = static_cast<unsigned char>( input );
write( port, buffer( command, 1 ) );
// read in the next input value
cin >> input;
}
// all done sending commands
return 0;
}
and I am building the code with following command:
c++ -Iboost_1_64_0 -Lboost_1_64_0/libs/ -stdlib=libc++ PortConfig.cpp -o PortConfig
but the terminal keeps giving me error:
Undefined symbols for architecture x86_64:
"boost::system::system_category()", referenced from:
boost::asio::error::get_system_category() in PortConfig-2187c6.o
boost::system::error_code::error_code() in PortConfig-2187c6.o
___cxx_global_var_init.2 in PortConfig-2187c6.o
"boost::system::generic_category()", referenced from:
___cxx_global_var_init in PortConfig-2187c6.o
___cxx_global_var_init.1 in PortConfig-2187c6.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
could anyone help me on that? Thanks in advance.
The compiler option -Lboost_1_64_0/libs/ just tells the compiler to look in that directory for libraries. You still need to specify which libraries to link. According to the boost documentation you will need the boost_system library, so add -lboost_system to the compiler options.
The corrected compile command should look something like this
c++ -Iboost_1_64_0 -Lboost_1_64_0/libs/ -lboost_system -stdlib=libc++ PortConfig.cpp -o PortConfig
I'm having trouble (dynamically) linking with the Qt libraries whilst compiling a C++ project with g++ 4.5.3:
g++ -Wall XMLInOut.cpp tinyxml2.cpp -I"C:\Qt\4.7.1\include" -I"$ADTF_ADDONS\adtf-device-toolbox\include" -L"C:\Qt\4.7.1\lib" -lQtCore4 -DtUInt32=uint -o xmltest.exe
The output is the following :
/tmp/cclJqe9S.o:XMLInOut.cpp:(.text+0x1db): undefined reference to `QString::toUInt(bool*, int) const'
/tmp/cclJqe9S.o:XMLInOut.cpp:(.text$_ZN7QStringC1EPKc[QString::QString(char const*)]+0x15): undefined reference to QString::fromAscii_helper(char const*, int)
/usr/lib/gcc/i686-pc-cygwin/4.5.3/../../../../i686-pc-cygwin/bin/ld: /tmp/cclJqe9S.o: bad reloc address 0x15 in section `.text$_ZN7QStringC1EPKc[QString::QString(char const*)]'
If I understand correctly, the -l option will link against a dynamic library. in the Qt/libs I have the QtCore4.dll
What am I doing wrong?
Cheers
EDIT :
Although I believe the problem lies fully in the linkage, I will post my code :
#include "XMLInOut.h"
#include "tinyxml2.h"
#include <iostream>
#include "ModelToInterfaceWrapper.h"
//#define __DEBUG_OUT_
#ifdef __DEBUG_OUT_
#define out(A) cout<<A
#else
#define out(A)
#endif
using namespace std;
using namespace tinyxml2;
void XMLInOut::readAndParseFile(const char* pFilename){
XMLDocument doc;
doc.LoadFile( "config.xml" );
XMLElement *service = doc.FirstChildElement( "root" )->FirstChildElement( "Service" );
for( service; service; service=service->NextSiblingElement() )
{// Iterating Services
out(endl<<service->FirstAttribute()->Value());
QString serviceName;
tUInt32 signalValue ;
const XMLAttribute* description = ((const XMLElement *)service)->FindAttribute("description");
if((const XMLAttribute*)0 != description){
out(" ("<<description->Value()<<")");
serviceName = description->Value();
}else {
serviceName = service->FirstAttribute()->Value();
}
for(XMLElement *signal = service->FirstChildElement( "Signal" ); signal; signal=signal->NextSiblingElement() ){
out(endl<<" "<<signal->GetText());
signalValue = (new QString (signal->GetText()))->toUInt();
}
}
}
int main(int argc, char **argv) {
XMLInOut xmlinOut;
xmlinOut.readAndParseFile("config.xml");
}
I am developing a ROS Qt GUI application and I face a problem on ROS Hydro (I had the same problem while working on ROS Fuerte). My project does not recognize my library like image_transport.h. I added it to the beginning of the qnode.hpp file but it did not solve the problem.
My major problem:
/home/attila/catkin_ws/src/arayuz/src/qnode.cpp:-1: error: undefined reference to `image_transport::ImageTransport::ImageTransport(ros::NodeHandle const&)'
This is the code that generates the error:
#include "ros/ros.h"
#include "ros/network.h"
#include "string"
#include "std_msgs/String.h"
#include "sstream"
#include "../include/arayuz/qnode.hpp"
namespace enc=sensor_msgs::image_encodings;
static const char WINDOW[ ]="Kinect";
namespace arayuz {
QNode::QNode(int argc, char** argv ) :
init_argc(argc),
init_argv(argv)
{}
QNode::~QNode() {
if(ros::isStarted()) {
ros::shutdown(); // explicitly needed since we use ros::start();
ros::waitForShutdown();
}
cv::destroyWindow(WINDOW);
wait();
}
bool QNode::init() {
ros::init(init_argc,init_argv,"arayuz");
if ( ! ros::master::check() ) {
return false;
}
ros::start(); // explicitly needed since our nodehandle is going out of scope.
ros::NodeHandle n;
// Add your ros communications here.
image_transport::ImageTransport it(n);
imagesub = it.subscribe("/kinectCamera", 1,&QNode::chatterimage,this);
start();
return true;
}
bool QNode::init(const std::string &master_url, const std::string &host_url) {
std::map<std::string,std::string> remappings;
remappings["__master"] = master_url;
remappings["__hostname"] = host_url;
ros::init(remappings,"arayuz");
if ( ! ros::master::check() ) {
return false;
}
ros::start(); // explicitly needed since our nodehandle is going out of scope.
ros::NodeHandle n;
// Add your ros communications here.
image_transport::ImageTransport it(n);
imagesub = it.subscribe("/kinectCamera",1,&QNode::chatterimage,this);
start();
return true;
}
void QNode::chatterimage(const sensor_msgs::ImageConstPtr& msg)
{
rgbimage=cv_bridge::toCvCopy(msg,enc::BGR8);
Q_EMIT chatterimageupdate();
}
void QNode::run() {
while ( ros::ok() ) {
ros::spin();
}
std::cout << "Ros shutdown, proceeding to close the gui." << std::endl;
Q_EMIT rosShutdown(); // used to signal the gui for a shutdown (useful to roslaunch)
}
}
In order to link against ROS libraries you need to add the dependency to your package.xml file:
<build_depend>image_transport</build_depend>
<run_depend>image_transport</run_depend>
and to your CMakeLists.txt:
find_package(catkin REQUIRED COMPONENTS
...
image_transport
...
)
Just adding the header to your compilation isn't sufficient: the error message states that you successfully compiled the code but it failed to link the executable. You will also need to find the library implementing the image_transport code and link it to your executable. I have no idea of ros but here is a link which seems to describe how to build code using this library.
I have an interesting problem that seems to be unresolved by my research on the internet.
I'm trying to load libraries dynamically in my c++ project with the functions from dlfcn.h. The problem is that when I try to reload the plugins at running time (because I made a change on any of them), the main program crashes (Segmentation fault (core dumped)) when dlclose() is called.
Here is my example that reproduces the error:
main.cpp:
#include <iostream>
#include <dlfcn.h>
#include <time.h>
#include "IPlugin.h"
int main( )
{
void * lib_handle;
char * error;
while( true )
{
std::cout << "Updating the .so" << std::endl;
lib_handle = dlopen( "./test1.so", RTLD_LAZY );
if ( ! lib_handle )
{
std::cerr << dlerror( ) << std::endl;
return 1;
}
create_t fn_create = ( create_t ) dlsym( lib_handle, "create" );
if ( ( error = dlerror( ) ) != NULL )
{
std::cerr << error << std::endl;
return 1;
}
IPlugin * ik = fn_create( );
ik->exec( );
destroy_t fn_destroy = ( destroy_t ) dlsym( lib_handle, "destroy" );
fn_destroy( ik );
std::cout << "Waiting 5 seconds before unloading..." << std::endl;
sleep( 5 );
dlclose( lib_handle );
}
return 0;
}
IPlugin.h:
class IPlugin
{
public:
IPlugin( ) { }
virtual ~IPlugin( ) { }
virtual void exec( ) = 0;
};
typedef IPlugin * ( * create_t )( );
typedef void ( * destroy_t )( IPlugin * );
Test1.h:
#include <iostream>
#include "IPlugin.h"
class Test1 : public IPlugin
{
public:
Test1( );
virtual ~Test1( );
void exec( );
};
Test1.cpp:
#include "Test1.h"
Test1::Test1( ) { }
Test1::~Test1( ) { }
void Test1::exec( )
{
std::cout << "void Test1::exec( )" << std::endl;
}
extern "C"
IPlugin * create( )
{
return new Test1( );
}
extern "C"
void destroy( IPlugin * plugin )
{
if( plugin != NULL )
{
delete plugin;
}
}
To compile:
g++ main.cpp -o main -ldl
g++ -shared -fPIC Test1.cpp -o plugin/test1.so
The problem occurs when for example I change something on the Test1::exec method (changing the string to be printed or commenting the line) and while the main program sleeps I copy the new test1.so to main running directory (cp). If I use the move command (mv), no error occurs. What makes the difference between using cp or mv? Is there any way to solve this problem or to do that using cp?
I'm using Fedora 14 with g++ (GCC) 4.5.1 20100924 (Red Hat 4.5.1-4).
Thanks in advance.
The difference between cp and mv that is pertinent to this question is as follows:
cp opens the destination file and writes the new contents into it. It therefore replaces the old contents with the new contents.
mv doesn't touch the contents of the original file. Instead, it makes the directory entry point to the new file.
This turns out to be important. While the application is running, the OS keeps open handles to the executable and the shared objects. When it needs to consult one of the these files, it uses the relevant handle to access the file's contents.
If you've used cp, the contents has now been corrupted, so anything can happen (a segfault is a pretty likely outcome).
If you've used mv, the open file handle still refers to the original file, which continues to exist on disk even though there's no longer a directory entry for it.
If you've used mv to replace the shared object, you should be able to dlclose the old one and dlopen the new one. However, this is not something that I've done or would recommend.
Try this:
extern "C"
void destroy( IPlugin * plugin )
{
if( plugin != NULL && dynamic_cast<Test1*>(plugin))
{
delete static_cast<Test1*>(plugin);
}
}