- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我正在尝试编写一个程序,将一个 C++ 代码的静态库用于另一个 C++ 代码。第一个 C++ 代码是 hello.cpp
:
#include <iostream>
#include <string.h>
using namespace std;
extern "C" void say_hello(const char* name) {
cout << "Hello " << name << "!\n";
}
int main(){
return 0;
}
我使用以下命令从这段代码 hello.a
中创建了一个静态库:
g++ -o hello.a -static -fPIC hello.cpp -ldl
这是使用库的第二个 C++ 代码,say_hello.cpp
:
#include <iostream>
#include <string>
#include <dlfcn.h>
using namespace std;
int main(){
void* handle = dlopen("./hello.a", RTLD_LAZY);
cout<<handle<<"\n";
if (!handle) {
cerr<<"Cannot open library: "<<dlerror()<<'\n';
return 1;
}
typedef void (*hello_t)();
dlerror(); // reset errors
hello_t say_hello = (hello_t) dlsym(handle, "say_hello");
const char *dlsym_error = dlerror();
if (dlsym_error) {
cerr<<"Cannot load symbol 'say_hello': "<<dlsym_error<<'\n';
dlclose(handle);
return 1;
}
say_hello("World");
dlclose(handle);
return 0;
}
然后我使用以下方法编译了 say_hello.cpp
:
g++ -W -ldl say_hello.cpp -o say_hello
并在命令行中运行 ./say_hello
。我希望得到 Hello World!
作为输出,但我却得到了这个:
0x8ea4020
Hello ▒▒▒▒!
问题是什么?是否有任何技巧可以使方法参数兼容,就像我们在 ctypes
中使用的那样或者什么?
如果有帮助,我会使用 lenny。
编辑 1:
我更改了代码并使用了动态库“hello.so”,这是我使用以下命令创建的:
g++ -o hello.so -shared -fPIC hello.cpp -ldl
第6行代码改为:
void* handle = dlopen("./hello.so", RTLD_LAZY);
当我尝试编译 say_hello.cpp
时,出现了这个错误:
say_hello.cpp: In function ‘int main()’:
say_hello.cpp:21: error: too many arguments to function
我还尝试使用这一行来编译它:
g++ -Wall -rdynamic say_hello.cpp -ldl -o say_hello
但是出现了同样的错误。所以我删除了参数 "World"
并且编译没有错误;但是当我运行可执行文件时,我得到了与之前提到的相同的输出。
编辑 2:
根据@Basile Starynkevitch 的建议,我将 say_hello.cpp
代码更改为:
#include <iostream>
#include <string>
#include <dlfcn.h>
using namespace std;
int main(){
void* handle = dlopen("./hello.so", RTLD_LAZY);
cout<<handle<<"\n";
if (!handle) {
cerr<<"Cannot open library: "<<dlerror()<<'\n';
return 1;
}
typedef void hello_sig(const char *);
void* hello_ad = dlsym(handle, "say_hello");
if (!hello_ad){
cerr<<"dlsym failed:"<<dlerror()<<endl;
return 1;
}
hello_sig* fun = reinterpret_cast<hello_sig*>(hello_ad);
fun("from main");
fun = NULL;
hello_ad = NULL;
dlclose(handle);
return 0;
}
在此之前,我使用下面一行来制作一个 .so
文件:
g++ -Wall -fPIC -g -shared hello.cpp -o hello.so
然后我用这个命令编译了say_hello.cpp
:
g++ -Wall -rdynamic -g say_hello.cc -ldl -o say_hello
然后使用 ./say_hello
运行它。现在一切顺利。感谢@Basile Starynkevitch 对我的问题耐心等待。
最佳答案
函数永远不会有空地址,因此函数名称(或实际上在 C++ 或 C 中定义的任何名称)上的 dlsym
不能为 NULL
而不会失败:
hello_t say_hello = (hello_t) dlsym(handle, "say_hello");
if (!say_hello) {
cerr<<"Cannot load symbol 'say_hello': "<<dlerror()<<endl;
exit(EXIT_FAILURE);
};
和dlopen(3)记录在dynamically load只有动态库(不是静态库!)。这意味着 ELF 中的共享对象 (*.so
)格式。阅读 Drepper 的论文 How To Use Shared Libraries
我相信您可能已经在 dlopen
中发现了一个错误(另请参阅其 POSIX dlopen 规范);对于静态库 hello.a
应该会失败;它总是用于位置无关的共享库(如hello.so
)。
您应该只dlopen
position independent code shared objects 用
g++ -Wall -O -shared -fPIC hello.cpp -o hello.so
或者如果您有多个 C++ 源文件:
g++ -Wall -O -fPIC src1.cc -c -o src1.pic.o
g++ -Wall -O -fPIC src2.cc -c -o src2.pic.o
g++ -shared src1.pic.o src2.pic.o -o yourdynlib.so
您可以删除 -O
优化标志或添加 -g
进行调试,或者如果需要将其替换为 -O2
.
这非常有效:我的 MELT项目(一种扩展 GCC 的领域特定语言)正在大量使用它(生成 C++ 代码,像上面那样即时进行编译,然后 dlopen
-ing 生成的共享对象).还有我的manydl.c示例演示了您可以在 Linux 上dlopen
大量(不同的)共享对象(通常数百万,至少数十万)。实际上限制是地址空间。
顺便说一句,你不应该 dlopen
有 main
函数的东西,因为 main
是在主程序调用中定义的(可能间接地)dlopen
。
此外,g++
的参数顺序也很重要;你应该编译主程序
g++ -Wall -rdynamic say_hello.cpp -ldl -o say_hello
-rdynamic
标志是让加载的插件 (hello.so
) 从您的 say_hello
程序中调用函数所必需的.
出于调试目的,始终将 -Wall -g
传递给上面的 g++
。
顺便说一句,原则上您可以 dlopen
一个没有 PIC 的共享对象(即未使用 -fPIC
编译);但是 dlopen
一些 PIC 共享对象要好得多。
另请阅读 Program Library HowTo和 C++ dlopen mini-howto (因为 name mangling )。
文件 helloshared.cc
(我的 C++ 小插件源代码)是
#include <iostream>
#include <string.h>
using namespace std;
extern "C" void say_hello(const char* name) {
cout << __FILE__ << ":" << __LINE__ << " hello "
<< name << "!" << endl;
}
我正在编译它:
g++ -Wall -fPIC -g -shared helloshared.cc -o hello.so
主程序在文件 mainhello.cc
中:
#include <iostream>
#include <string>
#include <dlfcn.h>
#include <stdlib.h>
using namespace std;
int main() {
cout << __FILE__ << ":" << __LINE__ << " starting." << endl;
void* handle = dlopen("./hello.so", RTLD_LAZY);
if (!handle) {
cerr << "dlopen failed:" << dlerror() << endl;
exit(EXIT_FAILURE);
};
// signature of loaded function
typedef void hello_sig_t(const char*);
void* hello_ad = dlsym(handle,"say_hello");
if (!hello_ad) {
cerr << "dlsym failed:" << dlerror() << endl;
exit(EXIT_FAILURE);
}
hello_sig_t* fun = reinterpret_cast<hello_sig_t*>(hello_ad);
fun("from main");
fun = NULL; hello_ad = NULL;
dlclose(handle);
cout << __FILE__ << ":" << __LINE__ << " ended." << endl;
return 0;
}
我用它编译
g++ -Wall -rdynamic -g mainhello.cc -ldl -o mainhello
然后我运行 ./mainhello
并得到预期的输出:
mainhello.cc:7 starting.
helloshared.cc:5 hello from main!
mainhello.cc:24 ended.
请注意 mainhello.cc
中的签名 hello_sig_t
应该与 say_hello
的函数兼容(同态,即相同) helloshared.cc
插件,否则为 undefined behavior (您可能会遇到 SIGSEGV
崩溃)。
关于c++ - 如何将参数传递给从 CPP 中的静态库加载的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20987913/
我想做的是让 JTextPane 在 JPanel 中占用尽可能多的空间。对于我使用的 UpdateInfoPanel: public class UpdateInfoPanel extends JP
我在 JPanel 中有一个 JTextArea,我想将其与 JScrollPane 一起使用。我正在使用 GridBagLayout。当我运行它时,框架似乎为 JScrollPane 腾出了空间,但
我想在 xcode 中实现以下功能。 我有一个 View Controller 。在这个 UIViewController 中,我有一个 UITabBar。它们下面是一个 UIView。将 UITab
有谁知道Firebird 2.5有没有类似于SQL中“STUFF”函数的功能? 我有一个包含父用户记录的表,另一个表包含与父相关的子用户记录。我希望能够提取用户拥有的“ROLES”的逗号分隔字符串,而
我想使用 JSON 作为 mirth channel 的输入和输出,例如详细信息保存在数据库中或创建 HL7 消息。 简而言之,输入为 JSON 解析它并输出为任何格式。 最佳答案 var objec
通常我会使用 R 并执行 merge.by,但这个文件似乎太大了,部门中的任何一台计算机都无法处理它! (任何从事遗传学工作的人的附加信息)本质上,插补似乎删除了 snp ID 的 rs 数字,我只剩
我有一个以前可能被问过的问题,但我很难找到正确的描述。我希望有人能帮助我。 在下面的代码中,我设置了varprice,我想添加javascript变量accu_id以通过rails在我的数据库中查找记
我有一个简单的 SVG 文件,在 Firefox 中可以正常查看 - 它的一些包装文本使用 foreignObject 包含一些 HTML - 文本包装在 div 中:
所以我正在为学校编写一个 Ruby 程序,如果某个值是 1 或 3,则将 bool 值更改为 true,如果是 0 或 2,则更改为 false。由于我有 Java 背景,所以我认为这段代码应该有效:
我做了什么: 我在这些账户之间创建了 VPC 对等连接 互联网网关也连接到每个 VPC 还配置了路由表(以允许来自双方的流量) 情况1: 当这两个 VPC 在同一个账户中时,我成功测试了从另一个 La
我有一个名为 contacts 的表: user_id contact_id 10294 10295 10294 10293 10293 10294 102
我正在使用 Magento 中的新模板。为避免重复代码,我想为每个产品预览使用相同的子模板。 特别是我做了这样一个展示: $products = Mage::getModel('catalog/pro
“for”是否总是检查协议(protocol)中定义的每个函数中第一个参数的类型? 编辑(改写): 当协议(protocol)方法只有一个参数时,根据该单个参数的类型(直接或任意)找到实现。当协议(p
我想从我的 PHP 代码中调用 JavaScript 函数。我通过使用以下方法实现了这一点: echo ' drawChart($id); '; 这工作正常,但我想从我的 PHP 代码中获取数据,我使
这个问题已经有答案了: Event binding on dynamically created elements? (23 个回答) 已关闭 5 年前。 我有一个动态表单,我想在其中附加一些其他 h
我正在尝试找到一种解决方案,以在 componentDidMount 中的映射项上使用 setState。 我正在使用 GraphQL连同 Gatsby返回许多 data 项目,但要求在特定的 pat
我在 ScrollView 中有一个 View 。只要用户按住该 View ,我想每 80 毫秒调用一次方法。这是我已经实现的: final Runnable vibrate = new Runnab
我用 jni 开发了一个 android 应用程序。我在 GetStringUTFChars 的 dvmDecodeIndirectRef 中得到了一个 dvmabort。我只中止了一次。 为什么会这
当我到达我的 Activity 时,我调用 FragmentPagerAdapter 来处理我的不同选项卡。在我的一个选项卡中,我想显示一个 RecyclerView,但他从未出现过,有了断点,我看到
当我按下 Activity 中的按钮时,会弹出一个 DialogFragment。在对话框 fragment 中,有一个看起来像普通 ListView 的 RecyclerView。 我想要的行为是当
我是一名优秀的程序员,十分优秀!