M
M
M4gpie2020-10-19 14:37:06
C++ / C#
M4gpie, 2020-10-19 14:37:06

Is it possible to port the "external" interface of one library to another via static linking?

Good afternoon. The following problem arose: you need to create a program that can connect many different functions from dynamic libraries. The external interface of the libraries is unified: from it you can get the number and names of the functions available in the library and call one of them through the name. But this is not the point, but the fact that the libraries themselves need to be made so that the person writing the new one does not have to do anything other than overload a couple of methods of the base class. Moreover, for the convenience of the user, it is necessary to provide the ability to manually type the container of callback functions (from which the library functions are eventually called). But all of the above is not a problem and has already been implemented in a more or less workable form. The problem arose when all this goodness had to be turned into a static library. The fact, that the dynamic interface was implemented inside this static library, and the user, using virtual inheritance, needs to create a child class and overload a couple of methods in it. And then the problem began - for some reason, the functions of the basic interface of the library are not loaded when connected in the main program. They are loaded if I do not inherit from a static library, but create a dynamic library right from the main code, all functions work fine. I'm not 100% sure, but when linking a static library, the binary does not separate this code in any way, and the function names that I declared extern "C" should still be visible "from the outside". And then the problem began - for some reason, the functions of the basic interface of the library are not loaded when connected in the main program. They are loaded if I do not inherit from a static library, but create a dynamic library right from the main code, all functions work fine. I'm not 100% sure, but when linking a static library, the binary does not separate this code in any way, and the function names that I declared extern "C" should still be visible "from the outside". And then the problem began - for some reason, the functions of the basic interface of the library are not loaded when connected in the main program. They are loaded if I do not inherit from a static library, but create a dynamic library right from the main code, all functions work fine. I'm not 100% sure, but when linking a static library, the binary does not separate this code in any way, and the function names that I declared extern "C" should still be visible "from the outside".
It looks like this in general terms:
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);}


This is what the inside looks like. And here the problems begin: I designed all this as a static library so that users do not have to drag all the code from project to project. In theory, the user code should look like this:

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);
}


This is how it should work in theory. And if you solder the user part and the interface, it works without problems. But if you include the interface part as a static library with h-nickname, no external functions are found in the main program. I don't quite understand why. Perhaps there is an explanation for this in the standard, but it seems that static linking of the library does not affect this - it's just that the functions have C-names. Maybe someone faced a similar problem or knows its solution. I don't know exactly what to call this problem, perhaps "inheritance" is a very inappropriate definition for what I'm trying to do, but what else would you call migrating an external interface from one library to another using a static link?
Platforms win7, win10, ubuntu. Compiler mingw 4.9.2 32bit. IDE Qt creator - the project is built using qmake.

Answer the question

In order to leave comments, you need to log in

1 answer(s)
E
Evgeny Shatunov, 2020-10-23
@M4gpie

Judging by the description of the question, the problem is that there is no direct functional connection between the code of the main module of the assembly and the code of the static library on which the main code depends.
At the same time, the declaration inside the static library of some functions as export ones does not show any functional dependence.
As an experiment, make a volatile pointer in the code of the main assembly module, say, to __getCountand after building the project, you will suddenly see the function in the export table.
The thing is that the complete linkage (linkage) of the code is performed only for the base module of the assembly, i.e. for the plugin itself. All static libraries that your base module depends on are sorted by their dependencies and linked with strict filtering.
This means that if the already linked code does not refer to some function from the static library, that function will not be linked. Sorting libraries by dependencies is required in order to just allow you to link all the necessary code in one pass through the library queue.
There are several ways to bypass the library link rule.
The first and easiest is to force the linker to process the entire library. This is done through the library view as Whole Archive[ ? ].
However, in this case, the entire static library will be linked into executable code. This is not always convenient. Especially if the library contains a lot of debug code.
The second option is to use __attribute__((used))[ ? ] orin a new way.
In Visual C++ code, you can use #pragma comment(linker: "/include:")[ ? ].
These constructs mark the function as important for linking, causing the function to always be linked from the static library to the binary.
The third option is to use the -u[ ? ] linker.
For Visual C++, this option would be /INCLUDE[ ? ].
Specifying important functions for forced linking directly in the build script is the most transparent and convenient method in terms of further code support.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question