3. A megoldás

3.1. extern "C"

A C++-nak van egy speciális kulcsszava arra, hogy függvényeket C kötéssel definiáljuk. Ez az extern "C". Az a függvény ami extern "C"-ként lett definiálva annak függvényneve szimbólumként használható akárcsak egy C függvénynek. Ezért csak nem-tagfüggvények deklarálhatók extern "C" segítségével, és ezeket nem lehet átdefiniálni.

Habár van néhány megkötés az extern "C" függvényekre, mégis igen hasznosak, mivel dinamikusan betölthetőek a dlopen segítségével akárcsak a C függvények.

Ez nem jelenti azt, hogy az extern "C"-vel definiált függvények nem tartalmazhatnak C++ kódot. Az ilyen függvények teljes értékű C++ függvények, kihasználhatják a C++ lehetőségeit és bármilyen típusú argumentummal rendelkezhetnek.

3.2. Függvények betöltése

C++ a függvények úgy tölthetőek be mint C-ben; a dlsym segítségével. A betölteni kívánt függvényeket extern "C"-vel kell jelölnöd, hogy a C-szerű szimbólum névképzést kikényszerítsd.

A hello függvény a hello.cpp állományban van definiálva, mint extern "C". A main.cpp-ben töltődik be a dlsym hívással. A függvényt extern "C"-vel kell megjelölni, mert különben nem tudjuk biztosan a hozzá tartozó szimbólumnevet.

3.3. Osztályok betöltése

Az osztályok betöltése egy kicsit komplikáltabb, mert nekünk az osztály egy példányára van szükségünk, nem csak egy függvényre mutató mutatóra.

Nem tudjuk létrehozni az osztály egy példányát a new operátor segítségével, mert az osztály nincs definiálva a futtatható állományban, és mert nem tudjuk a nevét.

A megoldás a polimorfizmus segítségével adódik. Egy alap interfész osztályt definiálunk a futtatható állományban virtuális tagfüggvényekkel, és egy származtatott implementációs osztályt a modulban. Általában az interfész absztrakt osztály (egy osztály absztrakt, ha minden függvénye virtuális).

A dinamikus osztálybetöltést általában plug-in-okban használják — Ezeknek egy világosan definiált interfészt kell használniuk — Egy interfészt és az azt implementáló osztályokat kell definiálnunk.

Ezek után - még mindig a modulban - definiálunk két további segédfüggvényt (úgynevezett class factory functions). Az egyik függvény ezek közül elkészíti egy példányát az osztálynak, és egy arra irányított mutatót ad vissza. Míg a másik egy osztályra irányított mutatót kap (amit a factory készített) és felszabadítja azt. Ezt a két függvényt extern "C" direktívával jelöljük meg.

Ahhoz, hogy osztályt tölts be modulból csak a két factory függvényt kell betöltened a dlsym segítségével. Szerkeszteni (link) ugyanúgy kell, mint ahogy azt ebben részben tettük a hello függvénnyel. Ezek után már annyi példányt tudsz létrehozni és felszabadítani az osztályból, amennyit csak akarsz.

Példa 2. Egy osztály betöltése

Itt mi most egy általános polygon osztályt használunk, mint interfész és egy származtatott triangle osztályt, mint implementációt.

main.cpp:

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

int main() {
    using std::cout;
    using std::cerr;

    // load the triangle library
    void* triangle = dlopen("./triangle.so", RTLD_LAZY);
    if (!triangle) {
        cerr << "Cannot load library: " << dlerror() << '\n';
        return 1;
    }

    // load the symbols
    create_t* create_triangle = (create_t*) dlsym(triangle, "create");
    destroy_t* destroy_triangle = (destroy_t*) dlsym(triangle, "destroy");
    if (!create_triangle || !destroy_triangle) {
        cerr << "Cannot load symbols: " << dlerror() << '\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

class polygon {
protected:
    double side_length_;

public:
    polygon()
        : side_length_(0) {}

    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 "polygon.hpp"
#include <cmath>

class triangle : public polygon {
public:
    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;
}

Néhány dolgot meg kell jegyeznünk az osztályok betöltésével kapcsolatban: