comp.lang.ada
 help / color / mirror / Atom feed
From: "Dan'l Miller" <optikos@verizon.net>
Subject: Re: "Plugin°-based code
Date: Mon, 4 Jun 2018 07:03:02 -0700 (PDT)
Date: 2018-06-04T07:03:02-07:00	[thread overview]
Message-ID: <53a67bab-44f5-4199-b89d-b30de04a7f89@googlegroups.com> (raw)
In-Reply-To: <564cec8c-8da9-4e59-a687-5408fd3a429f@googlegroups.com>

On Monday, June 4, 2018 at 5:29:26 AM UTC-5, mockturtle wrote:
> Dear.all, 
> I have a doubt about what it could be the best solution for some code that I am writing.  I am afraid that the introduction before the actual question is a bit long.  
> 
> ** The problem **
> 
> Let me use an example.  Suppose I am writing a program to plot, say, a GANTT chart.  I want the user to be able to choose the output format (say, PDF, SVG or even LaTeX) by giving a suitable option on the command line and I want to be able to add new output format easily, maybe even load the "format handler" as a dynamic library at run-time (so I do not need recompilation).  (Incidentally, I did some experiments with the dynamic loading and it works)
> 
> The structure I give to the code is as follows: first I define an interface for an "abstract plotter," for example
> 
>   package Plotters is 
>     type Abstract_Plotter is interface;
> 
>     procedure Line(P: Abstract_Plotter;
>                    X1,Y1, X2, Y2:Float) 
>     is abstract;
> 
>     -- And so on...
>   end Plotters;
> 
> Every actual plotter will be a descendant of Abstract_Plotters, for example
> 
>   package Plotters.SVG is
>      type SVG_Plotter is 
>      new Abstract_Plotter with private;
> 
>      -- Blah, blah ...
>   end Plotters.SVG;
> 
> In order to allow the user to select the actual plotter via the command line I use a generic package Plugin_Table that implements a kind of "map" from names to descendant of Abstract_Plotter.  Plugin_Table provides two operations:
> 
>   (1) Register_Plugin(T: Tag; Name: String)  
> 
>          used by a concrete plotter to "register" itself to the table.  Usually it is called from the "initialization" part of the body of the package definynig the concrete plotter.  For example, plotters-svg.adb could call
> 
>      Register_Plugin(SVG_Plotter'Tag, "svg");
> 
>   (2) Get_Plugin (Name:String) return Abstract_Plotter'Class
> 
>        to be called to generate a concrete plotter.
> 
> In a future version Plugin_Table will be able to search for some dynamic library to be loaded if a plugin with the given name is not registered.
> 
>    ** The Question ** 
> 
> In order to have all the built-in plugins registered in my executable, I need to "with" them,

No, that would make them elaborated too soon.  You want to delay the elaboration to run-time, so that the DLL's link-load/elaboration occurs far after the link-load/elaboration of the executable.

Although your intuition is correct regarding what needs to happen:

> so that they are linked to my code and the initialization part of their bodies is elaborated. However, no part of my code call explicitly, say, SVG_Plotter.  Therefore, my solution is to add something like
> 
>   with Plotters.SVG;
>   pragma Warnings(Off, Plotter.SVG);
> 
> in the "main." It works, but it leaves me a bit unsatisfied since it seems an "hack".  Also, since no actual code is called directly I am not sure if the binder could take the liberty of not including them (maybe it cannot, but I am not sure).
> 
> Do you have any better solution?

https://stackoverflow.com/questions/18141643/create-an-dll-written-in-ada

The StackOverflow question above points in the correct direction regarding the creation of a DLL (e.g., .so file) with GNAT*.

Next, link-load the DLL at run-time.  For GNAT, that is covered for Microsoft Windows at:

https://gcc.gnu.org/onlinedocs/gnat_ugn/Building-DLLs-with-gnatdll.html

… or for operating systems with .so shared-libraries (e.g., Linux, Unix, MacOS X**), that is covered for GNAT* at:

https://www.adacore.com/gems/gem-109-ada-plug-ins-and-shared-libraries-part-1
and
https://www.adacore.com/gems/gem-110-ada-plug-ins-and-shared-libraries-part-2

* If you are using some other vendor's nonGNAT Ada compiler, the technique would be somewhat similar.

** MacOS X has both .so shared libraries from BSD Unix heritage and .dylib (Dylan libraries) from MachO heritage.  Presumably the .dylib approach would strongly resemble much of the .so approach, but .dylib is probably interesting only if mixing Ada with Objective-C or with Swift (which you did not mention), and then the .dylib would need be supplemented Objective-C runtime framework API.  If Ada-to-Ada strictly ‘on the backend processing’ not invocable by UI/UX from Swift or from Objective-C, then stick with .so shared libraries in MacOS if at all possible.  And there is no debugger support on iDevices for GNAT (due to lack of LLDB support, due in turn to lack of modern LLVM backend for GNAT), so don't even think about iOS for the foreseeable future, which might be foreshadowing an increasing problem when/if MacOS too goes to ARM:
https://www.TheInquirer.net/inquirer/news/3033362/apple-reportedly-poaches-senior-intel-staff-for-secret-oregon-hardware-lab

> Also, I am not sure if I risk getting in trouble with Elaboration order.  I think not, but I am not 100% sure.
> What do you think?

The FSF & AdaCore references above seem to thoroughly cover elaboration order.  The topic gets impractically complex in GNAT if the DLL/.so creates its own Ada tasks, so avoid doing that by creating any needed Ada tasks in the parent executable instead.


  parent reply	other threads:[~2018-06-04 14:03 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-06-04 10:29 "Plugin°-based code mockturtle
2018-06-04 11:59 ` Dmitry A. Kazakov
2018-06-04 14:03 ` Dan'l Miller [this message]
2018-06-04 22:08 ` Plugin-based code Randy Brukardt
2018-06-05  8:42 ` "Plugin°-based code Brian Drummond
replies disabled

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox