избегать прерывания в libgmp - PullRequest
6 голосов
/ 24 августа 2010

У меня есть код, который использует libgmp.В какой-то момент пользователь может запросить факториал очень большого числа.К сожалению, это приводит к тому, что libgmp выдает сигнал прерывания.

Например, следующий код:

#include <cmath>
#include <gmp.h>
#include <iostream>

int main() {

    mpz_t result;
    mpz_init(result);

    mpz_fac_ui(result, 20922789888000);

    std::cout << mpz_get_si(result) << std::endl;
}

Результат:Действительно большой.Есть ли в любом случае обработать ошибку более изящно, чем прерывание.Это приложение на основе графического интерфейса пользователя, и его прерывание является наименее желательным способом решения такого рода проблем.

Ответы [ 3 ]

4 голосов
/ 25 августа 2010

Может показаться, что вам не повезло, исходя из кода в mpz / realloc.c и mpz / realloc2.c .Если запрошено слишком много памяти, он просто делает это:

if (UNLIKELY (new_alloc > INT_MAX))
  {
    fprintf (stderr, "gmp: overflow in mpz type\n");
    abort ();
  }
3 голосов
/ 25 августа 2010

Лучший способ корректно обработать эти ошибки в вашем приложении - это, вероятно, отключить вспомогательный процесс для выполнения вычислений GMP.Если вспомогательный процесс убит SIGABRT, ваш родительский процесс может обнаружить это и сообщить об ошибке пользователю.


(Ниже приведен мой оригинальный ответ, который имеет «неопределенные результаты»"в соответствии с документацией GMP - здесь она оставлена ​​для полноты).

Вы можете обнаружить ошибку, если установите обработчик сигнала для SIGABRT, который использует longjmp():

jmp_buf abort_jb;

void abort_handler(int x)
{
    longjmp(abort_jb, 1);
}

int dofac(unsigned long n)
{
    signal(SIGABRT, abort_handler);
    if (setjmp(abort_jb))
        goto error;

    mpz_t result;
    mpz_init(result);

    mpz_fac_ui(result, 20922789888000);

    std::cout << mpz_get_si(result) << std::endl;

    signal(SIGABRT, SIG_DFL);
    return 0;

    error:
    signal(SIGABRT, SIG_DFL);
    std::cerr << "Caught SIGABRT from GMP.\n";
    return 1;
}
1 голос
/ 09 ноября 2010

Перезаписать abort() с LD_PRELOAD.

Что такое уловка LD_PRELOAD?

Редактировать: Чтобы сделать ответ более автономным, я копирую текст этого ответа здесь:

Если для LD_PRELOAD указать путь к общему объекту, этот файл будет загружен перед любой другой библиотекой (включая среду выполнения C, libc.so).Итак, чтобы запустить ls с вашей специальной реализацией malloc (), сделайте следующее:

$ LD_PRELOAD=/path/to/my/malloc.so /bin/ls

Кредиты JesperE.

...