English | Magyar
JS ki | CSS ki | Ékezetek ki | HiContrast
Lapozó:  (0 - 1428) 
<== | ==>
Ugrás a végére | Összes megjelenítése | Utolsó oldal
OpenOpera patches | Opera-SSL patches | Opera 12.15 source (Git repository) | Opera 12.15 source (Torrent) | Opera internal pages | Otter Browser Linux x64 - Qt5.15.2/QtWebKit5.602.1 (2024.12.02. 21:02)
OS for MC680x0 | OS for PPC | OS for Sparc64 | besztofbégéaefcé | CSÉNDZSLOG | WebToolz | DDG Shit Filter | Google Shit Filter | Progz | Fast CSS Box | Browser | OS | Agent | Statisztika | BBCode
Monospace font-family: Courier New | Browser default monospace
Email értesítő / Email notification ===> 
Keresés
Σ: 1 post

TCH  (statz) Főfasz
#1, Főfasz (10466)
27596 | #5461 | ^ | Idézet | Sat, 30 Oct 2021 18:11:01 +02
84.236.*.* Linux x86 Opera Classic Hungary *.pool.digikabel.hu
Pluginezés UNIX alatt C és Pascal nyelven rovat.

Gondoltam csinálok a témakörben egy tutorialt. Vágjunk bele.

A pluginek tulajdonképpen kutyaközönséges dinamikus library-k, amiket a program futásidőben tud betölteni, vagy felszabadítani. Ennek megfelelően elsőként csináljunk egy dinamikus könyvtárat.

Legyen kezdetnek mondjuk három függvényünk. C-ben (mylib.c):
int mylib_a(int a)
{
	return a - 5;
}

void mylib_b(int *b)
{
	*b -= 5;
}

int mylib_c(int *c)
{
	*c -= 5;
	return *c - 5;
}
És Pascalban (mylib.pas):
library mylib;

{$mode objfpc}{$H+}

function mylib_a(a: longint): longint; cdecl;
begin
	result := a - 5;
end;

procedure mylib_b(b: plongint); cdecl;
begin
	b^ := b^ - 5;
end;

function mylib_c(c: plongint): longint; cdecl;
begin
	c^ := c^ - 5;
	result := c^ - 5;
end;

exports mylib_a, mylib_b, mylib_c;

end.
A C library úgy áll elő, hogy először leforgatjuk relokálható (-fPIC kapcsoló) objektumnak (-c kapcsoló)
cc -fPIC -c mylib.c
majd utána linkeljük Shared Object-nek (-shared kapcsoló, a -Wl,-soname,libmylib.so pedig a linkernek mondja meg, hogy mi lesz az object neve)
cc -shared -Wl,-soname,libmylib.so -o libmylib.so mylib.o
Pascal alatt egyszerűbb a dolog; mivel a forrásban deklarálva van, hogy ez egy library, így elég csak simán a
fpc mylib.pas
paranccsal megküldeni.

Ezzel előállt a library mindkét nyelven, benne a mylib_a, mylib_b, mylib_c symbolokkal; lehet ellenőrizni a
objdump -T libmylib.so
paranccsal. A kimenet a C-s esetén:
libmylib.so:     file format elf64-x86-64

DYNAMIC SYMBOL TABLE:
0000000000000000  w   D  *UND*  0000000000000000              _ITM_deregisterTMCloneTable
0000000000000000  w   D  *UND*  0000000000000000              __gmon_start__
0000000000000000  w   D  *UND*  0000000000000000              _Jv_RegisterClasses
0000000000000000  w   D  *UND*  0000000000000000              _ITM_registerTMCloneTable
0000000000000000  w   DF *UND*  0000000000000000  GLIBC_2.2.5 __cxa_finalize
0000000000201020 g    D  .data  0000000000000000  Base        _edata
0000000000201028 g    D  .bss   0000000000000000  Base        _end
0000000000000690 g    DF .text  000000000000000f  Base        mylib_a
000000000000069f g    DF .text  000000000000001a  Base        mylib_b
0000000000201020 g    D  .bss   0000000000000000  Base        __bss_start
0000000000000550 g    DF .init  0000000000000000  Base        _init
00000000000006dc g    DF .fini  0000000000000000  Base        _fini
00000000000006b9 g    DF .text  0000000000000022  Base        mylib_c
És a Pascalos esetén:
libmylib.so:     file format elf64-x86-64

