1 /**
2    Utility functions for writing XLLs
3  */
4 module xlld.framework;
5 
6 
7 import xlld.xlcall;
8 
9 
10 /**
11         Will free any malloc'd memory associated with the given
12         LPXLOPER, assuming it has any memory associated with it
13 
14    Parameters:
15 
16         LPXLOPER pxloper    Pointer to the XLOPER whose associated
17 */
18 void freeXLOper(T, A)(T pxloper, ref A allocator)
19     if(is(T == LPXLOPER) || is(T == LPXLOPER12))
20 {
21     import std.experimental.allocator: dispose;
22 
23     switch (pxloper.xltype & ~XlType.xlbitDLLFree) with(XlType) {
24         case xltypeStr:
25             if (pxloper.val.str !is null) {
26                 void* bytesPtr = pxloper.val.str;
27                 const numBytes = (pxloper.val.str[0] + 1) * wchar.sizeof;
28                 allocator.dispose(bytesPtr[0 .. numBytes]);
29             }
30             break;
31         case xltypeRef:
32             if (pxloper.val.mref.lpmref !is null)
33                 allocator.dispose(pxloper.val.mref.lpmref);
34             break;
35         case xltypeMulti:
36             auto cxloper = pxloper.val.array.rows * pxloper.val.array.columns;
37             const numOpers = cxloper;
38             if (pxloper.val.array.lparray !is null)
39             {
40                 auto pxloperFree = pxloper.val.array.lparray;
41                 while (cxloper > 0)
42                 {
43                     freeXLOper(pxloperFree, allocator);
44                     pxloperFree++;
45                     cxloper--;
46                 }
47                 allocator.dispose(pxloper.val.array.lparray[0 .. numOpers]);
48             }
49             break;
50         case xltypeBigData:
51             if (pxloper.val.bigdata.h.lpbData !is null)
52                 allocator.dispose(pxloper.val.bigdata.h.lpbData);
53             break;
54         default: // todo: add error handling
55             break;
56     }
57 }
58 
59 @("Free regular XLOPER")
60 unittest {
61     import xlld.memorymanager: allocator;
62     XLOPER oper;
63     freeXLOper(&oper, allocator);
64 }
65 
66 @("Free XLOPER12")
67 unittest {
68     import xlld.memorymanager: allocator;
69     XLOPER12 oper;
70     freeXLOper(&oper, allocator);
71 }
72 
73 /**
74    Wrapper for the Excel12 function that allows passing D arrays
75 
76    Purpose:
77         A fancy wrapper for the Excel12() function. It also
78         does the following:
79 
80         (1) Checks that none of the LPXLOPER12 arguments are 0,
81             which would indicate that creating a temporary XLOPER12
82             has failed. In this case, it doesn't call Excel12
83             but it does print a debug message.
84         (2) If an error occurs while calling Excel12,
85             print a useful debug message.
86         (3) When done, free all temporary memory.
87 
88         #1 and #2 require _DEBUG to be defined.
89 
90    Parameters:
91 
92         int xlfn            Function number (xl...) to call
93         LPXLOPER12 pxResult Pointer to a place to stuff the result,
94                             or 0 if you don't care about the result.
95         int count           Number of arguments
96         ...                 (all LPXLOPER12s) - the arguments.
97 
98    Returns:
99 
100         A return code (Some of the xlret... values, as defined
101         in XLCALL.H, OR'ed together).
102 
103    Comments:
104 */
105 
106 int Excel12f(int xlfn, LPXLOPER12 pxResult, LPXLOPER12[] args...) nothrow @nogc
107 {
108     import xlld.xlcallcpp: Excel12v;
109     import std.algorithm: all;
110 
111     assert(args.all!(a => a !is null));
112     return Excel12v(xlfn, pxResult, cast(int)args.length, cast(LPXLOPER12*)args.ptr);
113 }
114 
115 int Excel12f(int xlfn, LPXLOPER12 result, XLOPER12[] args...) nothrow {
116 
117     auto ptrArgs = new LPXLOPER12[args.length];
118 
119     foreach(i, ref arg; args)
120         ptrArgs[i] = () @trusted { return &arg; }();
121 
122     return Excel12f(xlfn, result, ptrArgs);
123 }
124 
125 int Excel12f(int xlfn, LPXLOPER12 pxResult) nothrow @nogc
126 {
127     return Excel12f(xlfn, pxResult, LPXLOPER12[].init);
128 }
129 
130 __gshared immutable excel12Exception = new Exception("Error calling Excel12f");
131 
132 /**
133    D version of Excel12f. "D version" in the sense that the types
134    are all D types. Avoids having to manually convert to XLOPER12.
135    e.g. excel12(xlfFoo, 1.0, 2.0);
136 
137    Returns a value of type T to specified at compile time which
138    must be freed with xlld.memorymanager: autoFreeAllocator
139  */
140 T excel12(T, A...)(int xlfn, auto ref A args) @trusted {
141     import xlld.memorymanager: gTempAllocator, autoFreeAllocator;
142     import xlld.wrap: toXlOper, fromXlOper;
143     import xlld.xlcall: xlretSuccess;
144 
145     XLOPER12[A.length] operArgs;
146     LPXLOPER12[A.length] operArgPtrs;
147 
148     scope(exit) gTempAllocator.deallocateAll;
149 
150     foreach(i, _; A) {
151         operArgs[i] = args[i].toXlOper(gTempAllocator);
152         operArgPtrs[i] = &operArgs[i];
153     }
154 
155     XLOPER12 result;
156     if(Excel12f(xlfn, &result, operArgPtrs) != xlretSuccess)
157         throw excel12Exception;
158 
159     return result.fromXlOper!T(autoFreeAllocator);
160 }
161 
162 /**
163    Version of excel12 that avoids automatic memory management
164    by asking the caller to supply a compile-time function
165    to call on the result.
166  */
167 auto excel12Then(alias F, A...)(int xlfn, auto ref A args) {
168     import std.traits: Parameters, Unqual, isArray, ReturnType;
169     import std.experimental.allocator: dispose;
170     import xlld.memorymanager: autoFreeAllocator;
171 
172     static assert(Parameters!F.length == 1, "Must pass function of one argument");
173     alias T = Parameters!F[0];
174     auto excelRet = excel12!T(xlfn, args);
175     static if(is(ReturnType!F == void))
176         F(excelRet);
177     else
178         auto ret = F(excelRet);
179 
180     static if(isArray!T) {
181         import std.range: ElementType;
182         alias RealType = Unqual!(ElementType!T)[];
183     } else
184         alias RealType = Unqual!T;
185 
186     void freeRet(U)() @trusted {
187         autoFreeAllocator.dispose(cast()excelRet);
188     }
189 
190     static if(__traits(compiles, freeRet!RealType()))
191         freeRet!RealType();
192 
193     static if(!is(ReturnType!F == void))
194         return ret;
195 }