виртуальные функции в статических библиотеках

Когда у меня есть абстрактный базовый класс foo, определенный в libX.a

class foo {
    virtual void bar() = 0;
};

... и производный класс foo_impl, определенный в libY.a

class foo_impl : public foo {
    void bar() { /* do something */ }
};

... и использовать обе библиотеки для компоновки программы -lX -lY

int main() {
    foo* my_foo = receive_a_foo_from_somewhere();
    my_foo->bar();
}

потом

  • как я могу заставить компоновщика фактически связать символ, относящийся к foo_impl::bar()?
  • objdump -t program перечисляет foo_impl::bar(), когда он правильно связан?

РЕДАКТИРОВАТЬ: чтобы уточнить, я использую Linux с gcc или clang

РЕДАКТИРОВАТЬ 2: пропустил virtual для bar()

РЕДАКТИРОВАТЬ 4: Я извиняюсь за отсутствие ясности. Вопрос должен был звучать так: «Как я могу заставить компоновщик включить foo_impl::bar() в исполняемый файл, чтобы его можно было разрешить во время выполнения?»


person idefixs    schedule 27.05.2013    source источник
comment
откуда он знает, куда его связать? это виртуальный метод, требующий динамического поиска.   -  person Elazar    schedule 28.05.2013
comment
Когда создается виртуальная таблица для foo_impl, функция-член foo_impl помечается как использованная при создании экземпляра объекта этого типа. Затем компоновщик автоматически включит его в ваше приложение.   -  person Captain Obvlious    schedule 28.05.2013
comment
РЕДАКТИРОВАТЬ 3: my_foo**-›**bar();   -  person Yam Marcovic    schedule 28.05.2013
comment
Капитан Очевидность, так что в моем случае (я не знаю, какой foo* я получаю) мне нужно где-то создать фиктивный foo_impl, чтобы убедить компилятор включить его функциональность?   -  person idefixs    schedule 28.05.2013
comment
Дело в том, что foo_impl является единственной реализацией foo, и вы хотите, чтобы все вызовы foo::bar() автоматически переводились в foo_impl::bar()? Тогда вам нужен static_cast. Но в таком случае я предлагаю вам сделать foo_impl родителем foo. Если это не так, то принудительное связывание компоновщиком символа, ссылающегося на foo_impl::bar(), вызовет ошибку, когда foo на самом деле не foo_impl.   -  person Jiaming Lu    schedule 28.05.2013
comment
foo_impl не является одной реализацией foo   -  person idefixs    schedule 28.05.2013
comment
Тогда как вы могли знать, что my_foo указывает на foo_impl и может безопасно вызывать foo_impl::bar()?   -  person Jiaming Lu    schedule 28.05.2013
comment
потому что каждый foo должен реализовать bar()   -  person idefixs    schedule 28.05.2013
comment
Каждый foo должен реализовывать bar(), но они могут реализовывать по-разному, не обязательно быть foo_impl::bar(), но также могут быть another_foo_impl::bar().   -  person Jiaming Lu    schedule 28.05.2013
comment
Разве это не может быть просто проблемой заказа библиотеки? Изменяет ли вообще указание -lX -lY -lX ошибки ссылок?   -  person aschepler    schedule 01.06.2013
comment
Кроме того, я надеюсь, что что-то, на самом деле связываемое, потенциально создает foo_impl?   -  person aschepler    schedule 01.06.2013
comment
Не обязательно. Когда, например, foo* происходит из файла, содержащего абстрактный сериализованный foo, тогда, очевидно, должен быть какой-то код десериализации для (среди прочего) foo_impl. Но пока foo_impl не используется явно, код десериализации может быть не связан...   -  person idefixs    schedule 02.06.2013
comment
foo::bar() объявлен закрытым!   -  person curiousguy    schedule 15.07.2013


Ответы (2)


Поскольку метод bar определен как виртуальный, нет необходимости в статической компоновке. Связывание будет выполняться динамически во время выполнения в зависимости от типа объекта, на который ссылается *my_foo. Независимо от того, определен ли он в libX.a или libY.a.

Ответьте на ваш вопрос, как заставить линкер связать символ, ссылающийся на foo_impl::bar()?

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

Очевидно, что реализация должна быть связана с вашим exe. Если он недоступен, он определенно потерпит неудачу. Но вы не можете заставить компоновщика связать конкретную реализацию,

person Rajesh Subramanian    schedule 28.05.2013
comment
... что должно потерпеть неудачу, потому что компилятор (фактически компоновщик) никогда не включал символы libY.a - person idefixs; 28.05.2013
comment
Ответьте на ваш вопрос, как заставить линкер связать символ, ссылающийся на foo_impl::bar()? Вы не можете заставить компоновщика ссылаться на конкретную реализацию. Связывание будет происходить на основе того, какой адрес объекта вы получили от receive_a_foo_from_somewhere(). Очевидно, что реализация должна быть связана с вашим exe. Если он недоступен, он определенно потерпит неудачу. Но вы не можете заставить компоновщика связать конкретную реализацию, - person Rajesh Subramanian; 28.05.2013
comment
Извините моя ошибка. см. РЕДАКТИРОВАТЬ 4 - person idefixs; 28.05.2013
comment
@idefixs: Тогда ваш вопрос больше связан с параметрами компилятора gcc для включения вашей статической библиотеки, верно? Я тоже считаю, что с виртуалкой ничего общего. Если вы получите опцию gcc для включения статических библиотек, ваша проблема будет решена? - person Rajesh Subramanian; 28.05.2013

Похоже, есть как минимум два способа заставить компоновщик включить foo_impl::bar() функциональность в исполняемый файл:

  1. С помощью gcc можно использовать -Wl,--whole-archive для компоновки всей статической библиотеки — несмотря ни на что.
  2. Как заметил капитан Обвлиус, когда foo_impl используется в программе, его виртуальные функции помечаются как использованные и, таким образом, включаются в исполняемый файл. Это также может быть бесполезный статический фиктивный foo_impl объект.
person idefixs    schedule 31.05.2013