On Wed, May 20, 2015 at 7:43 AM, Nick Wellnhofer <[email protected]> wrote: > On 18/05/2015 02:09, Marvin Humphrey wrote: >> >> As an alternative to throwing exceptions or storing exception objects in >> thread-local variables, let's consider encoding error information into >> return values using a crude form of algebraic data types: pre-defined >> "MAYBE" types which can be either an Err or something else. > > +1. This is a great idea.
This won't work with Ruby's GC, but here's another variant implementing a quasi-tagged-union using the low bit to distinguish error from value. https://gist.github.com/rectang/d229dfd3e27057540940 * MAYBE types are typedef'd to `size_t`. (This can encode enough bits to hold pointers and primitive types narrower than a pointer. MAYBE types for wider primitives will need to be implemented as structs.) * The low bit is set to 1 to indicate success and 0 for failure. * Access to the MAYBE type is funneled through subroutines. Unlike the previous proof-of-concept code which inspects the class pointer, using the low bit as a type tag allows us to differentiate between an error condition and a deliberate Err* return value. The reason this approach is incompatible with the MRI Ruby runtime is that MRI's conservative GC scans the C stack looking for Ruby object pointers, and setting the low bit would hide them from it. However, if we only manipulate MAYBE types through subroutines, they could be two-slot structs under some hosts (such as Ruby) and integers under others. Marvin Humphrey // ------------------------------------------------------------------------- #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAYBE_SUCCESS_FLAG 1 #define MAYBE_SUCCESS_MASK (((size_t)-1) ^ MAYBE_SUCCESS_FLAG) typedef enum { OBJ, ERR, GREETER } Class; typedef struct Obj { Class klass; } Obj; typedef struct Err { Class klass; char *message; } Err; typedef struct Greeter { Class klass; char *greeting; } Greeter; typedef size_t MAYBEGreeter; static inline MAYBEGreeter MAYBEGreeter_good(Greeter *greeter) { return ((MAYBEGreeter)greeter) | MAYBE_SUCCESS_FLAG; } static inline MAYBEGreeter MAYBEGreeter_bad(void *error) { return (MAYBEGreeter)error; } static void S_fail_unwrap(size_t maybe, Class klass) { Err *err = (Err*)maybe; (void)klass; const char *message = err == NULL ? "unexpected NULL" : err->message; fprintf(stderr, "An error occurred: %s\n", message); exit(EXIT_FAILURE); } static inline Obj* SI_unwrap_any(size_t maybe, Class klass) { if (!(maybe & MAYBE_SUCCESS_FLAG)) { S_fail_unwrap(maybe, klass); } return (Obj*)(maybe & MAYBE_SUCCESS_MASK); } Err* Err_new(const char *message) { Err *self = (Err*)malloc(sizeof(Err)); self->klass = ERR; self->message = strdup(message); return self; } MAYBEGreeter Greeter_new() { Greeter *self = (Greeter*)malloc(sizeof(Greeter)); self->klass = GREETER; self->greeting = "Hello, world!"; return MAYBEGreeter_good(self); } MAYBEGreeter Greeter_bad() { return MAYBEGreeter_bad(Err_new("*earth-shattering ka-boom*")); } static inline Greeter* Greeter_UNWRAP(MAYBEGreeter maybe) { return (Greeter*)SI_unwrap_any(maybe, GREETER); } int main() { Greeter *good = Greeter_UNWRAP(Greeter_new()); printf("Good Greeter says: %s\n", good->greeting); Greeter *bad = Greeter_UNWRAP(Greeter_bad()); printf("Bad Greeter says: %s\n", bad->greeting); return 0; }
