Есть ли способ заменить функцию в библиотеке? - PullRequest
4 голосов
/ 23 ноября 2010

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

double ScriptClass::Divide(double&, double&);

К сожалению, это даже не функция C. Можно ли как-нибудь заставить мое приложение использовать собственную функцию Divide вместо функции ScriptClass::Divide?

EDIT: Я знал о dlopen(NULL,..) и о замене функций "C" пользовательскими. Можно ли это сделать для функций-членов класса (не прибегая к искаженным именам)?

Ответы [ 5 ]

3 голосов
/ 23 ноября 2010

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

Все это, как говорится, при условии, что, поскольку она выглядит как функция C ++, у вас есть библиотека C ++, скомпилированная со всеми теми же опциями, которые вы используете для сборкиваше приложение, так что сопоставление имен совпадает, вы можете переопределить функцию в .so и использовать LD_PRELOAD для принудительной загрузки.Если вы создаете статическую ссылку, я думаю , что вы можете создать функцию в своем собственном файле .o и связать ее до самой библиотеки, чтобы компоновщик забрал вашу версию.

3 голосов
/ 23 ноября 2010

LD_PRELOAD - ваш друг. В качестве примера см .:

https://web.archive.org/web/20090130063728/http://ibm.com/developerworks/linux/library/l-glibc.html

1 голос
/ 23 ноября 2010

Различные реализации линкеров и динамических линкеров обеспечат нечто похожее на решение этой проблемы, как уже упоминали другие.

Однако, если вы переопределяете одну функцию C ++, используя любую из этих функций (GNU ld's --wrap, ld.so's LD_PRELOAD и т. Д.), вы нарушаете правило одного определения и, таким образом, вызываете неопределенное поведение.

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

Рассмотрим следующий код:

class A
{
public:
    void foo();
    void bar();
};

void A::foo()
{
    std::cout << "Old version.\n";
}

void A::bar()
{
   foo();
}

GCC 4.5, когда вызывается с -O3, фактически решит встроить определение foo () в bar (). Если вы каким-то образом заставили свой компоновщик заменить это определение A :: foo () на ваше собственное определение, A :: bar () все еще выведет строку "Старая версия. \ N".

Итак, одним словом: не надо.

0 голосов
/ 23 ноября 2010

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

(или даже просто обернуть функцию макросом)

0 голосов
/ 23 ноября 2010

Я не думаю, что вам удастся уйти от искаженных имен, но вы можете использовать опцию ld --wrap, чтобы конкретная функция получала новое имя на основе своего старого имени.Затем вы можете написать новую версию и переслать старую версию, если хотите.

Краткий обзор здесь:

http://linux.die.net/man/1/ld

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

...