DYNAMIC SYMBOL TABLE:
0000000000000a10 g    DF .text  0000000000000005 mylib_a
0000000000000a20 g    DF .text  0000000000000005 mylib_b
0000000000000a30 g    DF .text  0000000000000005 mylib_c
Amint látszik a C-s dinamikusan linkelődik a libc-hez, a míg a Pascalosnak nincs külső függősége. Cserébe a C-s nincs 6 kB se (strippelve), míg a Pascalos 30 kB felett van.

Mielőtt a főprogramra térnénk, először meg kell csinálni a library-hoz tartozó API-t. C esetén ez egy header file (mylib.h) képében fog manifesztálódni (lehetne .c is .h helyett; de így nem fog ütközni a neve a library-éval), Pascal esetén pedig egy unit file (mylib.pp, szintén az ütközést preventálandó nem .pas). Az API fog felelni azért, hogy betöltse/felszabadítsa a library-t, illetve, hogy a megfelelő szimbólumokat a főprogram számára használható függvény-prototípusokhoz kösse.

Kezdjük a betöltéssel és a bezárással.
Ezt C alatt a dlopen() függvénnyel lehet elérni (manualok: POSIX, Linux, FreeBSD, OpenBSD, Solaris), Pascal alatt a LoadLibrary() függvénnyel.
Utóbbinak csak a library útvonala kell (elég a fájlnév is, ha a library olyan könyvtárban van, ami szerepel a LD_LIBRARY_PATH környezeti változóban), előbbinek azonban van egy második paramétere is, ami azt határozza meg, hogy milyen megközelítéssel töltse be, vagy épp szabadítsa fel a library-t, végezze el a relokálásokat (azon szimbólumok referenciáinak helyes címre való átirányítását, amiknek a címe csak a betöltődés után lesz ismert) és szabályozza a szimbólumok hozzáférhetőségét is. Az RTLD_NOW flag használata (ill. Linux alatt a LD_BIND_NOW környezeti változó bármilyen - nem üres - stringre való beállítása) azt eredményezi, hogy az összes relokációt azonnal elvégzi a program, míg a RTLD_LAZY flag használata esetén csak akkor történik meg egy adott szimbólum relokálása, ha hozzá akar férni a program. Az RTLD_GLOBAL flag azért felel, hogy a betöltött library szimbólumai hozzáférhetőek legyenek más a program által betöltött library-k számára is, a RTLD_LOCAL pedig ennek ellentétét jelenti. Namármost, mivel globális hozzáférhetőség esetén az így betöltött library-k szimbólumai azonos nevek esetén ütközni fognak és a pluginek mind szükségszerűen tartalmazni fogják azokat a szimbólumokat, amikkel a program kapcsolódni tud hozzájuk, így ebből kiderül, hogy pluginek esetén a RTLD_LOCAL használata javallott. A feloldási megközelítés alapvetően mindegy, de a program működésétől függően lehet a RTLD_LAZY flag-gel teljesítménynövekedést elérni, ha pl. a program adott körülmények között adott szimbólumokhoz egyáltalán nem nyúl. A többi - egyébként non-POSIX - flag minket most nem érint.
Sikeres betöltés esetén egy pointert kapunk, ami a library handle-jére mutat, sikertelen esetben ez NULL, ill. Pascalban DynLibs.NilHandle (ami a gyakorlatban ugyanúgy nullát jelent, csak Int64 típussal). Azt viszont nem árt tudni, hogy bár újbóli kinyitási kísérlet esetén újból ugyanazt a handle-t fogjuk visszakapni, mert nem tölti be többször a library-t - viszont pont azért, mert a kódban akár több egymástól független rész is betölthet egy adott library-t - felszabadítani a library-t csak ugyanannyi bezárással lehet, mint ahányszor megnyitottuk. Bezárni pedig a dlclose() függvénnyel lehet a library-t C alatt (manualok: POSIX, Solaris, a többieké ugyanott van, ahol a dlopen()-é is) és a FreeLibrary() függvénnyel Pascal alatt.
Ennek megfelelően a betöltés és bezárás C-ben:
#include <dlfcn.h>

...

void *handle = dlopen("libmylib.so", RTLD_NOW | RTLD_LOCAL);
dlclose(handle);
És Pascalban:
uses DynLibs;
var handle: TLibHandle;

