Передача указателей на функции как интерфейс API в скомпилированную библиотеку

Дорогой стек обмена,

Я программирую МРТ сканер. Я не буду вдаваться в подробности, но я довольно ограничен в объеме кода, к которому у меня есть доступ, и в том, как все было настроено … неоптимально. У меня такая ситуация:

  • Есть большая библиотека, написанная на C ++. В конечном итоге он выполняет «транскодирование» (наихудшим из возможных способов), записывая сборку FPGA, которая делаетThThings. Он предоставляет набор функций для «пользовательского пространства», которые преобразуются (посредством сочетания макросов препроцессора и черной магии) в длинные строки из 16-битных и 32-битных слов. Способ, которым это делается, склонен к переполнению буфера и, как правило, к переполнению. *
  • Затем сборка FPGA выводится по прославленной последовательной линии связи с соответствующей электроникой, которая выполняет ее (выполняет сканирование) и возвращает данные обратно для обработки.
  • Предполагается, что программисты будут использовать функции, предоставляемые библиотекой, для выполнения своих задач в функциях C (не C ++), которые связаны со стандартной библиотекой. К сожалению, в моем случае мне нужно расширить библиотеку.
  • Существует довольно сложная цепочка препроцессорной замены и токенизации, вызова и (в общем) вещи происходят между написанием doSomething () в вашем коде и соответствующей библиотечной функцией, фактически выполняющей его. Я думаю, что до некоторой степени понял, но это в основном означает, что у меня нет реального представления о масштабах что-нибудь

Короче говоря, моя проблема:

  • В середине метода, в глубоком темном углу из многих тысяч строк кода в большом двоичном объекте, я почти не контролирую, с бог-знает-что происходит переменная область видимости, мне нужно:
    • Расширьте этот метод, чтобы взять в качестве аргумента указатель функции (на функцию пользовательского пространства), но
    • Пусть эта пользовательская функция, написанная после компиляции библиотеки, имеет доступ к переменным, которые являются локальными как для области действия метода, в котором она появляется, так и для переменных в функции (C), где она вызывается.

Это похоже на абсолютную суету управления памятью, и я подумал, что я бы попросил здесь «наилучшую практику» в этих ситуациях, так как вполне вероятно, что есть много тонких проблем, с которыми я мог бы столкнуться — и что у других может быть много соответствующей мудрости, чтобы передать. Отладка системы — это кошмар, и я не получил никакой поддержки от производителя сканера по этому вопросу.

Краткий набросок того, как я планирую действовать следующим образом:

В библиотеке .cpp:

/* In something::something() /*
/* declare a pointer to a function */
void (*fp)(int*, int, int, ...);
/* by default, the pointer points to a placeholder at compile time*/
fp = &doNothing(...);

...

/* At the appropriate time, point the pointer to the userland function, whose address is supplied as an argument to something(): /*
fp= userFuncPtr;
/* Declare memory for the user function to plonk data into */
i_arr_coefficients = (int) malloc(SOMETHING_SENSIBLE);
/* Create a pointer to that array for the userland function */
i_ptr_array=&i_arr_coefficients[0];
/* define a struct of pointers to local variables for the userland function to use*/
ptrStrct=createPtrStruct();

/* Call the user's function: */
fp(i_ptr_array,ptrStrct, ...);

CarryOnWithSomethingElse();

Смысл функции-заполнителя состоит в том, чтобы держать все как есть, если пользовательская функция не связана. Я понимаю, что это можно заменить на #DEFINE, но хитрость или глупость компилятора может привести к странному (по крайней мере, на мой взгляд, невежественному) поведению.

В функции userland у нас будет что-то вроде:

void doUsefulThings(i_ptr_array, ptrStrct, localVariableAddresses, ...) {
double a=*ptrStrct.a;
double b=*ptrStrct.b;
double c=*localVariableAddresses.c;
double d=doMaths(a, b, c);
/* I.e. do maths using all of these numbers we've got from the different sources */

storeData(i_ptr_array, d);
/* And put the results of that maths where the C++ method can see it */
}

...

something(&doUsefulThings(i_ptr_array, ptrStrct, localVariableAddresses, ...), ...);

...

Если это так же ясно, как грязь, пожалуйста, скажите мне! Большое спасибо за Вашу помощь. И, кстати, я искренне желаю, чтобы кто-нибудь сделал открытую аппаратную / исходную систему МРТ.

* Кроме того, это основное оправдание, которое производитель использует, чтобы отговорить нас от изменения большой библиотеки в первую очередь!

1

Решение

У вас есть полный доступ к коду C У вас ограниченный доступ к коду библиотеки C ++. Код на C определяет функцию «doUsefullthings». Из кода C вы вызываете функцию «Something» (класс / функция C ++) с указателем на «doUseFullThings» в качестве аргумента. Теперь управление переходит в библиотеку C ++. Здесь различные аргументы выделяются памятью и инициализируются. Затем с этими аргументами вызывается doUseFullThings. Здесь управление переходит обратно к коду C. Короче говоря, основная программа (C) вызывает библиотеку (C ++), а библиотека вызывает функцию C.

Одним из требований является то, что «пользовательская функция должна иметь доступ к локальной переменной из кода C, где она вызывается». Когда вы звоните «что-то», вы даете только адрес «doUseFullThings». Не существует параметра / аргумента «что-то», которое бы захватывало адрес локальных переменных. Таким образом, doUseFullThings не имеет доступа к этим переменным.

Оператор malloc возвращает указатель. Это не было обработано должным образом (вероятно, вы пытались дать нам обзор). Вы должны заботиться, чтобы освободить это где-нибудь.

Поскольку это смесь кода на C и C ++, трудно использовать RAII (заботиться о распределенной памяти), Perfect forwarding (избегать копирования переменных), функции Lambda (для доступа к локальным вариабелям) и т. Д. В этих условиях ваш подход кажется быть способом идти.

2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]