1

I'm trying to dynamically load libraries that implement a base class, defined in another library. I've followed instructions depicted here:

http://www.linuxjournal.com/article/3687?page=0,1

I created a shape class and put it in a separated library (not a real library since its just a header file, but put it that way), also I created two dynamic libraries, circle and square, that inherit from shape and implement the draw method and register their maker function in the factory map. Let me show you the code:

shape.h:

#ifndef __SHAPE_H
#define __SHAPE_H
#include <map>
#include <string>

// base class for all shapes
class shape{
public:
// our global factory
virtual void draw()=0;
};

// typedef to make it easier to set up our factory
typedef shape *maker_t();
extern std::map<std::string, maker_t *, std::less<std::string> > factory;


#endif // __SHAPE_H

circle.h

#ifndef __CIRCLE_H
#define __CIRCLE_H

#include "shape.h"

class circle : public shape
{
public:
   void draw();
};
#endif // __CIRCLE_H

circle.cpp:

#include <iostream>
#include "circle.h"

void circle::draw()
{
    // simple ascii square
std::cout << "\n";
std::cout << "      ****\n";
std::cout << "    *      *\n";
std::cout << "   *        *\n";
std::cout << "   *        *\n";
std::cout << "   *        *\n";
std::cout << "    *      *\n";
std::cout << "      ****\n";
std::cout << "\n";
}

extern "C"
{
shape *maker()
{
    return new circle;
}
class proxy
{
    public:
        proxy()
        {
            // register the maker with the factory
            factory["circle"] = maker;
        }
};
// our one instance of the proxy
proxy circle_p;
}

I won't go into the square, since it's almost identical to the circle neither the actual "client" that dynamically loads the libraries, since it actually works.

Now my question is, in case you have other classes and functionality in the circle.so (this is my case), and in some cases you need link at compile time (-l option), I run into problems. I created a test client library that instead of dynamically loading circle, it does it at compile time. Linker fails with the following output:

Invoking: GCC C++ Linker
g++ -rdynamic -L/home/lizardking/workspace/dynclientest/Debug/libs -o "test2"  ./src/test2.o   -ldynlib_circle
/home/lizardking/workspace/dynclientest/Debug/libs/libdynlib_circle.so: undefined reference to `factory'
collect2: ld returned 1 exit status

The main.cpp from this test app is just a hello world!:

#include <iostream>
using namespace std;

int main() {
cout << "!!!Hello World!!!" << endl; // prints !!!Hello World!!!
return 0;
}

I really dunno why the linker is complaining all the time about the map....any ideas???

Thanks!

AlejandroVK
  • 7,373
  • 13
  • 54
  • 77
  • nit: the use of two underscores (ex: `__SHAPE_H`) is [reserved](http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier) for use by the implementation. – Sam Miller May 03 '11 at 14:25

2 Answers2

0

You have only declared factory as extern in shape.h.

You should put something like:

std::map<std::string, maker_t *, std::less<std::string> > factory;

into shape.cpp to reserve space (i.e. create a defined symbol) in shape.o.

Andre Holzner
  • 18,333
  • 6
  • 54
  • 63
  • Hey Andre, ok I added it and it works IF I also include the -lbase_class.so library, otherwise it keeps complaining....can anyone tell me the reason behind this?? is puzzling me, thanks for the response – AlejandroVK May 03 '11 at 14:10
  • It works, although I have to link at compile time against the library of the base class, thanks Andre, I'd vote your response, but since I don't have enough reputation I can't, sorry – AlejandroVK May 03 '11 at 15:37
  • Move the code accessing `factory` from `circle.cpp` to `shape.cpp` e.g. by providing a (static) protected method `register(const std::string &myname, maker_t *)` in the class `shape`. – Andre Holzner May 03 '11 at 15:54
  • Hello Andre, if I make this register method, I won't be needing the proxy class anylonger right? I mean that class is the one supposed to update the "register" – AlejandroVK May 04 '11 at 07:45
  • I'm running into all kind of problems trying to move the accesing to the base class, could you provide an example Andre? – AlejandroVK May 04 '11 at 09:18
  • Well, I don't need the library for the base class now, since I have included std::map > factory in the circle.cpp, so now memory is reserved and no "undefined symbol" occurs, although I would really liek to see this register method in the base class, since I think doing this in the derived classes is not very elegant. – AlejandroVK May 04 '11 at 11:01
0

If you read the error carefully, You will understand the reason for it.

/home/lizardking/workspace/dynclientest/Debug/libs/libdynlib_circle.so: undefined reference tofactory`

It tells you that it cannot find reference to a object named factory, and it also tells you the location from where this object is getting accessed libdynlib_circle.so

Checking in your libdynlib_circle.so the factory object is declared as extern in shape.h. When you use the extern keyword on a variable or object it tells the compiler that the particular variable or object will be defined(created) in some other source file and the location where it is declared extern(shape.so) just references the same variable.In your case this factory object is not defined or created in any of the source files and hence the error that undefined reference to factory because source.so cannot find a created factory obj.

Hence, to avoid the error you need to define the factory object in one of the source files.

std::map<std::string, maker_t *, std::less<std::string> > factory;
Alok Save
  • 202,538
  • 53
  • 430
  • 533