...

handle := LoadLibrary('libmylib.so');
FreeLibrary(handle);
Folytassuk a relokálással.
A szimbólumok konkrét feloldásáért/relokálásáért a dlsym() függvény felel C alatt (manualok: POSIX, Solaris, a többieké pedig ismét ugyanott található, ahol a dlopen()) és a GetProcedureAddress() függvény Pascal alatt.
Példa C-ben:
int (*mylib_a)(int a);

...

mylib_a = dlsym(handle, "mylib_a");
És Pascalban:
type Tmylib_a = function (a: longint): longint; cdecl;
var mylib_a: Tmylib_a;

...

mylib_a := Tmylib_a(GetProcedureAddress(handle, 'mylib_a'));
Most már tkp. be tudjuk tölteni a library-t és használni a függvényeit. Mivel azonban itt pluginekről beszélünk, azaz több library-nk lesz, azonos függvényekkel, így mindenképpen szükség lesz valamilyen szintű "konténeresítésre", hogy egyrészt ne akadjanak össze (ezúttal nem szimbólumszinten, mint feljebb, hanem API szinten), másrészt meg, hogy tudjuk, hogy melyik relokált függvény melyik handle-höz is tartozik. Ezt lehet akár handle és funkciótömbökkel is csinálni, de sokkal egyszerűbb, ha bepakoljuk egy struct-ba
typedef struct
{
	void *handle;
	int (*mylib_a)(int a);
	void (*mylib_b)(int *b);
	int (*mylib_c)(int *c);
} mylib;
vagy record-ba
type
	Tmylib_a = function (a: longint): longint; cdecl;
	Tmylib_b = procedure (b: plongint); cdecl;
	Tmylib_c = function (c: plongint): longint; cdecl;

	Tmylib = record
		handle: TLibHandle;
		mylib_a: Tmylib_a;
		mylib_b: Tmylib_b;
		mylib_c: Tmylib_c;
	end;
	Pmylib = ^Tmylib;
és akkor minden pluginhez tartozhat egy ilyen konténer.

Valamivel fentebb tisztáztuk, hogy egy ilyen plugint egyszer nyitunk ki, így ezt nem ártana pl. lekezelni a lezáráskor (C)
void close_mylib(mylib *lib)
{
	if ((lib != NULL) && (lib->handle != NULL))
	{
		dlclose(lib->handle);
		lib->handle = NULL;
	}
}
(Pascal)
procedure close_mylib(lib: Pmylib);
begin
	if ((lib <> nil) and (lib^.handle <> DynLibs.NilHandle)) then
	begin
		FreeLibrary(lib^.handle);
		lib^.handle := DynLibs.NilHandle;
	end;
end;
és a megnyitáskor is. C:
#define MYLIB_ERROR_INVALID_CONTAINER	1
#define MYLIB_ERROR_ALREADY_OPEN	2
#define MYLIB_ERROR_CANNOT_OPEN	3
#define MYLIB_ERROR_MISSING_SYMBOL	4

int open_mylib(mylib *lib, char *path)
{
	if (lib == NULL)
	{
		return MYLIB_ERROR_INVALID_CONTAINER;
	}

	if (lib->handle != NULL)
	{
		return MYLIB_ERROR_ALREADY_OPEN;
	}

	lib->handle = dlopen(path, RTLD_NOW | RTLD_LOCAL);
	if (lib->handle == NULL)
	{
		return MYLIB_ERROR_CANNOT_OPEN;
	}

	lib->mylib_a = dlsym(lib->handle, "mylib_a");
	if (lib->mylib_a == NULL)
	{
		close_mylib(lib);
		return MYLIB_ERROR_MISSING_SYMBOL;
	}

	lib->mylib_b = dlsym(lib->handle, "mylib_b");
	if (lib->mylib_b == NULL)
	{
		close_mylib(lib);
		return MYLIB_ERROR_MISSING_SYMBOL;
	}

	lib->mylib_c = dlsym(lib->handle, "mylib_c");
	if (lib->mylib_c == NULL)
	{
		close_mylib(lib);
		return MYLIB_ERROR_MISSING_SYMBOL;
	}

	return 0;
}
Pascal:
const
	MYLIB_ERROR_INVALID_CONTAINER =	1;
	MYLIB_ERROR_ALREADY_OPEN =	2;
	MYLIB_ERROR_CANNOT_OPEN =	3;
	MYLIB_ERROR_MISSING_SYMBOL =	4;

