mpq_class(0, 1) segfaults (libgmpxx)

Marc Glisse marc.glisse at inria.fr
Tue Feb 9 12:25:10 CET 2010


On Mon, 8 Feb 2010, gsauthof at techfak.uni-bielefeld.de wrote:

> I tried to initialize mpq_class with 0/1, which results in a segfault.
>
> I did this after reading the libgmpxx documentation about the mpq_class API [1] :
>
> void mpq_class::mpq_class (integer num, integer den)
>
> Construct an mpq_class. The initial value can be a single value of any
> type, or a pair of integers (mpz_class or standard C++ integer types)
> representing a fraction, except that long long and long double are not
> supported. For example,
>           mpq_class q (1, 3);
>
> Strange, a minimal example to reproduce it:
>
> $ cat ratio.cc
> #include <gmpxx.h>
>
> int main()
> {
> #ifdef CRASH_ME
>  mpq_class *r = new mpq_class(0, 1);
> #else
>  mpq_class *r = new mpq_class(mpz_class(0), mpz_class(1));
> #endif
>  return 0;
> }
>
> $ g++ -g ratio.cc  -lgmpxx -o ratio -DCRASH_ME
> $ ./ratio
> Segmentation fault (core dumped)
>
> It looks like with CRASH_ME defined following constructor is used:
>
> __gmp_expr(const char *s, int base)
>
> But if we use mpq_class(1, 3) like in the example from the documentation
> this constructor is used:
>
> __gmp_expr(const mpz_class &num, const mpz_class &den)

Indeed. The problem seems to appear only when the first argument is the 
litteral 0, which can be a pointer just as well as an integer. I can see 2 
solutions:

- document this. There doesn't appear to be much point using
mpq_class(0,1) when you can just use mpq_class(0) or even mpq_class()
(unless maybe there is some complicated macro game going on which means
you don't know the argument is 0)

- write a billion overloads of the constructor (if there is a 
mpq_class(int,int), the compiler will use it before mpq_class(const 
char*,int). (bonus: this improves performance, as we don't need to create 
a temporary mpz_class)

- use some template meta-programming to modify the overload resolution

template <typename T> struct Z{};
template <> struct Z<char*> { typedef int type; };
template <> struct Z<const char*> { typedef int type; };
struct mpz_class { mpz_class(int){} };
struct mpq_class {
 	mpq_class(mpz_class,mpz_class)
 	{ std::cout << "mpq_class(mpz_class,mpz_class);\n"; }
 	template <class U> mpq_class(U,typename Z<U>::type)
 	{ std::cout << "mpq_class(char*,int);\n"; }
};
int main(){
 	mpq_class x(0,3);
}

Some inconvenients of this last method are that some compiler may not
like it so much, it is actually a bit more complicated than what I wrote
(there are more ways to put const and volatile in char*), and it changes
the resolution rules more than just for the litteral 0, which could
cause problems for some people.

Here I used the templates to reduce the likelihood of the char* 
constructor being used, but we could also try to use it to define the many 
integer overloads in one go (once again with different rules).

In any case, thank you for this useful bug report.

-- 
Marc Glisse


More information about the gmp-bugs mailing list