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.

Reply via email to