function init_mylib(lib: Pmylib; path: string): longint;
begin
	if (lib = nil) then
	begin
		result := MYLIB_ERROR_INVALID_CONTAINER;
		exit;
	end;

	if (lib^.handle <> DynLibs.NilHandle) then
	begin
		result := MYLIB_ERROR_ALREADY_OPEN;
		exit;
	end;

	lib^.handle := LoadLibrary(path);
	if (lib^.handle = DynLibs.NilHandle) then
	begin
		result := MYLIB_ERROR_CANNOT_OPEN;
		exit;
	end;

	lib^.mylib_a := Tmylib_a(GetProcedureAddress(lib^.handle, 'mylib_a'));
	if (lib^.mylib_a = Tmylib_a(DynLibs.NilHandle)) then
	begin
		close_mylib(lib);
		result := MYLIB_ERROR_MISSING_SYMBOL;
		exit;
	end;

	lib^.mylib_b := Tmylib_b(GetProcedureAddress(lib^.handle, 'mylib_b'));
	if (lib^.mylib_b = Tmylib_b(DynLibs.NilHandle)) then
	begin
		close_mylib(lib);
		result := MYLIB_ERROR_MISSING_SYMBOL;
		exit;
	end;

	lib^.mylib_c := Tmylib_c(GetProcedureAddress(lib^.handle, 'mylib_c'));
	if (lib^.mylib_c = Tmylib_c(DynLibs.NilHandle)) then
	begin
		close_mylib(lib);
		result := MYLIB_ERROR_MISSING_SYMBOL;
		exit;
	end;

	result := 0;
end;
(Pascalban természetesen ezt a két függvényt exportálni kell, C-ben pedig be kell húzni a stdlib.h-t a NULL-hoz.)

Ezzel le is tudtuk az API-t, most már jöhet a főprogram. Ebben semmi mást nem kell csinálni, mint behúzni az API-t és meghívogatni a megfelelő függvényeket a megfelelő paraméterrel, pl. a fájlnévvel és a deklarált konténerstruktúrával. Ezt most az egyszerűség kedvéért egy mezei globális változóban tároljuk; nyilván ezt allokálni kell mindegyik pluginhez, ha több pluginünk lesz, de szemléltetésnek most így is megteszi. C-ben:
char *libname = "./libmylib.so";
int err, a, b, c, c2;
mylib lib;

...

lib.handle = NULL;
rr = open_mylib(&lib, libname);
if (err != 0)
{
	fprintf(stderr, "Cannot open \"%s\" - error %d.\n", libname, err);
	return 1;
}

a = lib.mylib_a(9);
b = 10;
lib.mylib_b(&b);
c = 11;
c2 = lib.mylib_c(&c);
fprintf(stdout, "a = %d, b = %d, c = %d, c2 = %d\n", a, b, c, c2);

close_mylib(&lib);
És Pascalban:
const libname = './libmylib.so';
var
	err, a, b, c, c2: longint;
	lib: Tmylib;

...

lib.handle := DynLibs.NilHandle;
err := init_mylib(@lib, libname);
if (err <> 0) then
begin
	WriteLn(StdErr, 'Cannot open "' + libname + ' - error ' + IntToStr(err) + '.');
	Halt(1);
end;

a := lib.mylib_a(9);
b := 10;
lib.mylib_b(@b);
c := 11;
c2 := lib.mylib_c(@c);
WriteLn(StdOut, 'a = ' + IntToStr(a) + ', b = ' + IntToStr(b) + ', c = ' + IntToStr(c) + ', c2 = ' + IntToStr(c2));

close_mylib(@lib);
Pascalban ne felejtsük el a DynLibs unitot is behúzni a DynLibs.NilHandle használatához. (És persze a SysUtils a WriteLn()-hez és C-ben a stdio.h a fprintf()-hez.)

Ha megvan a főprogram, lehet fordítani. C-ben a dl lib kelleni fog a dinamikus library-khoz.
cc mytest.c -ldl -o mytest
Pascalban itt is elég csak simán megküldeni a fordítóval.
fpc mytest.pas
(Pascalban egyébként itt is és a librarynál is érdemes a -CX -Xs -XX kapcsolókat használni a smartlinkinghez és a strippinghez.)

