Вызов библиотечной функции с изюминкой — изменение некоторых ее свойств

Я пытаюсь встроить библиотеку октав в более крупную программу. Требуется, чтобы я мог запускать и останавливать октавный интерпретатор по желанию из этой большой программы. Однако единственная функция, которая корректно останавливает интерпретатор октавы, также вызывает функцию exit(), которая также убивает большую программу. Библиотечная функция октавы — clean_up_and_exit().

В идеале я хотел бы вызвать только часть очистки (do_octave_atexit) и пропустить вызов для выхода.

Я пробовал следующее:

1) вызов do_octave_atexit, однако символ не экспортируется в библиотеку октавы. Я пытался получить к нему доступ в любом случае, но безрезультатно.

2) Я попытался подключиться к вызову выхода и заменить его функцией, которая не завершается, через ld_preload. Это все испортило, так как все остальные вызовы выхода также были перехвачены.

3) Я пытался определить, когда выход вызывается октавой, только для того, чтобы предотвратить это только тогда, обнаружив вызывающую функцию с вызовом обратной трассировки. Это по какой-то причине не показало, что я ожидаю от истинной иерархии вызовов. По какой-то причине он показал только основную функцию, а не содержимое иерархии вызовов через библиотеку октавы. Итак, не удалось обнаружить вызов, исходящий от октавы.

Код, который я использую для вызова функций октавы, выглядит так:

//
// Octave Setup Functions
//
extern "C" void oct_init (const char * path) {
  string_vector argv (2);
  argv(0) = "embedded";
  argv(1) = "-q";

  octave_main (2, argv.c_str_vec (), 1);
  if(strlen(path) > 1) {
    oct_addpath(path);
  }
}

extern "C" void oct_exit (void) {
  printf("Exiting!");
  clean_up_and_exit (1,1);
}

Ключевой функцией здесь является clean_up_and_exit, которая реализована в исходном коде октавы как:

void clean_up_and_exit (int retval, bool safe_to_return)
{
  do_octave_atexit ();

  if (octave_link::exit (retval))
  {
    if (safe_to_return)
      return;
    else
     {
       gnulib::sleep (86400);
     }
 }
 else
 {
   if (octave_exit)
     (*octave_exit) (retval);
 }
}

Таким образом, приведенный выше код вызывает функцию, которую я хочу (do_octave_atexit), но затем переходит к вызову *octave_exit, который является указателем функции на exit().

В идеале я хочу либо а) предотвратить этот вызов exit(), либо б) перехватить вызов, когда он исходит только из октавы, и предотвратить его, и разрешить его, когда он исходит из других источников. Я не смог сделать а) или б) до сих пор!

Так что на данный момент у меня нет идей. Я мог бы перекомпилировать октаву, но это решение должно работать со стандартной установкой октавы.

Это должно было работать только в среде linux/gcc.

Любые и все предложения по этому очень сложному вопросу с благодарностью.


person John McGrath    schedule 01.04.2015    source источник
comment
Не могли бы вы показать свой пример кода, тот, в котором вы запускаете интерпретатор октавы, выходите и заставляете его также выйти из вашей собственной программы?   -  person carandraug    schedule 02.04.2015
comment
Как насчет того, чтобы запустить octave как отдельный процесс и общаться с ним через канал?   -  person fukanchik    schedule 03.04.2015
comment
@fukanchik Звучит очень интересно как альтернатива. У вас есть примеры того, как это можно сделать из C? Можно ли открыть канал как «дескриптор», который сохраняется между несколькими вызовами, пока он не будет закрыт?   -  person John McGrath    schedule 03.04.2015
comment
@carandraug Спасибо за ответ. Я добавил встроенный код в исходный вопрос. Я вызываю функцию oct_exit, которая вызывает clean_up_and_exit, а затем вызывает stdlib exit() через указатель на функцию, и моя программа завершает работу.   -  person John McGrath    schedule 07.04.2015


Ответы (1)


Вам придется раскошелиться и запустить Octave в отдельном процессе. Вот простой пример того, как это сделать:

#include <unistd.h>
#include <iostream>

#include <octave/oct.h>
#include <octave/octave.h>
#include <octave/parse.h>
#include <octave/toplev.h>

int
main_octave (void)
{
  string_vector argv (2);
  argv(0) = "embedded";
  argv(1) = "-q";

  octave_main (2, argv.c_str_vec (), 1);

  octave_value_list out = feval ("pi", octave_value_list (0), 1);
  if (! error_state && out.length () > 0)
    std::cout << "pi is " << out(0).double_value () << std::endl;
  else
    std::cout << "invalid\n";

  clean_up_and_exit (0);
}

int
main (void)
{
  pid_t pid = fork();
  if (pid == 0)
    main_octave ();
  else if (pid > 0)
    {
      std::cout << "Parent process going for a nap" << std::endl;
      sleep (5);
    }
  else
    {
      std::cout << "Unable to fork()" << std::endl;
      return 1;
    }
  std::cout << "Leaving standalone application" << std::endl;
  return 0;
}

Что в моей системе возвращает:

$ mkoctfile --link-stand-alone embedded.cc -o embedded
$ ./embedded
Parent process going for a nap
pi is 3.14159
Leaving standalone application

Таким образом, вы можете продолжить работу приложения после выхода из процесса Octave. Конечно, если вы хотите запускать и останавливать Octave несколько раз, вам придется выполнять форк несколько раз. Кроме того, я бы порекомендовал вам задавать такие вопросы в списке рассылки помощи Octave, там вы, скорее всего, быстрее получите полезные ответы.

person carandraug    schedule 07.04.2015
comment
Спасибо за это. Я отправил этот вопрос в список рассылки Octave-General, хотя, к сожалению, он не получил там большого (никакого!) отклика. Одна вещь, которую я не понимаю с методом fork, - это как повторно обращаться к дочернему процессу из родительского процесса. Отдельные функции запуска и выхода из оригинала были потому, что родитель запускает октаву, затем выполняет N вызовов для feval (которые не определены заранее во время компиляции) и затем выходит из октавы. Этот процесс может повторяться много раз. Итак, ваш метод будет работать, но я думаю, что мне понадобится «дескриптор» для связи с дочерним процессом? - person John McGrath; 08.04.2015