Добрый день. Возникла следующая проблема: нужно создать программу, способную подключать множество разных функций из динамических библиотек. Внешний интерфейс библиотек унифицирован: из него можно получить кол-во и имена доступных в библиотеке функций и вызвать одну из них через имя. Но дело не в этом, а в том, что сами библиотеки нужно сделать так, чтобы человеку, пишущему новую, не нужно было делать ничего кроме как перегрузить пару методов базового класса. Более того, для удобства пользователя нужно предоставить возможность вручную типизировать контейнер функций обратного вызова (из которых в итоге и вызываются библиотечные функции). Но всё вышеперечисленное не проблема и уже реализовано в более-менее работоспособном виде. Проблема возникла, когда всё это добро нужно было превратить в статическую библиотеку. Дело в том, что динамический интерфейс был реализован внутри этой статической библиотеки, а пользователю с помощью виртуального наследования нужно создать класс-потомок и перегрузить в нём пару методов. И тут началась проблема - почему-то функции базового интерфейса библиотеки не загружаются при подключении в главной программе. Они загружаются, если я не наследую из статической библиотеки, а создаю динамическую библиотеку сразу из основного кода, все функции работают нормально. Я не уверен на 100%, но при линковке статической библиотеки бинарник никак не делит этот код, и имена функций, которые я объявил extern "C" должны быть по прежнему видны "извне".
Выглядит это в общих чертах так:
abstract_part.h
//Я максимально урезал функционал, чтоб не отвлекать от главного вопроса, но описать, для чего каждый класс нужен.
//Общий абстрактный интерфейс (не интерфейс библиотеки)
class Basic_Interface
{
public:
Basic_Interface();
virtual ~basic_interface() = 0;
virtual int function_count() = 0; // возвращает кол-во функций
virtual char** call_function(char * name, int num, char ** variables) = 0; //вызывает функцию по имени
}
//Шаблон-с-натяжкой-контейнер, хранящий информацию по функции
template<typename Ftype>
class function_container
{
private:
std::map<std::string,Ftype> container;
public:
function_container(){}
Ftype getFunction(char * name)
{
std::string fname(name);
auto iter = container.find(fname);
return *iter;
}
int getCount()
{
return container.size();
}
}
//Общий шаблон управления, реализует некоторые промежуточные функции.
//От него пользователь должен наследовать для создания собственной библиотеки.
template<typename Ftype>
class Basic_Manager: public Basic_Interface
{
private:
function_container<Ftype> container;
public:
Basic_Manager():Basic_interface(){}
int function_count()
{
return container.getSize();
}
virtual char** call_function(char * name, int num, char ** variables) = 0; //По прежнему реализуется конечным пользователем
}
global_interface.h
//Здесь уже интерфейс самой библиотеки, с которым и проблема
#include "abstract_part.h"
struct GLB
{
Basic_Interface * target; // Инициализируют в .cpp
}
//Те самые функции, которые нужно подключить в итоге
extern "C" int __getCount()
{return target->function_count();}
extern "C" char ** __callFunction(char * name, int count, char ** variables)
{return target->call_function(name,count,variables);}
Вот так примерно выглядит внутренняя часть. И вот тут начинаются проблемы: всё это я оформил как статическую библиотеку, чтобы пользователям не приходилось таскать весь код из проекта в проект. По идее пользовательский код должен выглядеть так:
user_library.h
#include "global_interface.h"
typedef char ** (*cb_foo)(int,char**);
class UserManager:public Basic_Manager<cb_foo>
{
public:
UserManager():BasicManager(){}
char ** call_function(char * name, int count, char ** variables);
}
user_library.cpp
#include "user_library.h"
UserManager manager;
GLB.target = &manager;
UserManager():call_function(char * name, int count, char ** variables)
{
cb_foo func = container.getFunction(name);
return (*func)(count,variables);
}
Вот так оно должно работать по идее. И если спаять пользовательскую и часть с интерфейсом, оно работает без проблем. Но если подключать интерфейсную часть как статическую библиотеку с h-ником, в основной программе экстерн-функции не обнаруживаются. Я не совсем понимаю, почему так. Возможно этому есть объяснение в стандарте, но вроде статичная линковка библиотеки не влияет на подобное - просто у функций остаются С-имена. Может быть кто-нибудь сталкивался с подобной проблемой или знает её решение. Я не знаю, как точно назвать эту проблему, возможно "наследование" крайне неподходящее определение для того, что я пытаюсь сделать, но как ещё назвать перенесение внешнего интерфейса из одной библиотеки в другую с помощью статической связки?
Платформы win7, win10, ubuntu. Компилятор mingw 4.9.2 32bit. IDE Qt creator - проект собирается с помощью qmake.