És ezzel tulajdonképpen készen is vagyunk, a library működik. Illetve egy dolog még hátra van: az az eset, amikor nem a főprogram hívja meg a library valamelyik függvényét, hanem fordítva. Ezt kétféleképpen lehet megoldani: lokálisan és globálisan. Abban az esetben, amikor valamilyen aszinkron visszajelzést akarunk csinálni - azaz az az egy darab függvény fogja meghívni a főprogram valamely függvényét, akkor, amikor a főprogram őt meghívja - és a feladattól függően akár változhat, hogy mit is akarunk meghívatni, abban az esetben ezeket a callback függvényeket célszerű közvetlenül átadni a library egyes függvényeinek, tehát lokálisan megoldani. Amik viszont különféle segédfunkciókat valósítanak meg és több függvény is használhatja őket, bármikor, azokat az összes függvénynek el kell tudnia érni, azaz globálisan kell megoldani. Ez utóbbit két megközelítéssel lehet csinálni: automatikusan és manuálisan.

Automatikus megközelítés esetén a library-ben külsőnek megjelölt prototípusokat helyezünk el, a főprogramot pedig dinamikusan linkeljük; ilyenkor megnyitáskor a library fogja a saját függvényéhez kötni a főprogram szimbólumait.
Legyen egy új függvényünk a library kódjában. C:
int mylib_d(int *d)
{
	*d += 5;
	return mylib_caller(d);
}
Pascal:
function mylib_d(d: plongint): longint; cdecl;
begin
	d^ := d^ + 5;
	result := mylib_caller(d);
end;
Ne felejtsük el az interface fájlban az új függvényt betenni a "konténerbe", feloldani az inicializálófüggvényben, valamint Pascalban ne felejtsük el felvenni a library-ban az exports listára sem.
Ez egy egyelőre még nem létező függvényt hív meg, ennek kell még a prototípusa. C:
extern int mylib_caller(int *ptr);
Pascal:
function mylib_caller(ptr: plongint): longint; cdecl; external name 'mylib_caller';
Ezek után már csak a főprogramba kell bebiggyeszteni ezt az új függvényt. C:
int mylib_caller(int *ptr)
{
	*ptr += 5;
	return *ptr + 5;
}
Pascal:
function mylib_caller(ptr: plongint): longint; cdecl;
begin
	ptr^ := ptr^ + 5;
	result := ptr^ + 5;
end;
És persze a meghívását is. C:
d = 12;
d2 = lib.mylib_d(&d);
fprintf(stdout, "a = %d, b = %d, c = %d, c2 = %d, d = %d, d2 = %d\n", a, b, c, c2, d, d2);
Pascal:
d := 12;
d2 := lib.mylib_d(@d);
WriteLn(StdOut, 'a = ' + IntToStr(a) + ', b = ' + IntToStr(b) + ', c = ' + IntToStr(c) + ', c2 = ' + IntToStr(c2) + ', d = ' + IntToStr(d) + ', d2 = ' + IntToStr(d2));
Ne felejtsük el a két új változót felvenni és Pascalban exportálni a főprogramban a függvényt. (exports mylib_caller;)

A library fordítási opciói nem változnak, a főprogramé viszont igen: a linkernek át kell adni az --export-dynamic kapcsolót. Ezt C-ben a már fentebb használt -Wl segítségével lehet, míg Pascalban a -k felel ugyanezért. Tehát C:
cc mytest.c -ldl -Wl,--export-dynamic -o mytest
Pascal:
fpc -k--export-dynamic mytest.pas
(A Pascalnál egy kellemetlen mellékhatása ennek a megközelítésnek, hogy a kapott bináris tízszer akkora lesz, mert egy tonna egyéb szimbólumot is belefordít...)

