Is it possible to load a shared library in ECL, import some C functions which receive and return cl_object (as if they were defined with ecl_def_c_function()) and invoke them in REPL (without compiling a *.lisp file)?
For example:
// file 'extensions.c' compiled to 'extensions.so'
cl_object
do_something
(cl_object arg)
{
cl_object
result = ...;
return result;
}
; in ECL REPL
(uffi:load-foreign-library #p".../extensions.so")
... ; importing stuff
(format t "got: ~a~%" (do-something "text"))
As I found there's no way of telling ECL (via UFFI) that C functions deal with cl_object, not a regular pointer (void*).
Looks like you can't do it as easy as in Python.
The only solution I found so far:
extlib.c
#include <stdio.h>
#include <ecl/ecl.h>
static
cl_object
make_pair
(cl_object arg)
{
return cl_cons(arg, arg);
}
void
init_extlib
(void)
{
ecl_def_c_function(
ecl_read_from_cstring("make-pair"),
make_pair,
1
);
}
Compile it:
clang `ecl-config --cflags` extlib.c -shared -fPIC -o extlib.so `ecl-config --libs`
load-extlib.lisp
(uffi:load-foreign-library "~/extlib.so")
(uffi:def-function ("init_extlib" init-extlib)
()
:returning :void)
(init-extlib)
Compile it:
ecl -compile load-extlib.lisp -o load-extlib.fas
Load & test it:
ecl -load load-extlib.fas
> (make-pair "blah")
("blah" . "blah")
Related
I am currently learning C and am trying to understand the possibilities of dynamic libraries.
My current question is, if I have a simple "Hello World" application in C called "ProgA", and this program dynamically loads a shared library with some example code called "LibB", can LibB access a global variable in ProgA, which was declared as external?
Given is the following example code for demonstration of the problem:
file header.h
#ifndef TEST_H
#define TEST_H
typedef struct test_import_s {
int some_field;
} test_import_t;
extern test_import_t newtestimport;
#endif
file prog_a.c
#include <stdio.h>
#include <windows.h>
#include "header.h"
test_import_t newtestimport = {
.some_field = 42
};
int main()
{
HINSTANCE hinstLib;
typedef void (*FunctionPointer)();
newtestimport.some_field = 42;
hinstLib = LoadLibrary("lib_b.dll");
if (hinstLib != NULL)
{
FunctionPointer initialize_lib_b;
initialize_lib_b = (FunctionPointer)GetProcAddress(hinstLib, "initialize_lib_b");
if (initialize_lib_b != NULL)
{
initialize_lib_b();
}
FreeLibrary(hinstLib);
}
return 0;
}
file lib_b.c
#include <stdio.h>
#include "header.h"
test_import_t *timp;
void initialize_lib_b() {
timp = &newtestimport;
int some_field = timp->some_field;
printf("Result from function: %d\n", some_field);
}
file CMakeLists.txt
cmake_minimum_required(VERSION 3.24)
project(dynamic-library-2 C)
set(CMAKE_C_STANDARD 23)
add_library(lib_b SHARED lib_b.c)
set_target_properties(lib_b PROPERTIES PREFIX "" OUTPUT_NAME "lib_b")
add_executable(prog_a prog_a.c)
target_link_libraries(prog_a lib_b)
In the above example, the headerfile header.h defines the struct test_import_t and an external variable newtestimport using this struct. In the C file of the main program prog_a.c one property of this struct is assigned the value 42. It then dynamically loads the library lib_b.c using the Windows API and executes a function in it. The function then should access the variable newtestimport of the main program and print out the value of the variable (42).
This example does not work. The compiler throws the following error:
====================[ Build | prog_a | Debug ]==================================
C:\Users\user1\AppData\Local\JetBrains\Toolbox\apps\CLion\ch-0\223.8617.54\bin\cmake\win\x64\bin\cmake.exe --build C:\Users\user1\projects\learning-c\cmake-build-debug --target prog_a -j 9
[1/2] Linking C shared library dynamic-library-2\lib_b.dll
FAILED: dynamic-library-2/lib_b.dll dynamic-library-2/liblib_b.dll.a
cmd.exe /C "cd . && C:\Users\user1\AppData\Local\JetBrains\Toolbox\apps\CLion\ch-0\223.8617.54\bin\mingw\bin\gcc.exe -fPIC -g -Wl,--export-all-symbols -shared -o dynamic-library-2\lib_b.dll -Wl,--out-implib,dynamic-library-2\liblib_b.dll.a -Wl,--major-image-version,0,--minor-image-version,0 dynamic-library-2/CMakeFiles/lib_b.dir/lib_b.c.obj -lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32 && cd ."
C:\Users\user1\AppData\Local\JetBrains\Toolbox\apps\CLion\ch-0\223.8617.54\bin\mingw\bin/ld.exe: dynamic-library-2/CMakeFiles/lib_b.dir/lib_b.c.obj:lib_b.c:(.rdata$.refptr.newtestimport[.refptr.newtestimport]+0x0): undefined reference to `newtestimport'
collect2.exe: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.
How can the example be fixed to accomplish the described goal?
Windows DLLs are self-contained, and can not have undefined references similar to newtestimport, unless these references are satisfied by another DLL.
How can the example be fixed to accomplish the described goal?
The best fix is to pass the address of newtestimport into the function that needs it (initialize_lib_b() here).
If for some reason you can't do that, your next best option is to define the newtestimport as a dllexport variable in another DLL, e.g. lib_c.dll.
Then both the main executable and lib_b.dll would be linked against lib_c.lib, and would both use that variable from lib_c.dll.
P.S. Global variables are a "code smell" and a significant source of bugs. You should avoid them whenever possible, and in your example there doesn't seem to be any good reason to use them.
I have dynamic library created as follows
cat myfile.cc
struct Tcl_Interp;
extern "C" int My_Init(Tcl_Interp *) { return 0; }
1) complile the cc file
g++ -fPIC -c myfile.cc
2) Creating a shared library
g++ -static-libstdc++ -static-libgcc -shared -o libmy.so myfile.o -L/tools/linux64/qt-4.6.0/lib -lQtCore -lQtGui
3) load the library from a TCL proc
then I give command
tclsh
and given command
% load libmy.so
is there any C++ function/ Qt equivalent to load that can load the shared library on demand from another C++ function.
My requirement is to load the dynamic library on run time inside the function and then use the qt functions directly
1) load the qt shared libraries (for lib1.so)
2) call directly the functions without any call for resolve
For example we have dopen, but for that for each function call we have to call dsym. My requirement is only call for shared library then directly call those functions.
You want boilerplate-less delay loading. On Windows, MSVC implements delay loading by emitting a stub that resolves the function through a function pointer. You can do the same. First, let's observe that function pointers and functions are interchangeable if all you do is call them. The syntax for invoking a function or a function pointer is the same:
void foo_impl() {}
void (*foo)() = foo_impl;
int main() {
foo_impl();
foo();
}
The idea is to set the function pointer initially to a thunk that will resolve the real function at runtime:
extern void (*foo)();
void foo_thunk() {
foo = QLibrary::resolve("libmylib", "foo");
if (!foo) abort();
return foo();
}
void (*foo)() = foo_thunk;
int main() {
foo(); // calls foo_thunk to resolve foo and calls foo from libmylib
foo(); // calls foo from libmylib
}
When you first call foo, it will really call foo_thunk, resolve the function address, and call real foo implementation.
To do this, you can split the library into two libraries:
The library implementation. It is unaware of demand-loading.
A demand-load stub.
The executable will link to the demand-load stub library; that is either static or dynamic. The demand-load stub will automatically resolve the symbols at runtime and call into the implementation.
If you're clever, you can design the header for the implementation such that the header itself can be used to generate all the stubs without having to enter their details twice.
Complete Example
Everything follows, it's also available from https://github.com/KubaO/stackoverflown/tree/master/questions/demand-load-39291032
The top-level project consists of:
lib1 - the dynamic library
lib1_demand - the static demand-load thunk for lib1
main - the application that uses lib1_demand
demand-load-39291032.pro
TEMPLATE = subdirs
SUBDIRS = lib1 lib1_demand main
main.depends = lib1_demand
lib1_demand.depends = lib1
We can factor out the cleverness into a separate header. This header allows us to define the library interface so that the thunks can be automatically generated.
The heavy use of preprocessor and a somewhat redundant syntax is needed due to limitations of C. If you wanted to implement this for C++ only, there'd be no need to repeat the argument list.
demand_load.h
// Configuration macros:
// DEMAND_NAME - must be set to a unique identifier of the library
// DEMAND_LOAD - if defined, the functions are declared as function pointers, **or**
// DEMAND_BUILD - if defined, the thunks and function pointers are defined
#if defined(DEMAND_FUN)
#error Multiple inclusion of demand_load.h without undefining DEMAND_FUN first.
#endif
#if !defined(DEMAND_NAME)
#error DEMAND_NAME must be defined
#endif
#if defined(DEMAND_LOAD)
// Interface via a function pointer
#define DEMAND_FUN(ret,name,args,arg_call) \
extern ret (*name)args;
#elif defined(DEMAND_BUILD)
// Implementation of the demand loader stub
#ifndef DEMAND_CAT
#define DEMAND_CAT_(x,y) x##y
#define DEMAND_CAT(x,y) DEMAND_CAT_(x,y)
#endif
void (* DEMAND_CAT(resolve_,DEMAND_NAME)(const char *))();
#if defined(__cplusplus)
#define DEMAND_FUN(ret,name,args,arg_call) \
extern ret (*name)args; \
ret name##_thunk args { \
name = reinterpret_cast<decltype(name)>(DEMAND_CAT(resolve_,DEMAND_NAME)(#name)); \
return name arg_call; \
}\
ret (*name)args = name##_thunk;
#else
#define DEMAND_FUN(ret,name,args,arg_call) \
extern ret (*name)args; \
ret name##_impl args { \
name = (void*)DEMAND_CAT(resolve_,DEMAND_NAME)(#name); \
name arg_call; \
}\
ret (*name)args = name##_impl;
#endif // __cplusplus
#else
// Interface via a function
#define DEMAND_FUN(ret,name,args,arg_call) \
ret name args;
#endif
Then, the dynamic library itself:
lib1/lib1.pro
TEMPLATE = lib
SOURCES = lib1.c
HEADERS = lib1.h
INCLUDEPATH += ..
DEPENDPATH += ..
Instead of declaring the functions directly, we'll use DEMAND_FUN from demand_load.h. If DEMAND_LOAD_LIB1 is defined when the header is included, it will offer a demand-load interface to the library. If DEMAND_BUILD is defined, it'll define the demand-load thunks. If neither is defined, it will offer a normal interface.
We take care to undefine the implementation-specific macros so that the global namespace is not polluted. We can then include multiple libraries the project, each one individually selectable between demand- and non-demand loading.
lib1/lib1.h
#ifndef LIB_H
#define LIB_H
#ifdef __cplusplus
extern "C" {
#endif
#define DEMAND_NAME LIB1
#ifdef DEMAND_LOAD_LIB1
#define DEMAND_LOAD
#endif
#include "demand_load.h"
#undef DEMAND_LOAD
DEMAND_FUN(int, My_Add, (int i, int j), (i,j))
DEMAND_FUN(int, My_Subtract, (int i, int j), (i,j))
#undef DEMAND_FUN
#undef DEMAND_NAME
#ifdef __cplusplus
}
#endif
#endif
The implementation is uncontroversial:
lib1/lib1.c
#include "lib1.h"
int My_Add(int i, int j) {
return i+j;
}
int My_Subtract(int i, int j) {
return i-j;
}
For the user of such a library, demand loading is reduced to defining one macro and using the thunk library lib1_demand instead of the dynamic library lib1.
main/main.pro
if (true) {
# Use demand-loaded lib1
DEFINES += DEMAND_LOAD_LIB1
LIBS += -L../lib1_demand -llib1_demand
} else {
# Use direct-loaded lib1
LIBS += -L../lib1 -llib1
}
QT = core
CONFIG += console c++11
CONFIG -= app_bundle
TARGET = demand-load-39291032
TEMPLATE = app
INCLUDEPATH += ..
DEPENDPATH += ..
SOURCES = main.cpp
main/main.cpp
#include "lib1/lib1.h"
#include <QtCore>
int main() {
auto a = My_Add(1, 2);
Q_ASSERT(a == 3);
auto b = My_Add(3, 4);
Q_ASSERT(b == 7);
auto c = My_Subtract(5, 7);
Q_ASSERT(c == -2);
}
Finally, the implementation of the thunk. Here we have a choice between using dlopen+dlsym or QLibrary. For simplicity, I opted for the latter:
lib1_demand/lib1_demand.pro
QT = core
TEMPLATE = lib
CONFIG += staticlib
INCLUDEPATH += ..
DEPENDPATH += ..
SOURCES = lib1_demand.cpp
HEADERS = ../demand_load.h
lib1_demand/lib1_demand.cpp
#define DEMAND_BUILD
#include "lib1/lib1.h"
#include <QLibrary>
void (* resolve_LIB1(const char * name))() {
auto f = QLibrary::resolve("../lib1/liblib1", name);
return f;
}
Quite apart from the process of loading a library into your C++ code (which Kuber Ober's answer covers just fine) the code that you are loading is wrong; even if you manage to load it, your code will crash! This is because you have a variable of type Tcl_Interp at file scope; that's wrong use of the Tcl library. Instead, the library provides only one way to obtain a handle to an interpreter context, Tcl_CreateInterp() (and a few other functions that are wrappers round it), and that returns a Tcl_Interp* that has already been initialised correctly. (Strictly, it actually returns a handle to what is effectively an internal subclass of Tcl_Interp, so you really can't usefully allocate one yourself.)
The correct usage of the library is this:
Tcl_FindExecutable(NULL); // Or argv[0] if you have it
Tcl_Interp *interp = Tcl_CreateInterp();
// And now, you can use the rest of the API as you see fit
That's for putting a Tcl interpreter inside your code. To do it the other way round, you create an int My_Init(Tcl_Interp*) function as you describe and it is used to tell you where the interpreter is, but then you wouldn't be asking how to load the code, as Tcl has reasonable support for that already.
I've stayed up all night trying to figure this out (it's now 7am where I'm at...).
I'm having trouble setting the address of an instantiated object to a pointer. Here's the main function:
#include "position_vector.h"
int main(){
PositionVector res = PositionVector(10);
PositionVector * ptr;
ptr = &res; // <--- WHERE IT BREAKS
}
A stripped down version of the h file "position_vector.h":
#include<iostream>
typedef uint32_t word_t;
class PositionVector {
public:
word_t * vec;
/*some other member variables */
PositionVector();
PositionVector(size_t len);
PositionVector & operator & ();
PositionVector & operator !();
~PositionVector();
/*some other member functions*/
void resize(size_t len);
};
I have another cpp file that defines all the methods in the class.
This is part of some larger set of code but here's the compile that fails:
g++-4.9 -std=c++11 -Werror -Wall -Wextra -g -Isrc -ggdb -c -o bin/main.o src/main.cpp
It fails with the error:
g++-4.9 -std=c++11 -Werror -Wall -Wextra -g -Isrc -ggdb -c -o bin/main.o src/main.cpp
src/main.cpp: In function ‘int main()’:
src/main.cpp:27:9: error: cannot convert ‘PositionVector’ to ‘PositionVector*’ in assignment
ptr = &res;
^
I must be missing something super basic but I've just pulled an all nighter and I have to run to work... so I cant really think full well any more.
You've overloaded operator& in your class:
class PositionVector {
// ...
PositionVector & operator & ();
};
When you than try to take the address, the compiler calls your overloaded member function which returns a PositionVector&. This cannot be assigned to a PositionVector* and thus you get the error.
I am trying to make a little IDE to compile c files. I am running under Ubuntu 14.04 and QtCreator 5.4.0. I wrote this code.
void MainWindow::on_pushButton_clicked()
{
QFile file("hello.c");
file.open(QIODevice::WriteOnly);
QTextStream out(&file);
out << ui->plainTextEdit->toPlainText();
QProcess::execute("gcc hello.c -o hello");
}
Everything is OK until run the gcc command. When I click on the button I get this error:
In function `_start':
(.text+0x20): undefined reference to `main'
collect2: error: ld returned 1 exit status
and my C code is here:
#include <stdio.h>
int main () {
printf("Hello World..");
return 0;
}
You haven't flushed the QTextStream buffer, so hello.c is empty when you call gcc on it. Use QTextStream 's flush() to flush the buffer, it also calls flush on the underlying device (the QFile here).
I'm trying to open multiple files simultaneously (random number of files) and store their textstreams in qlist for simple using in other code:
QList<QTextStream> files;
QList<QString> fnames;
fnames.append("file1.txt");
fnames.append("file2.txt");
// ..and so on with random iterations
// collect qtextsrams into qlist
foreach (QString file, fnames) {
QFile f(file);
f.open(QIODevice::ReadOnly);
QTextStream textStream(&f);
files2.append(&textStream);
}
// use qtextstreams in a loop
QList<QTextStream>::iterator i;
for (i = files.begin(); i != files.end(); ++i) {
qDebug() << i->readLine();
}
So i have an error:
/make debug
make -f Makefile.Debug
make[1]: Entering directory `/home/pixx/Workspace/collocs'
g++ -c -pipe -g -Wall -W -D_REENTRANT -DQT_GUI_LIB -DQT_CORE_LIB -DQT_SHARED -I/usr/share/qt4/mkspecs/linux-g++ -I. -I/usr/include/qt4/QtCore -I/usr/include/qt4/QtGui -I/usr/include/qt4 -Idebug -o debug/main.o main.cpp
main.cpp: In function ‘int main(int, char**)’:
main.cpp:128: error: no matching function for call to ‘QList<QTextStream>::append(QTextStream*)’
/usr/include/qt4/QtCore/qlist.h:493: note: candidates are: void QList<T>::append(const T&) [with T = QTextStream]
/usr/include/qt4/QtCore/qlist.h:819: note: void QList<T>::append(const QList<T>&) [with T = QTextStream]
main.cpp:117: warning: unused variable ‘cc’
In file included from /usr/include/qt4/QtCore/QList:1,
from main.cpp:1:
/usr/include/qt4/QtCore/qtextstream.h: In member function ‘void QList<T>::node_copy(QList<T>::Node*, QList<T>::Node*, QList<T>::Node*) [with T = QTextStream]’:
/usr/include/qt4/QtCore/qlist.h:695: instantiated from ‘void QList<T>::detach_helper(int) [with T = QTextStream]’
/usr/include/qt4/QtCore/qlist.h:709: instantiated from ‘void QList<T>::detach_helper() [with T = QTextStream]’
/usr/include/qt4/QtCore/qlist.h:126: instantiated from ‘void QList<T>::detach() [with T = QTextStream]’
/usr/include/qt4/QtCore/qlist.h:254: instantiated from ‘QList<T>::iterator QList<T>::begin() [with T = QTextStream]’
main.cpp:133: instantiated from here
/usr/include/qt4/QtCore/qtextstream.h:258: error: ‘QTextStream::QTextStream(const QTextStream&)’ is private
/usr/include/qt4/QtCore/qlist.h:386: error: within this context
/usr/include/qt4/QtCore/qtextstream.h:258: error: ‘QTextStream::QTextStream(const QTextStream&)’ is private
/usr/include/qt4/QtCore/qlist.h:399: error: within this context
make[1]: Leaving directory `/home/pixx/Workspace/collocs'
make[1]: *** [debug/main.o] Error 1
make: *** [debug] Error 2
What should i fix?
I understand that it's very simple question, but i can't find right query for google :(
The first error, namely "no matching function for call to ‘QList::append(QTextStream*)’" is caused by you using & operator in this line:
files2.append(&textStream);
Your list is supposed to be made of QTextStream objects, not pointers to QTextStream objects.
But the real problem lies deeper. To put an object into a list, an object must have copy constructor. QTextStream doesn't have any, since it's not clear how different copies of a same text stream should work together. I suggest you create a list of pointers to text streams, as in "QList ". Of course, in that case don't forget to handle their destruction, when they are no longer needed:
foreach (QTextStream *cur, files) delete cur;
If you need to pass this list between different parts of your code, make multiple copies of it and such, you may need smart pointers (QSharedPointer), but I can hardly think of a task where you'd need to do it to text streams.
I found a solution, thanks Septagram for idea! QList files2; // file list to merge
QList<QTextStream> files;
QList<QString> fnames;
fnames.append("file1.txt");
fnames.append("file2.txt");
// ..and so on with random iterations
QList<QFile *> files2; // file list to merge
QList<QTextStream *> files3;
foreach (QString file, files) {
files2.append(new QFile(file)); // create file obj
files2.last()->open(QIODevice::ReadOnly); // open file
files3.append(new QTextStream(files2.last())); // create textstream
}
QList< QTextStream * >::iterator i3;
for (i3 = files3.begin(); i3 != files3.end(); ++i3) {
qDebug() << (*i3)->readLine();
}