I completely agree with Niel's assessment of Victor's options (1) - (4) for handling errors.
Recall the following from Torbjörn's original post:
There are real scenarios where one would want different sets of allocation functions. E.g., many libraries use GMP. Some of them have their own error reporting mechanisms, and memory handles. But then the user might use GMP directly, or she might from the same program use several libraries which use GMP. The current GMP memory allocation mechanism is not suitable here.
Correct. Single global pointer -- bothersome to share in a multi-library context, impossible to share in a multi-library + multi-threaded context.
[Torbjörn] I am not too fond of these global pointers. It would be better design to refer the memory handling and error handling functions from each GMP user variable, akin to object oriented languages' vtables. Except that this would make these small structures 3 times larger. ... That would cost a lot in cache load for applications which e.g. use arrays of GMP numbers.
I use large (potentially huge) arrays of GMP numbers, and would suffer greatly from such a design choice. This is why I entered the discussion. This is a poor resource utilization trade-off -- i.e., making these objects 3 times larger for functionality that is executed very rarely (if at all) in normal operation. It is still a poor trade-off at 25% overhead, and perhaps even at 4 bits if the main functionality suffers too much.
[Niels] I think we should also keep in mind other types of exceptions than those provided by C++, e.g., guile exceptions or objc exceptions. I'd strongly prefer if we can define an interface for error handling and clean up temporary storage which works fine if gmp is compiled as plain C, and is language agnostic, e.g., based on memory pools. But if that turns out to be too difficult, I guess it's not too unreasonable to have proper cleanup on C++ exceptions require that gmp is compiled with a C++ compiler.
I agree completely. While I do not mind compiling GMP as C++ code, I vigorously reject the recent suggestion of migrating GMP toward a "C++ only" code base. There is too much C code out there that uses GMP, mine included. Although it is relatively simple for C++ to call a C library, the reverse is indescribably ugly, difficult and non-portable.
[Niels] I'd strongly prefer if we can define an interface for error handling and clean up temporary storage which works fine if gmp is compiled as plain C, and is language agnostic...
Consider my "dynamically scoped" exception handler example -- upon further reflection, I realize that it is "possible" to implement this completely outside of GMP using only a single static function pointer that GMP invokes to report exceptions. This would even work in a multi-threaded context. [The handler record resides in the application's stack frame, and the signal raising/propagation loop resides in the application's handler function invoked via pointer from GMP.] The reason you would want to build it all into GMP is to address Torbjörn's "several libraries" requirement. If this is built into GMP, then every well-behaved library that uses GMP will choose to receive exceptions using dynamically-scoped handlers, and the "handler of last resort" can/should be set by the top-level application. This works well even in contexts that are both multi-library and multi-threaded. If you don't build it into GMP, there would be no standard for libraries and applications to adhere to and each library would still fight over the global function pointer -- and it wouldn't work at all if you add multiple threads to the mix. The cost of this mechanism (both in space and time) yields an efficient utilization of resources, especially for a mechanism that is almost never used under normal conditions. One piece missing from my example code: a GMP "longjmp cleanup" function that takes a prospective "jmp_buf *" as an argument. It would perform cleanup / popping of both the stack temporaries, and pop any intervening handler records (while possibly delivering "unwind-protect" exceptions) from the dynamic chain. Applications could call this before doing longjmp(). It would take care of the platform-dependent problem of choosing how far to traverse/pop each chain (TMP_ALLOC and exception). Details of what to pass to the handler (and how) are an independent issue. A proposal, based on Unix/Linux signal handler interface: - An integer code indicating the type of exception (#define's in gmp.h). - A pointer to the "struct gmp_exception_handler_record" whose handler is being invoked (NULL when invoking the static pointer handler). The user can embed her gmp_exception_handler_record inside of a larger structure in the stack frame. The handler can know this and use it to gain access to arbitrary local state -- even cleanup when handling unwind-protect "exceptions". - A "void *" pointer to info provided by the context raising the exception. Structure and interpretation of this data depend upon the integer code. At the very least it should describe the problem and include a pointer to the offending GMP object(s). Various flavors of structure, defined in gmp.h. The above framework could achieve Niels' stated objectives. Option (1) is of course trivial. Victor could write C wrappers around each GMP function using these mechanisms to obtain his option (2). It also allows option (4). Option (3): compile GMP as C++. But it might also be an interesting exercise to see if these mechanisms can be used to translate "raw C" GMP exceptions into corresponding C++ exceptions. This is probably already done for certain OS signals (SIGFPE, etc.). David _______________________________________________ gmp-devel mailing list gmp-devel@gmplib.org https://gmplib.org/mailman/listinfo/gmp-devel