A manuális megközelítés esetén a külső függvényeknek pointereket kell betenni a library-be, a főprogramot pedig nem kell dinamikusan linkelni (azaz itt nem változik a főprogram fordítása az alap felálláshoz képest), hanem helyette a főprogramnak kell feloldania eme pointerek szimbólumait és betöltenie a helyükre az odavágó rutinjainak címét.
Tehát C-ben a függvény ezúttal így néz ki:
int (*mylib_caller)(int *ptr);
Pascalban pedig így:
var mylib_caller: function (ptr: plongint): longint; cdecl; CVar;
Ellentétben az automatikus megközelítéssel, itt értelemszerűen ezt a szimbólumot be kell tenni az interface-ben a konténerbe és fel is kell oldani, hogy a főprogram hozzáférjen. Viszont az interface-ben itt nem maga a függvény pointere kell, hanem egy arra mutató pointer, hogy a főprogram be tudja írni a megfelelő értéket. C-ben
typedef struct
{
	...
	int (**mylib_caller)(int *ptr);
}
Pascalban pedig
type
	Tmylib_caller = function (ptr: plongint): longint; cdecl;
	Pmylib_caller = ^Tmylib_caller;

	Tmylib = record
		...
		mylib_caller: Pmylib_caller;
	end;
	Pmylib = ^Tmylib;
lesz a felállás és Pascal esetén a feloldás is ennek megfelelően alakul: Pmylib_caller-re kell castolni, nem közvetlenül a Tmylib_caller-re. A főprogram majdnem 100%-ban ugyanaz, mint az automatikus esetén, a különbség csak az, hogy itt a library megnyitása és a használata közé még be kell ékelni egy
*(lib.mylib_caller) = &mylib_caller;
sort C-ben és egy
(lib.mylib_caller)^ := @mylib_caller;
sort Pascalban, illetve ez utóbbiban az is eltér, hogy az exports mylib_caller; itt nem a főprogramban kell, hanem a library-ben.

Hátra van még a callback-es felállás, az (értelemszerűen) ugyanaz mind automatikus, mind manuális linkelésnél: van egy függvényünk a library-ben, aminek át lehet adni egy másik függvény pointerét, amit belül meghívhat. C:
void mylib_e(int e, void (*callback_ptr)(int))
{
	callback_ptr(e + 5);
}
Pascal:
type Tmylib_callback = procedure (val: longint); cdecl;

procedure mylib_e(e: longint; callback_ptr: Tmylib_callback); cdecl;
begin
	callback_ptr(e + 5);
end;
Exportálást Pascalban nem elfelejteni, ahogy az interface-ben a feloldást és a konténerbe való felvételt sem (C)
typedef struct
{
	...
	void (*mylib_e)(int e, void (*callback_ptr)(int));
}
(Pascal)
type
	Tmylib_callback = procedure (val: longint); cdecl;
	Tmylib_e = procedure (e: longint; callback_ptr: Tmylib_callback); cdecl;

	Tmylib = record
		...
		mylib_e: Tmylib_e;
	end;
	Pmylib = ^Tmylib;
Marad a főprogram, ahol először is kell maga a callback függvény (C)
void mylib_callback1(int val)
{
	e = val + 5;
}
(Pascal)
procedure mylib_callback1(val: longint); cdecl;
begin
	e := val + 5;
end;
és aztán a tényleges használat (C)
e = 13;
lib.mylib_e(e, &mylib_callback1);
fprintf(stdout, "a = %d, b = %d, c = %d, c2 = %d, d = %d, d2 = %d, e = %d\n", a, b, c, c2, d, d2, e);
(Pascal)
e := 13;
lib.mylib_e(e, @mylib_callback1);
WriteLn(StdOut, 'a = ' + IntToStr(a) + ', b = ' + IntToStr(b) + ', c = ' + IntToStr(c) + ', c2 = ' + IntToStr(c2) + ', d = ' + IntToStr(d) + ', d2 = ' + IntToStr(d2) + ', e = ' + IntToStr(e));
(Az új e változó itt értelemszerűen globális, hiszen a fő függvényen kívül a callback függvény is közvetlenül fér hozzá.)

Nos, nagyjából ennyi lenne a pluginezés, legalábbis, ami a lényeget illeti, bár biztos van egy-két specifikus dolog, ami nem lett kivesézve. Pl. arra nem tértem ki, hogy mi van, ha a pluginek egymás függvényeit akarják hívogatni; nos ezt értelemszerűen úgy lehet megoldani, hogy vagy van a főprogramban egy wrapper/transmitter-callback erre a célra, vagy nemes egyszerűséggel a főprogram, miután feloldotta a szimbólumokat mind a két (vagy több) pluginben, egyszerűen az egyes pluginok megfelelő callback pointereire beírja a másik plugin függvényeinek címét. (Ez utóbbi felállás egyébként nem célszerű; két mezei library (nem plugin) közt simán működhet, még két eltérő interface-ű plugin közt is, bár ott is csak 1-1 közt, viszont azonos interface-ű pluginek garmadája esetén symbol-spagetti szintű, sok sikert kategóriás a dolog.)

