error handling

Victor Shoup shoup at
Tue Dec 16 01:22:10 UTC 2014

Marc: I'm glad your still planning on working on this!

Here are some ideas which I hope are helpful.

You could have the RAII object provide an explicit "kill" method,
which is called by the destructor or explicitly by TMP_FREE,
and have the object keep track of whether "kill" has already been
invoked on it.  That should solve your first problem: in normal
execution, TMP_FREE will call "kill" before tests_end() has a
chance to complain, and during stack unwinding, the destructor will
take care of it instead.

About C++03 vs C++11:  I'd say for stuff like this, just go with C++11,
if that is significantly more convenient.

Anyway, do you think there are other exception-related issues to
worry about?  

Some other things to consider:

  * Maybe all calls to abort should be replaced (conditionally, with ifdefs)
    with throwing an exception (not just men alloc failures)

  * What exception classes should actually be thrown? (I'm still trying to
    figure this out for NTL...)

    One thing I would request: you only throw exceptions that are declared
    in standard C++ header files, because right now, I don't expose any
    GMP interfaces in NTL header files, and it would be nice to keep it that way.
    Failing that, I'd ask that you at least put GMP's exception class declarations
    in a separate header file...
    Failing that, I can probably find a work-around (and maybe dumping gmp.h
    into some NTL header files is anyway not a big deal).
    Anyway, it's something to think would be a shame to get this far,
    and then have something trivial like this mess things up...
    What I certainly don't want to do is catch, translate, and rethrow GMP exceptions
    in my interface layers: I just want to let GMP exceptions pass through my own code.

    Anyway, it would be nice if we could coordinate on this just a bit...
    My current thinking is to throw bad_alloc for memory allocation failure,
    and runtime_error (possibly subclasses thereof) for everything else...
    but I'm not sure.
  * Are there any other places in GMP that might cause exception safety problems?
    Any other type of global state, etc.?
    Note that NTL only uses a small number of mpn-level routines...I imagine that
    it suffices to just take care of the memory allocation issues for temps to make this
    code exception safe, whereas making all of GMP's higher level code exception
    safe is a more daunting task.

On Dec 15, 2014, at 7:03 PM, Marc Glisse wrote:

> On Mon, 10 Nov 2014, Marc Glisse wrote:
>> I am still planning to implement the second paragraph of this message:
> One thing that is a bit painful is that many tests look like:
>  tests_start ();
> ... use TMP_ALLOC
>  tests_end ();
> But if I move the functionality of TMP_FREE to the destructor of an object declared in TMP_DECL or TMP_MARK, it now happens after tests_end(), which complains that some memory was not released. So for many tests I need to add { before TMP_DECL and } after TMP_FREE, or split the tests to a separate function.
> mpf_get_str is the only function in the library (ignoring tests) that fails to compile if I ignore TMP_DECL and do the declaration in TMP_MARK (because of a goto). Adding { and } solves it. There may be other places where some extra {} to invoke the destructor earlier would help.
> If WANT_TMP_DEBUG, some variables are initialized by TMP_DECL instead of TMP_MARK. I don't understand why, and it complicates the code if I want to handle C++03 and not just C++11 or later, so I'll move those initializations if noone gives a reason.
> +/* Handle C++ exceptions.  */
> +#ifdef __cplusplus
> +struct gmp_tmp_salloc_t {
> +  /* May warn in pedantic mode when TMP_SDECL is empty.  */
> +  gmp_tmp_salloc_t(){ TMP_SMARK; }
> +  void*salloc(size_t n){ return TMP_SALLOC(n); }
> +  ~gmp_tmp_salloc_t(){ TMP_SFREE; }
> +};
> +struct gmp_tmp_alloc_t {
> +  TMP_DECL;
> +  gmp_tmp_alloc_t(){ TMP_MARK; }
> +  void*salloc(size_t n){ return TMP_SALLOC(n); }
> +  void*balloc(size_t n){ return TMP_BALLOC(n); }
> +  void*alloc(size_t n){ return TMP_ALLOC(n); }
> +  ~gmp_tmp_alloc_t(){ TMP_FREE; }
> +};
> +#undef TMP_SDECL
> +#undef TMP_DECL
> +#undef TMP_SMARK
> +#undef TMP_MARK
> +#undef TMP_SALLOC
> +#undef TMP_BALLOC
> +#undef TMP_ALLOC
> +#undef TMP_SFREE
> +#undef TMP_FREE
> +#define TMP_SDECL
> +#define TMP_DECL
> +#define TMP_SMARK	gmp_tmp_salloc_t gmp_tmp_alloc
> +#define TMP_MARK	gmp_tmp_alloc_t gmp_tmp_alloc
> +#define TMP_SALLOC(n)	gmp_tmp_alloc.salloc(n)
> +#define TMP_BALLOC(n)	gmp_tmp_alloc.balloc(n)
> +#define TMP_ALLOC(n)	gmp_tmp_alloc.alloc(n)
> +#define TMP_SFREE
> +#define TMP_FREE
> +#endif
> -- 
> Marc Glisse
> _______________________________________________
> gmp-discuss mailing list
> gmp-discuss at

More information about the gmp-discuss mailing list