excel-d v0.0.1 - D API to write functions callable from Excel

Atila Neves via Digitalmars-d-announce digitalmars-d-announce at puremagic.com
Mon Apr 24 14:59:34 PDT 2017


On Monday, 20 March 2017 at 20:09:58 UTC, Atila Neves wrote:
> http://code.dlang.org/packages/excel-d
>
> This dub package allows D code to be called from Excel. It uses 
> compile-time reflection to register the user's code in an XLL 
> (a DLL loaded by Excel) so no boilerplate is necessary. Not 
> even `DllMain`! It works like this:
>
> main.d:
>
> import xlld;
> mixin(wrapAll!(__MODULE__, "funcs"));
>
> funcs.d:
>
> import xlld;
>
> @Register(ArgumentText("Array to add"),
>           HelpTopic("Adds all cells in an array"),
>           FunctionHelp("Adds all cells in an array"),
>           ArgumentHelp(["The array to add"]))
> double FuncAddEverything(double[][] args) nothrow @nogc {
>     import std.algorithm: fold;
>     import std.math: isNaN;
>
>     double ret = 0;
>     foreach(row; args)
>         ret += row.fold!((a, b) => b.isNaN ? 0.0 : a + b)(0.0);
>     return ret;
> }
>
> This code, once compiled to an XLL (see the example in the 
> repository) and loaded in Excel, will permit a user to write 
> `=FuncAddEverything(B1:D6)` and have the cell populated with 
> the sum of all arguments in that range.
>
> There's a lot going on behind the scenes, and that's the point. 
> For instance, the function above takes a 2d array of doubles 
> and returns a double, but those aren't Excel types. The wrapper 
> code writes out an Excel-compatible type signature at 
> compile-time, does all the conversions, calls the user's code 
> then converts back to types Excel can understand.
>
> The user functions have to be `nothrow`. This is guaranteed at 
> compile-time and the user gets a warning message about the 
> function not being considered. `@nogc` is optional but won't 
> work if returning a string due to allocations. The code is 
> compatible with std.experimental.allocator internally but 
> there's no way to specify an allocator currently for the 
> registration.
>
> I can make the registration mixin easier to use but haven't 
> gotten around to it yet. I thought it was better to put the 
> code out there as is than wait.
>
> This is another one of those "only in D" packages due to the 
> metaprogramming, which is always nice.
>
> Atila

Now with more `@nogc`. Before, this worked fine:

double func(double d) @nogc nothrow { return d * 2; }

The function is `@nogc`, the wrapper function (i.e. the function 
that Excel actually calls) is also `@nogc` via the magic of 
compile-time reflection. So far, so good. But what if you want to 
return a string or an array back to Excel. Oh, oh...

Enter the `@Dispose` UDA:


// @Dispose is used to tell the framework how to free memory that 
is dynamically
// allocated by the D function. After returning, the value is 
converted to an
// Excel type sand the D value is freed using the lambda defined 
here.
@Dispose!((ret) {
     import std.experimental.allocator.mallocator: Mallocator;
     import std.experimental.allocator: dispose;
     Mallocator.instance.dispose(ret);
})
double[] FuncReturnArrayNoGc(double[] numbers) @nogc @safe 
nothrow {
     import std.experimental.allocator.mallocator: Mallocator;
     import std.experimental.allocator: makeArray;
     import std.algorithm: map;

     try {
         // Allocate memory here in order to return an array of 
doubles.
         // The memory will be freed after the call by calling the
         // function in `@Dispose` above
         return Mallocator.instance.makeArray(numbers.map!(a => a 
* 2));
     } catch(Exception _) {
         return [];
     }
}

And Bob's your uncle.

Atila





More information about the Digitalmars-d-announce mailing list