C / C ++ Динамическая загрузка функций с неизвестным прототипом - PullRequest
3 голосов
/ 07 июля 2010

Я нахожусь в процессе написания своего рода системы / интерпретатора времени выполнения, и одна из вещей, которые мне нужно уметь делать, это вызывать функции c / c ++, расположенные во внешних библиотеках.

В Linux я использую функции dlfcn.h, чтобы открыть библиотеку и вызвать функцию, расположенную внутри. Проблема в том, что при использовании dlsysm() возвращаемый указатель функции должен быть приведен к соответствующему типу перед вызовом, чтобы были известны аргументы функции и возвращаемый тип, однако, если я вызываю какую-то произвольную функцию в библиотеке, тогда Я не буду знать этот прототип во время компиляции.

Итак, я спрашиваю: есть ли способ вызвать динамически загруженную функцию и передать ей аргументы и получить возвращаемое значение , не зная, что это прототип?

Пока я пришел к выводу, что сделать это нелегко, но я нашел несколько обходных путей:

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

  • Используйте встроенную asm для переноса параметров в стек и считывания возвращаемого значения. Я действительно хочу избежать этого, если это возможно!

Если у кого-то есть какие-либо идеи, тогда это будет высоко оценено.

Edit:

Теперь я нашел именно то, что искал:

http://sourceware.org/libffi/

«Переносимая библиотека интерфейса с внешними функциями»

(хотя, признаюсь, я мог бы быть более ясным в исходном вопросе!)

Ответы [ 5 ]

6 голосов
/ 07 июля 2010

То, что вы запрашиваете, - это если C / C ++ поддерживает рефлексию для функций (то есть получение информации об их типе во время выполнения). К сожалению, ответ - нет.

Вам нужно будет привести функции в соответствие со стандартным контрактом (как вы и сказали), или начать внедрять механизмы для вызова функций во время выполнения, не зная их аргументов.

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

Придерживайтесь своего плана, применяйте четко определенный контракт для функций, которые вы загружаете динамически, и, мы надеемся, сделайте это должным образом.

2 голосов
/ 07 июля 2010

Можете ли вы добавить функцию отправки во внешние библиотеки, например, тот, который принимает имя функции и N (необязательные) параметры какого-либо типа варианта и возвращает вариант? Таким образом, прототип функции диспетчеризации известен. Затем функция диспетчеризации выполняет поиск (или переключение) имени функции и вызывает соответствующую функцию.

Очевидно, что это становится проблемой обслуживания, если есть много функций.

1 голос
/ 07 июля 2010

Я считаю, что библиотека ruby ​​FFI достигает того, о чем вы просите.Он может вызывать функции во внешних динамически связанных библиотеках, не связывая их конкретно.

http://wiki.github.com/ffi/ffi/

Вы, вероятно, не можете использовать его непосредственно в своем языке сценариев, но, похоже, идеи переносимы.

- Брэд Фелан http://xtargets.heroku.com

0 голосов
/ 27 января 2014

Мое решение состоит в том, что вы можете определить generic proxy function, который преобразует динамическую функцию в единый прототип, что-то вроде этого:

#include <string>
#include <functional>

using result = std::function<std::string(std::string)>;

template <class F>
result proxy(F func) {
  // some type-traits technologies based on func type
}

В пользовательском файле вы должны добавить определение, чтобы сделатьthe convert:

double foo(double a) { /*...*/ }
auto local_foo = proxy(foo);

В вашей исполняющей системе / интерпретаторе вы можете использовать dlsym для определения foo-function.Пользовательская функция foo отвечает за выполнение вычислений.

0 голосов
/ 07 июля 2010

Я нахожусь в процессе написания своего рода системы / интерпретатора времени выполнения, и одна из вещей, которые мне нужно уметь делать, это вызывать функции c / c ++, расположенные во внешних библиотеках.

Вы, вероятно, можете проверить примеры того, как Tcl и Python делают это.Если вы знакомы с Perl, вы также можете проверить Perl XS .

Общий подход заключается в том, чтобы дополнительная библиотека шлюза располагалась между вашим интерпретатором и целевой библиотекой C.Исходя из моего опыта работы с Perl XS, основными причинами являются управление памятью / сборка мусора и типы данных C, которые трудно / невозможно отобразить непосредственно на язык интерпретатора.

Итак, я спрашиваю:, есть ли способ вызвать динамически загруженную функцию, передать ей аргументы и получить ее возвращаемое значение, не зная, что это прототип?

Мне неизвестно.

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

Это то, что в моем проекте делает и другая команда.У них есть стандартизированный API для внешних плагинов на что-то вроде этого:

typedef std::list< std::string > string_list_t;
string_list_t func1(string_list_t stdin, string_list_t &stderr);

Обычными задачами для плагинов является выполнение преобразования или сопоставления или расширения ввода, часто с использованием СУБД.

Предыдущие версии интерфейса со временем становились неосуществимыми, вызывая проблемы как у покупателей, так и у разработчиков продуктов и сторонних разработчиков плагинов.Легкомысленное использование std :: string допускается тем фактом, что плагины вызываются относительно редко (и все же накладные расходы по сравнению с SQL используются повсеместно).Аргумент stdin заполняется вводом в зависимости от типа плагина.Вызов плагина считается неудачным, если внутри выходного параметра stderr любая строка начинается с 'E:' (W: для предупреждений, остальные игнорируются, поэтому могут использоваться для разработки / отладки плагинов).

dlsym используется только один раз для функции с предопределенным именем для выборки из массива совместно используемой библиотеки с таблицей функций (публичное имя функции, тип, указатель и т. Д.).

...