Can't retrieve the archive from that URL.
britseyeview.com/plugin101.tar.bz2
Interested, so can you please fix?
On Monday, 10 March 2014 at 11:59:20 UTC, Steve Teale wrote:
On Sunday, 9 March 2014 at 12:07:22 UTC, Steve Teale wrote:
Now suppose that my D shared library contains a class, rather
that just module ctors/dtors, how do I go about creating an
instance of that class and using its methods?
After wandering down several dead-end paths, and help from
other contributors, I have finally come up with something that
looks like the basis of a plugin pattern for Linux DMD using
shared objects (.so files). This is somewhat long for a forum
post. You can download this readme and the associated files
from britseyeview.com/plugin101.tar.bz2
To get started, you need a base class that provides
declarations for all functions that the plugin will be allowed
to use externally. Why base class, and not interface? Well I
guess because interfaces don't provide any information about
data. If you create a shared library based on an interface,
then all the shared object methods that reference data in the
class that implements the interface fail miserably. I'm sure
someone will explain why - probably some obvious thing I have
overlooked.
OK, so my base class is:
module plugin;
class Plugin
{
int n;
this(int _n) { n = _n; }
int foo() { return int.min; }
void bar() {}
}
The class that implements this base in the shared library is:
module exta;
import plugin;
import std.stdio;
import std.math;
class ExtA: Plugin
{
double d;
this(int n) { super(n); d = PI; }
override int foo() { return ++n; }
override void bar() { writefln("Done my thing (%f)", d); }
}
Plugin getInstance(int n)
{
return new ExtA(n);
}
shared static this() {
writeln("exta.so shared static this");
}
shared static ~this() {
writeln("exta.so shared static ~this");
}
The module ctor/dtor are included because that has become
conventional in discussions about dynamic loading. Otherwise,
the so has the class implementation - ExtA, and a shared method
to create an instance of same. It includes references to
methods in Phobos.
The test program is as follows:
module main;
import core.runtime;
import std.stdio;
import plugin;
extern(C) void* dlsym(void*, const char*);
alias Plugin function(int) pfi;
Plugin getPlugin(string name)
{
void* lib = Runtime.loadLibrary(name~".so");
if (lib is null)
{
writeln("failed to load plugin shared object");
return null;
}
void* vp = dlsym(lib,
"_D4exta11getInstanceFiZC6plugin6Plugin\0".ptr);
if (vp is null)
{
writeln("plugin creator function not found");
return null;
}
pfi f = cast(pfi) vp;
Plugin x = f(42);
if (x is null)
{
writeln("creation of plugin failed");
return null;
}
return x;
}
void main()
{
Plugin x = getPlugin("exta");
int n = x.foo();
writefln("n = %d", n);
x.bar();
}
The long symbol name used in the dlsym() call is of course from
the .map file generated when the .so file is created
These can be built using the following primitive makefile,
whose main purpose is to spell out the required compiler flags:
main :
dmd -c plugin.d
dmd -c -shared -fPIC exta.d
dmd exta.o -shared -defaultlib=libphobos2.so -map
dmd -c main.d
dmd main.o plugin.o -L-ldl -defaultlib=libphobos2.so -L-rpath=.
This assumes that the plugins will be in the same directory as
the executable (rpath=.).
Note that there is no call to Runtime.unloadLibrary(). The
assumption her is that once the plugin has been loaded it will
be there for the duration of the program. If you want to unload
it you'll probably have to make sure the plugin object is
purged from memory first, and I have not discovered how to do
that yet ;=(
Steve