I want to dynamically link a shared library and assign a function from it to a std::function. Here is the code:
function.cpp:
#include <array>
#ifdef __cplusplus
extern "C" {
#endif
double function(std::array<double, 1> arg)
{
return arg[0] * 2;
}
#ifdef __cplusplus
}
#endif
main.cpp:
#include <functional>
#include <iostream>
#include <fstream>
#include <array>
#include <functional>
#ifdef __linux__
#include <dlfcn.h>
#endif
int main()
{
void *handle;
double (*function)(std::array<double, 1>);
char *error;
handle = dlopen("/home/oleg/MyProjects/shared_library_test/libFunction.so", RTLD_LAZY);
if (!handle)
{
fprintf(stderr, "%s\n", dlerror());
exit(EXIT_FAILURE);
}
dlerror();
*(void **) (&function) = dlsym(handle, "function");
if ((error = dlerror()) != NULL)
{
fprintf(stderr, "%s\n", error);
exit(EXIT_FAILURE);
}
std::cout << "Native function output: " << function(std::array<double, 1>{ 3.0 }) << std::endl;
dlclose(handle);
std::function<double(std::array<double, 1>)> std_function(*function);
std::cout << "std::function output: " << std_function(std::array<double, 1>{ 3.0 }) << std::endl;
exit(EXIT_SUCCESS);
}
Build shared library:
g++ -Wall -Wextra -g -std=c++17 -shared -o libFunction.so -fPIC function.cpp
Build main:
g++ -Wall -Wextra -g -std=c++17 main.cpp -ldl
Running the program leads to the following output:
Native function output: 6
Segmentation fault
So, as you can see, I successfully compile the library and load it in my main program. However, assigning the function pointer to std::function doesn't work.
Please, help!
You better do conversion in C++ style:
using function_ptr = double (*)(std::array<double, 1>);
function_ptr function = reinterpret_cast<function_ptr>( dlsym(handle, "function") );
But the culprit is that you cannot call this function directly or indirectly through std::function wrapper after you close shared library:
dlclose(handle);
// function cannot be used anymore
note it can be better to use RAII for this:
std::unique_ptr<void *,int(void*)> handle( dlopen("/home/oleg/MyProjects/shared_library_test/libFunction.so", RTLD_LAZY), dlclose );
then you do not need to call dlclose() manually
Note: it is a bad idea to call exit from main() in C++, use return instead, details can be found here Will exit() or an exception prevent an end-of-scope destructor from being called?
Related
I am trying to created a shared library in c++ and to initialize the library i define a function with __attribute__ ((constructor)) , but this function is not at all getting invoked.
Any idea why __attribute__ ((constructor)) is not getting called here?
lib.h file
#include <iostream>
using namespace std;
class lib {
public:
int i=0;
lib() { i=5; }
~lib() { cout << "calling lib destructor" << endl; }
static lib* getInstance();
void set() { i=i+25; }
int get() { return i; }
private:
static lib Obj;
};
lib.cpp
#include "lib.h"
lib lib::Obj;
lib* lib::getInstance()
{
return &Obj;
}
void __attribute__ ((constructor)) init_fn(void) ;
void init_fn(void)
{
lib::getInstance()->set();
}
i am creating a shared object using below command
g++ -Wall -Wextra -fPIC -shared lib.cpp -Wl,-soname,libfoo.so -o libfoo.so
Once shared library is created , i am linking it with a main program
main.cpp
#include <iostream>
#include "lib.h"
using namespace std;
int main()
{
cout << lib::getInstance()->get() << endl; // Expecting a output of 30, but always getting 5
cout << "End of program" << endl;
return 0;
}
g++ -L./ -Wall -o main main.cpp -lfoo (linking with shared object libfoo.so)
output:
./main
5
End of program
calling lib destructor
init_fn() function with constructor attribute is not at all getting invoked during loading of the shared object libfoo.so.
Tried on various c++ compilers (clang and g++), but i am not able to make it work.
Anything i am doing wrong while creating shared object? Please help me .
With the MCVE below, when I compile source classes_test.cc with
$ c++ --version
c++.exe (Rev3, Built by MSYS2 project) 10.2.0
$ c++.exe -Wall -Wunused -Wuninitialized -g -o classes_test classes_test.cc -std=c++17
under Windows 10 + Msys2 (without option -std=c++17 I get the same), I get the expected results
$ ./classes_test.exe
Using GNU G: True
Prototype of BaseClass::get_bc() is int (BaseClass::*)()
When I try the same in g++ under Ubuntu, I get compilation error
$ c++ --version
c++ (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
$ c++ -Wall -Wunused -Wuninitialized -g -o classes_test classes_test.cc -std=c++17
classes_test.cc: In function ‘int main()’:
classes_test.cc:63:83: error: invalid use of non-static member function ‘int BaseClass::get_bc()’
63 | aseClass::get_bc()" << " is " << type(BaseClass::get_bc) << endl;
| ^~~~~~
How can the difference be explained?
How can I make code work, consistently in both Linux and Msys2?
#include <iostream>
#include <cstdio>
#include <vector>
#include <string>
#include <typeinfo>
using namespace std;
struct BaseClass {
public:
explicit BaseClass() : _baseClass_int(0) {};
explicit BaseClass(const int bci) : _baseClass_int(bci) {};
~BaseClass() {};
int get_bc() { return _baseClass_int; }
void set_bc(const int bci) { _baseClass_int = bci; }
private:
int _baseClass_int;
};
#ifdef __GNUG__
#include <cstdlib>
#include <memory>
#include <cxxabi.h>
#include <string>
#define USING_GNUG ("True")
std::string demangle(const char* name) {
int status = -4; // some arbitrary value to eliminate the compiler warning
// enable c++11 by passing the flag -std=c++11 to g++
std::unique_ptr<char, void(*)(void*)> res {
abi::__cxa_demangle(name, NULL, NULL, &status),
std::free
};
return (status==0) ? res.get() : name ;
}
#else
#define USING_GNUG ("False")
// does nothing if not g++
std::string demangle(const char* name) {
return name;
}
#endif
template <class T>
std::string type(const T& t) {
return demangle(typeid(t).name());
}
int main() {
cout << "Using GNU G: " << USING_GNUG << endl;
cout << "Prototype of " << "BaseClass::get_bc()" << " is " << type(BaseClass::get_bc) << endl;
return 0;
}
The code is ill-formed, but MinGW enables some Microsoft-style extensions by default.
Compile with -fno-ms-extensions to fix that.
As per comment by Nathan Oliver, the correct argument for type is &BaseClass::get_bc.
With that, code works fine.
Plus, as mentioned by HolyBlackCat, the way to force Msys2 to report the error (instead of being permissive) is by adding flag -fno-ms-extensions to the compilation line. Note that this does not fix the problem.
I'm trying to load all the libraries, and call a function from each one, to create a file and shate the fiddle thru the pointer, and write stuff in the main program with them, the close them.
This is my main function:
#include <dlfcn.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
using namespace std;
char LIBA[]="./LIBA.SO";
char LIBB[]="./LIBB.SO";
char LIBC[]="./LIBC.SO";
typedef void (*FUNC_T)(int*);
FUNC_T FUNC[2];
void ifnull(void *detect)
{
if (detect == NULL)
{
cout << "ERROR:" << dlerror() << endl;
exit(-1);
}
}
void ifnull2(FUNC_T detect)
{
if (detect == NULL)
{
cout << "ERROR:" << dlerror() << endl;
exit(-1);
}
}
int main()
{
void *handle[2];
int FD[2];
handle[0]=dlopen(LIBA, RTLD_LAZY); ifnull(handle[0]);
handle[1]=dlopen(LIBB, RTLD_NOW); ifnull(handle[1]);
handle[2]=dlopen(LIBC, RTLD_NOW); ifnull(handle[2]);
FUNC[0]=(void(*)(int*))dlsym(handle[0], "c"); ifnull2(FUNC[0]);
FUNC[1]=(void(*)(int*))dlsym(handle[1], "c"); ifnull2(FUNC[1]);
FUNC[2]=(void(*)(int*))dlsym(handle[2], "c"); ifnull2(FUNC[2]);
FUNC[0](&FD[0]); FUNC[1](&FD[1]); FUNC[2](&FD[2]);
return 0;
}
This is inside the libs:
#include <stdio.h>
void c(int *fd)
{
printf("ok A\n");
}
I keep getting
ERROR:./LIBA.SO: undefined symbol: c
please help
C++ mungs function names because it can have multiple functions with the same name.
So, for
#include <stdio.h>
void c(int *fd)
{
printf("ok A\n");
}
we get
$ g++ -Wall -Wextra -pedantic -c -fPIC liba.cpp -o liba.so
liba.cpp: In function ‘void c(int*)’:
liba.cpp:3:13: warning: unused parameter ‘fd’ [-Wunused-parameter]
void c(int *fd)
^~
$ nm liba.so
U _GLOBAL_OFFSET_TABLE_
U puts
0000000000000000 T _Z1cPi
As you can see, the function exists as _Z1cPi. I'm not sure if that's safe to use. What you can do, however, is use extern "C".
For
#include <stdio.h>
extern "C" void c(int *fd)
{
printf("ok A\n");
}
we get
$ g++ -Wall -Wextra -pedantic -c -fPIC liba.cpp -o liba.so
liba.cpp: In function ‘void c(int*)’:
liba.cpp:3:24: warning: unused parameter ‘fd’ [-Wunused-parameter]
extern "C" void c(int *fd)
^~
$ nm liba.so
0000000000000000 T c
U _GLOBAL_OFFSET_TABLE_
U puts
I have implemented in C++ a simple module system (for demonstrative purpose only). Unfortunately the program is not working correctly (it produces not the expected output, it prints nothing out).
Here is my source code:
module.hpp
#ifndef _MODULE_HPP
#define _MODULE_HPP
#include <memory>
/// Interface for modules.
class Module {
public:
virtual void run() = 0;
virtual ~Module(){};
};
/// Symbol of the module construtor function.
const auto MODULE_CONSTRUCTOR_SYMBOL = "create_module";
/// Type of the module constructor function.
using MODULE_CONSTRUCTOR = std::unique_ptr<Module> (*)();
#endif /* _MODULE_HPP */
first.hpp
#ifndef _FIRST_HPP
#define _FIRST_HPP
#include <iostream>
#include "module.hpp"
class First : public Module
{
public:
First();
virtual ~First();
virtual void run();
std::unique_ptr<Module> create_module(void);
};
extern "C"
{
typedef std::unique_ptr<Module> create_first_module_t(void);
std::unique_ptr<Module> create_module(void);
}
#endif /* _FIRST_HPP */
first.cpp
#include "first.hpp"
First::First()
{
}
std::unique_ptr<Module> First::create_module()
{
auto ptr = std::unique_ptr<Module>{};
std::cout << "Creation of first module" << std::endl;
return ptr;
}
First::~First()
{
std::cout << "Destruction of first module" << std::endl;
}
void First::run()
{
std::cout << "Running the first module" << std::endl;
}
second.hpp
#ifndef _SECOND_HPP
#define _SECOND_HPP
#include <iostream>
#include "module.hpp"
class Second : public Module
{
public:
Second();
virtual ~Second();
virtual void run();
std::unique_ptr<Module> create_module(void);
};
extern "C"
{
typedef std::unique_ptr<Module> create_second_module_t(void);
std::unique_ptr<Module> create_module(void);
}
#endif /* _SECOND_HPP */
second.cpp
#include "second.hpp"
Second::Second()
{
}
std::unique_ptr<Module> Second::create_module()
{
auto ptr = std::unique_ptr<Module>{};
std::cout << "Creation of second module" << std::endl;
return ptr;
}
Second::~Second()
{
std::cout << "Destruction of second module" << std::endl;
}
void Second::run()
{
std::cout << "Running the second module" << std::endl;
}
main.cpp
#include <memory>
#include <dlfcn.h>
#include "first.hpp"
#include "second.hpp"
int main(void)
{
if (void* firstModule = dlopen("libFirst", RTLD_NOW))
{
if (create_first_module_t* createFirstModule = (create_first_module_t*)dlsym(firstModule, "create_module"))
{
std::unique_ptr<Module> firstModule(createFirstModule());
firstModule.get()->run();
}
dlclose(firstModule);
}
if (void* secondModule = dlopen("libSecond", RTLD_NOW))
{
if (create_second_module_t* createSecondModule = (create_second_module_t*)dlsym(secondModule, "create_module"))
{
std::unique_ptr<Module> secondModule(createSecondModule());
secondModule.get()->run();
}
dlclose(secondModule);
}
return 0;
}
Here are the instructions for the g++ compiler (on a linux machine):
g++ -Wall -Wextra -shared -fPIC first.cpp -o first.so -ldl
g++ -Wall -Wextra -shared -fPIC second.cpp -o second.so -ldl
g++ -Wall -Wextra -c main.cpp
g++ -o main main.o first.so -ldl
The expected output should be the following:
$ LD_LIBRARY_PATH=. ./main ./first.so
Creation of first module
Running the first module
Destruction of first module
$ LD_LIBRARY_PATH=. ./main ./first.so ./second.so
Creation of first module
Creation of second module
Running the first module
Running the second module
Destruction of first module
Destruction of second module
Something is wrong with my implementation, but I do not know where the mistakes are.
I hope that someone can help me with my problem.
I have a small doubt in the compilation of a c++ code along with a shared library.
So I have two files main.cpp and sample.cpp.
main.cpp
#include <iostream>
using namespace std;
#include "sample.h"
myStruct obj;
void populateData() {
obj.s = "hello world";
}
myStruct giveData() {
cout << "Inside main: " << obj.s << endl;
return obj;
}
int main() {
populateData();
}
sample.h
#ifndef SAMPLE_H
#define SAMPLE_H
#include <string>
struct myStruct {
std::string s;
void populateData();
};
myStruct giveData();
#endif
sample.cpp
#include "sample.h"
#include <iostream>
#include <boost/python.hpp>
using namespace std;
void myStruct :: populateData() {
cout << giveData().s;
}
BOOST_PYTHON_MODULE(boosts) {
using namespace boost::python;
class_<myStruct>("struct")
.add_property("s", &myStruct::s)
.def("populateData", &myStruct::populateData)
;
}
I compile the program using
g++ -c -fPIC sample.cpp
g++ -c -fPIC main.cpp
g++ -shared -Wl,-soname,boosts.so -o boosts.so sample.o main.o -lpython2.7 -lboost_python
g++ -o main main.o
./main
Now, when I run the main, it populates the string inside the obj. But when I run a python script, that imports the boosts.so, the obj.s is empty.
I am guessing it is because the library boosts.so is not properly linked with the executable main.
How do I fix this?