0

I have my_program which loads some shared libraries. Basicaly user can create one project and then that project can be executed with my_program. I want to load class from shared library. I've found this example: C++ Dynamic Shared Library on Linux

This example is great, but I have a problem. I don't have header file. So, is there any possible way to load class for which I don't have header file? I suppose there isn't. If I am not mistaken I don't think that there is a way to load header file dynamically?

If user write class Child : public Parent then in my main program I must replace MyClass (from that example that I provided) with Child. I know which methods are in Child class, becase I provide BaseParent class with pure virtual methods. So I would include BaseParent in my main program. But still I would need to know class name of this project.

What if I say that project class name must be always MY_PROJECT? Would that help? Then I would know that I must replace MyClass (from example above) with MY_PROJECT but wouldn't that trigger an error because main program wouldn't know nothing about class MY_PROJECT?

I'm open for any suggestions because I am literally stuck.

EDIT: If I have this class which is in main program:

class BaseParent
{
public:
    virtual bool init(const TestCase&);
    virtual void run();
    virtual bool done();
}

Next two classes are writen by user who creates project

class Parent : public BaseParent
{
public:
    bool init(const TestCase &test)
    {
         //implementation of this method
    }

    void run()
    {
         execute(.....);
    }

    bool done()
    {
        //implementation of this method
    }

    //he must also define this method which is in child class
    virtual void execute(...);
};

And the other class

class Child : public Parent
{
public:
    void execute(...)
    {
         //implementation of this method
    }
};

extern "C" Child* create_object()
{
  return new Child;
}

extern "C" void destroy_object( Child* object )
{
  delete object;
}

So, I can include only BaseParent in my main program. If Child class return pointer to BaseParent then I can call methods like init, done, and run like this bellow? Even that BaseParent is not aware of execute which is called in run?

void *handle = dlopen(shared_lib, RTLD_LAZY);
if (handle)
{
    BaseParent* (*create)();
    void (*destroy)(BaseParent*);

    create = (BaseParent* (*)())dlsym(handle, "create_object");
    destroy = (void (*)(BaseParent*))dlsym(handle, "destroy_object");

    BaseParent* myClass = (BaseParent*)create();
    myClass->init(test); //wouldn this trigger an error because it's not implemented in BaseParent but in Child?
}

Edit 2:

BaseParent* (*create)();
void (*destroy)(BaseParent*);

//other code.... buff is path to libProject.so
void *handle = dlopen(buff, RTLD_LAZY);
if (handle)
{
    create = (BaseParent* (*)())dlsym(handle, "create_object");
    destroy = (void(*)(BaseParent*))dlsym(handle, "destroy_object");
    BaseParent *a = (BaseParent*)create();
    a->run();
}else
{
    LOG(ERROR) << "Error in opening lib " << buff;
    return;
}

Problem is that linker (on OS X) gives me an error:

Undefined symbols for architecture x86_64:
  "BaseParent::run()", referenced from:
      vtable for BaseParent in BaseParent
  "BaseParent::init(TestCase&)", referenced from:
      vtable for BaseParent in BaseParent
ld: symbol(s) not found for architecture x86_64

But on Ubuntu 12.04 I get this:

(gdb) 
99          AbsAlgorithm *a = (AbsAlgorithm*)create();
(gdb) 

Program received signal SIGSEGV, Segmentation fault.
0x0000000000000000 in ?? ()

So, that run and init method are virtual as you can see above. Any advice?

Community
  • 1
  • 1