Ha esetleg valami nem lenne tiszta, a komplett példákat, buildscripttel, tokkal-vonóval le lehet tölteni ezekről a linkekről:
C - Basic setup
Pascal - Basic setup
C - Automatically linked setup
Pascal - Automatically linked setup
C - Manually linked setup
Pascal - Manually linked setup

Egyébként írtam egy-egy alap unitot (C-hez és Pascalhoz is), ami ezeknek a feladatoknak a többségét elvégzi; allokálja a struct-hoz/record-hoz szükséges tárterületet, majd feloldja az átadott szimbólumokat és ha van callback pointer beállítva, akkor a szimbólum címére beírja azt is. (Direct cross-plugin calling not supported. :P) Kicsit közérthetőbben pl. a fenti manuálisan linkelt library használata így nézne ki C-ben vele:
#include <stdio.h>
#include "dynlib_handler.c"

typedef struct
{
	void *handle;
	int (*mylib_a)(int a);
	void (*mylib_b)(int *b);
	int (*mylib_c)(int *c);
	int (*mylib_d)(int *d);
	void (*mylib_e)(int e, void (*callback_ptr)(int));
	void (**mylib_callback)(int *ptr);
} dynlib_handle;

int mylib_callback(int *ptr)
{
	*ptr += 5;
	return *ptr + 5;
}

dynlib_symbol_entry lib_entries[] =
{
	{ "mylib_a", (void *)NULL },
	{ "mylib_b", (void *)NULL },
	{ "mylib_c", (void *)NULL },
	{ "mylib_d", (void *)NULL },
	{ "mylib_e", (void *)NULL },
	{ "mylib_caller", (void *)&mylib_callback }
};
int lib_entry_count = sizeof(lib_entries) / sizeof(dynlib_symbol_entry);

int e;

void mylib_callback1(int val)
{
	e = val + 5;
}

int main()
{
	int err, serr, a, b, c, c2, d, d2;
	char *libname = "./libmylib.so";
	dynlib_handle *lib;

	lib = create_dynlib_handler(libname, lib_entries, lib_entry_count, &err, &serr);
	if (err != 0)
	{
		switch (err)
		{
			case DYNLIB_HANDLER_ERROR_CANNOT_ALLOC_MEMORY:
				fprintf(stderr, "Cannot allocate memory.\n");
			break;
			case DYNLIB_HANDLER_ERROR_CANNOT_OPEN_LIBRARY:
				fprintf(stderr, "Cannot open \"%s\".\n", libname);
			break;
			case DYNLIB_HANDLER_ERROR_MISSING_SYMBOL:
				fprintf(stderr, "Missing symbol \"%s\".\n", lib_entries[serr].symbol);
			break;
		}
		return e;
	}

	a = lib->mylib_a(9);
	b = 10;
	lib->mylib_b(&b);
	c = 11;
	c2 = lib->mylib_c(&c);
	d = 12;
	d2 = lib->mylib_d(&d);
	e = 13;
	lib->mylib_e(e, &mylib_callback1);
	fprintf(stdout, "a = %d, b = %d, c = %d, c2 = %d, d = %d, d2 = %d, e = %d\n", a, b, c, c2, d, d2, e);

	free_dynlib_handler(lib);

	return 0;
}
És így Pascalban:
program mytest;

{$mode objfpc}{$H+}

uses dynlib_handler, SysUtils;

type
	Tmylib_callback = procedure (val: longint); cdecl;
	Tmylib_a = function (a: longint): longint; cdecl;
	Tmylib_b = procedure (b: plongint); cdecl;
	Tmylib_c = function (c: plongint): longint; cdecl;
	Tmylib_d = function (d: plongint): longint; cdecl;
	Tmylib_e = procedure (e: longint; callback_ptr: Tmylib_callback); cdecl;
	Tmylib_caller = function (ptr: plongint): longint; cdecl;
	Pmylib_caller = ^Tmylib_caller;

	Tdynlib_handle = record
		lib: TLibHandle;
		mylib_a: Tmylib_a;
		mylib_b: Tmylib_b;
		mylib_c: Tmylib_c;
		mylib_d: Tmylib_d;
		mylib_e: Tmylib_e;
		mylib_caller: Pmylib_caller;
	end;
	Pdynlib_handle = ^Tdynlib_handle;

