Is it possible to use DMD as a library to compile strings at runtime?
H. S. Teoh
hsteoh at quickfur.ath.cx
Sun Feb 2 06:03:01 UTC 2020
On Sun, Feb 02, 2020 at 03:16:46AM +0000, Saurabh Das via Digitalmars-d-learn wrote:
> On Saturday, 1 February 2020 at 20:37:03 UTC, H. S. Teoh wrote:
[...]
> > I've actually done this before in an equation grapher program: the
> > user inputs an equation, the program generates D code to compute the
> > equation, then runs dmd to compile it into a shared library, and
> > opens the shared library and looks up the symbol to execute the
> > compiled code. Dmd is fast enough that this actually works fairly
> > well. When the input to dmd is small, it's so fast you don't even
> > notice it.
[...]
> This approach seems more tractable at present. Would you have any
> example code lying around for this?
[...]
It's very simple. Let's say you have your code in some string called
'code'. Since dmd nowadays can take stdin as input (specify "-" as input
filename), all you have to do is to assemble your dmd command and use
std.process's awesome API to run it:
/*
* Step 1: Compile the code
*/
string code = ...;
auto cmd = [
"/usr/bin/dmd", // or wherever your dmd is
"-O", // or whatever other flags you need
"-fPIC", "-shared", // this is important
"-of" ~ soFilename, // specify output filename
"-" // read from stdin
]
// This part is a bit involved because we have to spawn the
// compiler as a child process then write our code string into
// its stdin.
// Alternatively, just write your code into a temporary file and
// pass the filename to dmd, then you can just use
// std.process.execute() which has a much simpler API.
import std.process : pipeProcess, Redirect, wait;
auto pipes = pipeProcess(cmd, Redirect.stdin | Redirect.stdout |
Redirect.stderrToStdout);
// Send code to compiler
pipes.stdin.write(code);
pipes.stdin.flush();
pipes.stdin.close();
// Read compiler output (optional)
auto app = appender!string();
enum chunkSize = 4096;
pipes.stdout.byChunk(chunkSize)
.copy(app);
// Wait for compiler to finish
auto status = wait(pipes.pid);
auto output = app.data;
if (status != 0)
throw new Exception("Failed to compile code:\n" ~ output);
/*
* Step 2: Load the compiled library.
*/
// This is for Posix; replace with Windows equivalent if you're
// on Windows.
auto libhandle = dlopen(soFilename.toStringz, RTLD_LAZY | RTLD_LOCAL);
if (libhandle is null) ... /* handle error here */
// Look up entry point by symbol.
string entryPoint = ...; /* symbol of library entry point */
alias FunType = int function(string); // your function signature here
auto funptr = cast(FunType) dlsym(libhandle, entryPoint.toStringz);
/*
* Step 3: Use the compiled code.
*/
// Call the compiled function with whatever arguments.
int result = funptr("my input");
...
// Cleanup once you're done with the library.
dlclose(libhandle);
std.file.remove(soFilename);
T
--
First Rule of History: History doesn't repeat itself -- historians merely repeat each other.
More information about the Digitalmars-d-learn
mailing list