如何在運行時加載C++函數和類別

另外一個問題就是dlopen函數僅支持load 函數,不支持load class。 很明顯的是如果你想使用該class,就需要new instance出來。

C++有個特殊的關鍵字來聲明函數用C的方式來綁定——extern “C”,在C++中函數如果用extern “C”在前面聲明的話,就表示該函數的symbol以C語言方式來命名。

所以只有非成員函數可以用extern “C”來聲明,而且他們不能被重載。 雖然很侷限,但是這樣足夠利用dlopen來運行時調用function了。需要強調的是用extern “C”不是就不可以在function內寫C++ 代碼,依然可以調用class和class的function的。

用dlsym,加載C++函數就像加載C函數一樣,你要加載的函數必須要用extern “C”來聲明,避免name mangling

-main.cpp

#include <iostream>
#include <dlfcn.h>
#include "polygon.hpp"

int main()
{
    using std::cout;
    using std::cerr;
    // load the triangle library
    std::cout << "dlopen[+]" << std::endl;
    void* triangle = dlopen("./triangle.so", RTLD_LAZY);
    std::cout << "dlopen[-]" << std::endl;

    if (!triangle) {
        cerr << "Cannot load library: " << dlerror() << '\n';
        return 1;
    }

    // reset errors
    dlerror();
    // load the symbols
    create_t* create_triangle = (create_t*) dlsym(triangle, "create");
    const char* dlsym_error = dlerror();

    if (dlsym_error) {
        cerr << "Cannot load symbol create: " << dlsym_error << '\n';
        return 1;
    }

    destroy_t* destroy_triangle = (destroy_t*) dlsym(triangle, "destroy");
    dlsym_error = dlerror();

    if (dlsym_error) {
        cerr << "Cannot load symbol destroy: " << dlsym_error << '\n';
        return 1;
    }

    // create an instance of the class
    polygon* poly = create_triangle();
    // use the class
    poly->set_side_length(7);
    cout << "The area is: " << poly->area() << '\n';
    // destroy the class
    destroy_triangle(poly);
    // unload the triangle library
    dlclose(triangle);
}
  • polygon.hpp
#ifndef POLYGON_HPP
#define POLYGON_HPP
#include <iostream>

class polygon
{
protected:
    double side_length_;
public:
    polygon()
        : side_length_(0)
    {
        std::cout << "polygon Constructor" << std::endl;
    }

    virtual ~polygon()
    {
        std::cout << "polygon Destructor" << std::endl;
    }

    void set_side_length(double side_length)
    {
        side_length_ = side_length;
    }

    virtual double area() const = 0;
};
// the types of the class factories
typedef polygon* create_t();
typedef void destroy_t(polygon*);
#endif
  • triangle.cpp
#include <iostream>
#include <cmath>
#include "polygon.hpp"

class triangle : public polygon
{
public:
    triangle()
    {
        std::cout << "triangle Constructor" << std::endl;
    }

    ~triangle()
    {
        std::cout << "triangle Destructor" << std::endl;
    }

    virtual double area() const
    {
        return side_length_ * side_length_ * sqrt(3) / 2;
    }
};
// the class factories
extern "C" polygon* create()
{
    return new triangle;
}
extern "C" void destroy(polygon* p)
{
    delete p;
}
  • Makefile
CXX = g++
SOFLAGS = -shared -fPIC
LDFLAGS = -ldl
CXXFLAGS = -Wall -pedantic -ggdb3 -O0 -std=c++11 -lpthread

all : a.out                                       

a.out: main.cpp libtriangle 
    $(CXX) $(CXXFLAGS)  main.cpp -o main $(LDFLAGS)

libtriangle:                                            
    $(CXX) $(CXXFLAGS) $(SOFLAGS) -o triangle.so triangle.cpp
.PHONY: clean                                     
clean:                                            
    @echo "remove the main and so" 
    rm -rf  main *.so