| 263 | |
---|
| 264 | == Handling Errors == |
---|
| 265 | |
---|
| 266 | Error handling in C is a joke. You either have to check return codes, or use a setjmp/longjmp protocol, or check a global errno or something equally tedious and ugly. In D, we have exceptions. In MiniD, we also have exceptions. Actually, as far as the implementation is concerned, these are one and the same. |
---|
| 267 | |
---|
| 268 | When MiniD code throws an exception, on the native side it really does throw an exception as well (one derived from MDException, to be exact). The interpreter does some catching and rethrowing trickery to unwind the script call stack, but when it hits a native call, it rethrows the exception to allow native code to catch it. When native code throws an exception, the exact same mechanism is used. This means that any exceptions that script code throws can be caught by native code and vice versa. Isn't that nice? |
---|
| 269 | |
---|
| 270 | The interpreter will only bother with exceptions derived from MDException, however. If some other exception occurs, such as an exception in a native library function, or an out of memory error, stack overflow, access violation etc. it will skip over the exception handlers in the interpreter. This is good in most cases, but sometimes you don't want this to happen, such as when you're writing a library wrapper for a native library to expose code to MiniD. In this case, you usually want to translate any non-MiniD exceptions into MiniD exceptions. For this, MDState provides the safeCode method. |
---|
| 271 | |
---|
| 272 | safeCode takes a lazy expression of any type. It evaluates that expression, and returns its result. However, if the expression throws a MiniD exception, safeCode will rethrow it, and if the expression throws a non-MiniD exception, safeCode will throw a new MiniD exception whose message is the result of the thrown exception's .toUtf8() method. This way, you can wrap calls to native calls which can throw an exception in safeCode, and any exceptions it throws will be catchable by MiniD code. |
---|
| 273 | |
---|
| 274 | As an example, here is the implementation of the string.toInt function in the standard library. |
---|
| 275 | |
---|
| 276 | {{{ |
---|
| 277 | #!d |
---|
| 278 | int toInt(MDState s, uint numParams) |
---|
| 279 | { |
---|
| 280 | dchar[] src = s.getContext!(dchar[]); |
---|
| 281 | |
---|
| 282 | int base = 10; |
---|
| 283 | |
---|
| 284 | if(numParams > 0) |
---|
| 285 | base = s.getParam!(int)(0); |
---|
| 286 | |
---|
| 287 | s.push(s.safeCode(Integer.toInt(src, base))); |
---|
| 288 | return 1; |
---|
| 289 | } |
---|
| 290 | }}} |
---|
| 291 | |
---|
| 292 | You'll notice a new function -- getContext -- in there. That gets the context parameter which was passed to this function. This is how you write methods in native code. The string library is set as the type metatable for strings, and so when you call toInt on a string object, the string on which it was called is passed as the context, which we then retrieve in a similar way to any other parameter. |
---|
| 293 | |
---|
| 294 | The optional base parameter is retrieved next, and then it calls the Integer (really tango.text.convert.Integer) toInt function with the string and the base. Note the use of safeCode wrapped around that expression. Integer.toInt can throw exceptions if there is a formatting error or so, and so we use safeCode to ensure that any exceptions that it throws are converted to MiniD exceptions. |