LINUX.ORG.RU

С++ работа с библиотеками


0

2

Всем привет.

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

module.h:
class A() {
 public:
   static A* get_instance();
 private:
   A();
   static A    *ptr;
}
module.cpp:
A* A::ptr=0;

A* A::get_instance() {
  if(ptr==0) {
     ptr = new A();
  }
  return ptr;
}
A* instance() {
  return A::get_instance();
}

Теперь код библиотеки

#include "..../module.h"

extern "C" A* instance();
A *p = instance();

module.cpp линкуется только в приложение, там создается «одиночка» и ведется с ним работа. Далее в какой то момент времени подключается библиотека. Я хочу чтобы эта библиотека использовала этого же «одиночку». Все прекрасно компилится. Но когда я запускаю программу, то в момент подключения библиотеки выдает следующую ошибку:

./application: symbol lookup error: .../libplugin.so: undefined symbol: instance

P.S. Библиотека собиралась через механизм плагинов Qt. Однако проверял тоже самое штатными средствами ОС, ситуация та же.

module.cpp линкуется только в приложение

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

roof ★★
()

При линковке исполняемого файла --export-dynamic. Под виндой не заработает. Вообще так лучше не делать.

staseg ★★★★★
()
Последнее исправление: staseg (всего исправлений: 1)
Ответ на: комментарий от roof

какая разница

Большая. Если её не будет в module.h, конпелятор не экспортирует её из сборки, и без применения специальной магии до той функции извне не достучишься. Более того, если конпелятор плюсовый, то он вдобавок и задекорирует эту функцию.

one_more_hokum ★★★
()
Ответ на: комментарий от staseg

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

roof ★★
()
Ответ на: комментарий от one_more_hokum

Хм, можно ссылку на доку, где говорится, что компилятор анализирует включаемые заголовки вообще хоть как-то? Не говоря уж о том, что импортом/экспортом заведует линковщик, который вообще не знает, что такое эти *.h"

roof ★★
()
Последнее исправление: roof (всего исправлений: 1)
Ответ на: комментарий от roof

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

staseg ★★★★★
()
Ответ на: комментарий от staseg

О как. Надо про это поподробнее почитать. Мой поеденый виндой мозг такие вещи с трудом усваивает.

roof ★★
()
Ответ на: комментарий от roof

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

energyclab
() автор топика
Ответ на: комментарий от energyclab

Откуда возьмутся две инстанции, если ни у кого, кроме либы, владеющей объектом не будет возможности его инстанцироввать? Пусть все получают ссылку на единственный инстанс из этой либы. Ну и, это... Сделай нормальный синглтон, твоя реализация сейчас не потокобезопасна.

roof ★★
()

Для начала:

module.cpp

extern "C" A* instance();

У тебя модуль на C++ и имена итоговых функций будут по правилам C++. А пытаешься ты подключить функцию по правилам Си. Так что, либо

extern A* instance();

Либо делай как люди о объяви instance в заголовочном файле.

AlexVR ★★★★★
()
Ответ на: комментарий от one_more_hokum

Тьфу, понял о чем речь только после комментов AlexVR. Я почему-то на

"C"
внимания не обратил, и думал, что ты утверждаешь, что если extern не в заголовочном файле, то это как-то на линковку повлияет. :) Вопрос закрыт.

roof ★★
()
Ответ на: комментарий от roof

Да, эта «C» не очень в глаза бросается, пропустить — как два байта об асфальт. А потом разглядывать лог ошибок линкера, и удивляться, что и где сломалось.

one_more_hokum ★★★
()
Ответ на: комментарий от roof

Откуда возьмутся две инстанции, если ни у кого, кроме либы, владеющей объектом не будет возможности его инстанцироввать?

Я сделаю либу, где есть мой инстанс, далее эту либу залинкую на стадии сборки с приложением. Точно так же залинкую эту либу на стадии сборки с плагином (он тоже либа). А вот плагин будет подгружен в приложение на стадии выполнения. Неужели системный вызов dlopen увидит, что либа (на которую указывают ссылки в плагине) в этом приложении уже загружена и заменит ссылки в плагине на эту подгруженную либу? Разве не будет создан новый инстанс либы для плагина?

sudo cast energyclab

energyclab
() автор топика
Ответ на: комментарий от roof

Всё это результат того, что вопрос использования функции частично перенесён на уровень линковки, а не оставлен на уровне компиляции.

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

AlexVR ★★★★★
()
Ответ на: комментарий от energyclab

Тогда бы точно в каждой библиотеке была бы своя реализация этой функции

Почему? Описание - это только описание. А реализация - это реализация.

AlexVR ★★★★★
()
Ответ на: комментарий от energyclab

CMakeLists.txt

cmake_minimum_required(VERSION 2.8)

project(hello_world)


add_library(module SHARED "module.cpp")
add_library(foo SHARED "foo.cpp")
target_link_libraries(foo module)
add_library(bar SHARED "bar.cpp")
target_link_libraries(bar module)

add_executable(main "main.cpp")

target_link_libraries(main module)
target_link_libraries(main foo)
target_link_libraries(main bar)

module.h

#ifndef MODULE_H
#define MODULE_H

class A {
 public:
   static A* get_instance();
 private:
   A() {};
   static A    *ptr;
};

A* instance();
#endif // MODULE_H

module.cpp

#include <iostream>

#include "module.h"
#include "foo.h"
#include "bar.h"

int main()
{
	std::cout << instance() << std::endl;
	foo::print();
	bar::print();
	return 0;
}

foo.h

#ifndef FOO_H
#define FOO_H

namespace foo {
	void print();
};

#endif // FOO_H

foo.cpp

#include <iostream>

#include "module.h"
#include "foo.h"

void foo::print() {
	std::cout << instance() << std::endl;
}

bar.h

#ifndef BAR_H
#define BAR_H

namespace bar {
	void print();
};

#endif // BAR_H

bar.cpp

#include <iostream>

#include "module.h"
#include "bar.h"

void bar::print() {
	std::cout << instance() << std::endl;
}

main.cpp

#include <iostream>

#include "module.h"
#include "foo.h"
#include "bar.h"

int main()
{
	std::cout << instance() << std::endl;
	foo::print();
	bar::print();
	return 0;
}

Win (MinGW):

>main
0x6d4b58
0x6d4b58
0x6d4b58

Lin:

> ./main
0x603010
0x603010
0x603010

AlexVR ★★★★★
()
Ответ на: комментарий от roof

Да, проблема решена, всем спасибо за участие.

Решение: Поместить в отдельную либу все что необходимо всем плагинам и самому приложению. И приложение и плагины линковать с либой на стадии сборки. В дальнейшем, при подгрузке плагинов на всех будет один инстанс либы

sudo cast energyclab

energyclab
() автор топика
Ответ на: комментарий от slackwarrior

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

staseg ★★★★★
()
Ответ на: комментарий от staseg

Посмотри как это делают вариации на тему rundll :) Он вполне себе умеет дергать вызовы из некоторых экзеков. Работающий рецепт гуглица раз два, но вообще было где-то у Рихтера или еще кого... Кто там в SysInternals работал. В онтопике, емнип, такое тоже не поощряется, но если очень хочется - делается(обычный совет - «перепроектирование» всего подряд, канает не всегда).

slackwarrior ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.