golobitch
  • 1,466
  • 3
  • 19
  • 38
  • What do you mean by loading a class? Do you want to be able to create such a class yourself and use its methods, or do you want only to have some function from the library that returns you the pointer to a class, and you want to use it only using the base class methods? – Petr Jul 23 '15 at 13:10
  • What is this vendor that provides library files but no headers? Is it a well-known/open-source library? – Nasser Al-Shawwa Jul 23 '15 at 13:10
  • @Petr when I load shared lib I want to create instance of class that user wrote. – golobitch Jul 23 '15 at 13:14
  • If you want to create the instances _yourself_, then I guess you _have to_ have a header, at least to know which constructors are available. However, you can ask the author of the library to provide a free-standing function that returns a pointer to a class as `Parent*` and then you will not need a header as long as you use only `Parent` virtual functions. – Petr Jul 23 '15 at 13:16
  • @Nasser this is my project or better yet my bachelor's degree. Problem is that even if I provide him with header file, I don't know where this header file is located. Sure, I can install this header file in /usr/local/lib but user would probably need some variables inside this class. So he can't just go and change content of that file. What if there would be 10 projects? I think that best solution is that he provide header file, but I don't know where that files is located. Well I know ($ALGATOR_ROOT/data_root/projects/PROJ-ProjectName/alg/ALG-ALGNAME/src/). But I can't dynamically include it. – golobitch Jul 23 '15 at 13:18
  • 1
    @Petr if you click on that link you will see that user provides pointer to child class. Parent class has pure virtual methods. But yes, if i change them to virtual, then I suppose it will work. User just implements this virtual functions. I will edit my question and provide example in a moment – golobitch Jul 23 '15 at 13:22
  • Headers do not go in `/usr/local/lib`. Headers go in `/usr/local/include`. You know where they are because you installed them, either directly or in the installation instructions for your project as a dependency. – Lightness Races in Orbit Jul 23 '15 at 13:32
  • @LightnessRacesinOrbit, yes, that was typo. My header files go into /usr/local/include, but what about header files that user wrote them? They can be anywhere on computer – golobitch Jul 23 '15 at 13:41
  • The code that you have posted should work. In its current version, `create_object` returns `new BaseParent`, which I assume to be a typo, it should return `new Child`, should not it? And basically yes, that's how polymorphism works: the code using a class knows nothing about the class itself, only about parent class. – Petr Jul 23 '15 at 13:46
  • @Petr yes, that was typo. I fixed it now. So, I return pointer to Child clas. So basicly above code do this: `BaseParent *p = new Child()`. Is this right? If yes, then please post answer and I will accept it. Thank you – golobitch Jul 23 '15 at 13:53
  • @golobich: Why would they be "anywhere on computer"? They should be installed under the appropriate and conventional include directories. – Lightness Races in Orbit Jul 23 '15 at 14:16
  • @LightnessRacesinOrbit user can create project anywhere but default is $ALGATOR_ROOT/data_root/projects/. Let's say we have PROJ-Sorting. Inside we have folder algs, lib, proj, tests. Let's say that header files will be under proj. So...main problem here is that I don't know what is $ALGATOR_ROOT. it can be anythink. That project that user create IS NOT installed. my_program just compile files that user wrote, creates instance of classes and calls methods. – golobitch Jul 23 '15 at 15:24
  • @golobich: That's nonsense. If someone is to use the project then you must _deploy_ it. That includes installing reusable headers in the system include paths. Or, if your project is not a shared library, then why does it matter whatsoever where the headers are? They're all compiled into your executable and don't need to be reused in the future at all. – Lightness Races in Orbit Jul 23 '15 at 15:27
  • @LightnessRacesinOrbit yes, I deploy header file for `BaseParent` which is located in /usr/local/include. I also create directory structure for project. You can create project and add algorithm to existing project with options. like `my_program -n Project_name` and so on. But I don't know what is the name of class that user wrote. I know which methods are there (init, run, done => these three are only one that matters) but again I don't know class name. So I think that Petr gave correct answer (not suer yet I must test this) – golobitch Jul 23 '15 at 15:51
  • And no, I can't have static library of user project because I can't link it with my program. My program must be executable file so no compiling and so on. The best way is then to create shared lib and load symbols into main. Because in main I know what symbols am I looking for. If they are not provided, then this is not my fault. It's users fault – golobitch Jul 23 '15 at 15:52

1 Answers1

1

UPD: the most correct, I think, is

extern "C" BaseParent* create_object()
{
  return new Child;
}

so you keep the fact that Child exists completely inside the user's library, and you provide the library interface completely in terms of BaseParent.

With this correction, this code will work (as long as it does not contain other errors, I did not check it for compilability). Basically that is the key idea of polymorphism: if you have a base class (interface) and you have a derived class, and some code uses only the methods of the interface, then that code does not need to know any particulars about the derived class, nor even know that the derived class exists at all. All the code needs is the pointer to an object, and from the point of view of that code this pointer will have the type like Interface*, i.e. will be pointer to the interface.

Petr
  • 9,812
  • 1
  • 28
  • 52
  • I will try this later today and I will give you feedback, but I think it should work. For now it's just upvote, but if it will work I will accept you answer. Thank you! – golobitch Jul 23 '15 at 15:25
  • I've edited my original post. Please check. Thank you. – golobitch Jul 24 '15 at 10:14
  • @golobich, I suggest you declare the `BaseParent` methods pure virtual (`=0`). If this does not help, then this means you are actually _creating_ `BaseParent` object somewhere in the code that you did not show. – Petr Jul 24 '15 at 10:16
  • yes, this works, but I get error in this line `BaseParent *a = (BaseParent*)create();` This create is exactly the same as you wrote it. In my original post in EDIT 2 section you can see part of code where is this written. Any suggestions why I get segmentation fault? – golobitch Jul 24 '15 at 10:45
  • @golobich, for this I hardly can offer any suggestion, because this requires inspecting your whole code and probably build process. Probably try using valgrind. – Petr Jul 24 '15 at 12:10
  • OK. Thank you for that. Question about this you answer. If I return pointer of my child class to BaseParent class...Well here is the thing. BaseParent class doesn't know anything about execute method that is in Parent/Child class. Yes, execute method is called within run method. So I can call method run on BaseParent class, but will then run method call execute method ?? This is a little confusing for me :) – golobitch Jul 24 '15 at 12:21
  • @golobich, you return a pointer that is actually a pointer to a `Child`, only the caller thinks its a pointer to a `BaseParent`. So it should work exactly the same way as usual virtual functions work: the opject pointed to will have vtable for `Child` (because that object was constructed as `Child`), and when a function is called, the actual address of a function is looked up in the object's vtable, the vtable is `Child`'s, thus the `Child`'s function is called. Absolutely the same way as if there was no shared libraried, and it all was one executable. – Petr Jul 24 '15 at 12:28
  • OK. Know I understand completely how this should work. Now, I only need to fix that error. @Petr, thank you for all your help! I will accept you answer. – golobitch Jul 24 '15 at 12:30