Using GMP library in c++. I have a function that receives a pointer to a mpz_t number and sets another gmp_z number from that. I need a way to check if *_amount has been initialized before.
void f(mpz_t* _amount)
{
mpz_t amount;
mpz_init(amount);
if(!_amount){
throw std::bad_alloc();
}
mpz_set(amount, *_amount);
}
How can I check if _amount has been initialized previously?
This is bad practice in C - end of story. You don't check that _amount
has been initialized. The GMP library is highly optimized, and ensuring an mpz_t
object is correctly initialized is the only correct approach. So an mpz_t
object might look initialized, but unless mpz_init(_amount)
has been used, the fields might be uninitialized garbage.
It doesn't matter if _amount
has already been set. Look at mpz/set.c
:
void
mpz_set (mpz_ptr w, mpz_srcptr u)
{
mp_ptr wp, up;
mp_size_t usize, size;
usize = SIZ(u);
size = ABS (usize);
wp = MPZ_REALLOC (w, size);
up = PTR(u);
MPN_COPY (wp, up, size);
SIZ(w) = usize;
}
Clearly, if the fields of mpz_t
(see: gmp-h.in
) are uninitialized, they could be garbage. mpz_set
could perform realloc
on contiguous memory that has not been allocated. If you're lucky, that's a crash. If not, the program continues in an undefined state.
It might be useful to think of mpz_t
as 'C objects'. They are not valid (and cannot be used correctly) until initialized. I would also stress that when it comes to the requirements of GMP (multiple-precision arithmetic), the 'overhead' of initializing a variable (e.g., to zero) is absolutely negligible.
So let's assume _amount
has been correctly initialized:
mpz_init(_amount); /* correctly sets mpz_t and assigns (0). */
/* (maybe `_amount` isn't (0)... that's OK too) */
void f (mpz_t *_amount)
{
mpz_t amount;
mpz_init(amount);
/* unless using the GMP C++ interface, there's no definition
* for the (!) operator. However `mpz_sgn` can be used here: */
if (mpz_sgn(_amount) == 0) /* (_amount == 0 : exit condition?) */
{
mpz_clear(_amount); /* free space occupied by (_amount) */
/* NOTE: this is NOT C++ with object unwinding! (amount) will
* potentially leak resources! */
mpz_clear(amount);
throw std::bad_alloc {}; /* no leaks; deallocated storage. */
}
/* since (amount) will need to be deallocated, and we don't want
* to deal with assignment errors - we simply 'swap' contents. */
mpz_swap(_amount, amount); mpz_clear(amount);
}
I feel you are mixing paradigms here. The use of throw
in what is essentially C code forces you to manually clean up, and mpz_t
is not a C++ object that 'constructs' to a default, or frees all resources as it destructs / passes out of scope.
You might find the GMP C++ interface more appropriate for prototyping your code. mpz_class
acts as a C++ class, which initializes when instanced, and calls it's destructor at the end of its scope.
Right now you are mixing C/C++ incorrectly, and leaving yourself vulnerable to a lot of bugs, some of which may not cause an immediate crash - and these can be the worst to pin down...