var
	err, serr, a, b, c, c2, d, d2, e: integer;
	lib: Pdynlib_handle;

function caller(ptr: pinteger): integer; cdecl;
begin
	ptr^ := ptr^ + 5;
	result := ptr^ + 5;
end;

procedure mylib_callback1(val: longint); cdecl;
begin
	e := val + 5;
end;

const
	lib_entries: Tdynlib_symbols =
	(
		( symbol: 'mylib_a'; ext_func: nil ),
		( symbol: 'mylib_b'; ext_func: nil ),
		( symbol: 'mylib_c'; ext_func: nil ),
		( symbol: 'mylib_d'; ext_func: nil ),
		( symbol: 'mylib_e'; ext_func: nil ),
		( symbol: 'mylib_caller'; ext_func: @caller )
	);

	libname = './libmylib.so';

begin
	lib := Pdynlib_handle(create_dynlib_handler(libname, @lib_entries, @err, @serr));
	if (err <> 0) then
	begin
		case err of
			DYNLIB_HANDLER_ERROR_CANNOT_ALLOC_MEMORY:
			begin
				WriteLn(StdErr, 'Cannot allocate memory.');
			end;
			DYNLIB_HANDLER_ERROR_CANNOT_OPEN_LIBRARY:
			begin
				WriteLn(StdErr, 'Cannot open ' + libname + '.');
			end;
			DYNLIB_HANDLER_ERROR_MISSING_SYMBOL:
			begin
				WriteLn(StdErr, 'Missing symbol ' + lib_entries[serr].symbol + '.');
			end;
		end;
		Halt(e);
	end;

	a := lib^.mylib_a(9);
	b := 10;
	lib^.mylib_b(@b);
	c := 11;
	c2 := lib^.mylib_c(@c);
	d := 12;
	d2 := lib^.mylib_d(@d);
	e := 13;
	lib^.mylib_e(e, @mylib_callback1);
	WriteLn(StdOut, 'a = ' + IntToStr(a) + ', b = ' + IntToStr(b) + ', c = ' + IntToStr(c) + ', c2 = ' + IntToStr(c2) + ', d = ' + IntToStr(d) + ', d2 = ' + IntToStr(d2) + ', e = ' + IntToStr(e));

	free_dynlib_handler(PPointer(lib));
end.
A unit is letölthető ezekről a linkekről:
C version
Pascal version

Asszem ennyi. Remélem sajtóhiba nem lesz a cikkben... :P Bikacsököt bilgécnek és a kurwa anyját a mikrofosnak!


English | Magyar
JS ki | CSS ki | Ékezetek ki | HiContrast
Lapozó:  (0 - 1428) 
<== | ==>
Ugrás a végére | Összes megjelenítése | Utolsó oldal
OpenOpera patches | Opera-SSL patches | Opera 12.15 source (Git repository) | Opera 12.15 source (Torrent) | Opera internal pages | Otter Browser Linux x64 - Qt5.15.2/QtWebKit5.602.1 (2024.12.02. 21:02)
OS for MC680x0 | OS for PPC | OS for Sparc64 | besztofbégéaefcé | CSÉNDZSLOG | WebToolz | DDG Shit Filter | Google Shit Filter | Progz | Fast CSS Box | Browser | OS | Agent | Statisztika | BBCode
Monospace font-family: Courier New | Browser default monospace
Email értesítő / Email notification ===> 
Keresés

Név: (max 255 byte)

Email: (max 255 byte) Nem kötelező!

Üzenet: (max 65536 kar.) 65536-0=65536




crap_vkn v4.34.0 by TCH
Thx to saxus for the escaped string decoder function (PHP), the realIP function (PHP) & the SQL handle layer (PHP), to thookerov for the int_divide function (PHP), to Jeff Anderson for the getSelText function (JS), to Alex King for the insertAtCursor function (JS), Flood3r for the new CSS styles, Pety for the spamprotection idea and some design and comfort ideas, MaxMind for the IP2Country database, famfamfam for the flags of countries and an unknown PHP programmer for the removeAccents function.



Kecskebaszók ide!