使用dlopenC++动态库的函数符号的常规做法

文章目录

  • 一、Name Mangling
  • 二、extern "C"
  • 三、Loading Functions
  • 四、Loading Classes
  • 参考

一、Name Mangling

在 C 中,符号名称与函数名称相同:strcpy 的符号将是 strcpy,因为在 C 中没有两个非静态函数可以具有相同的名称。

因为 C++ 允许重载(具有相同名称但不同参数的不同函数)并且具有许多 C 所没有的功能(如类、成员函数、异常规范),所以不可能简单地使用函数名称作为符号名称。

  • 为了解决这个问题,C++ 使用所谓的name mangling,它将函数名称和所有必要的信息(例如参数的数量和大小)转换为一些只有编译器知道的看起来奇怪的字符串。
  • 例如,"foo "的mangled name可能看起来像 foo@4%6^。或者,它甚至可能不包含 "foo "这个词。

二、extern “C”

C++ 有一个特殊的关键字来声明具有 C 绑定的函数:extern “C”。声明为 extern “C” 的函数使用函数名称作为符号名称,就像 C 函数一样。因此,只有非成员函数可以声明为 extern “C”,并且不能重载它们。

extern “C” 的函数可以包含 C++ 代码,这样的函数是一个full-featured的 C++ 函数。

  • 使用 extern “C” 的目的是为了在C++代码中正确处理C语言的命名约定和链接规则。它通常用于确保C++编译器正确处理C语言的全局变量或函数的命名。

三、Loading Functions

在 C++ 中,函数的加载方式与 C 语言相同,都是使用 dlsym。要加载的函数必须限定为 extern “C”,以避免符号名被混淆。

main.cpp:

#include <iostream>
#include <dlfcn.h>int main() {using std::cout;using std::cerr;cout << "C++ dlopen demo\n\n";// open the librarycout << "Opening hello.so...\n";void* handle = dlopen("./hello.so", RTLD_LAZY);if (!handle) {cerr << "Cannot open library: " << dlerror() << '\n';return 1;}// load the symbolcout << "Loading symbol hello...\n";typedef void (*hello_t)();// reset errorsdlerror();hello_t hello = (hello_t) dlsym(handle, "hello");const char *dlsym_error = dlerror();if (dlsym_error) {cerr << "Cannot load symbol 'hello': " << dlsym_error <<'\n';dlclose(handle);return 1;}// use it to do the calculationcout << "Calling hello...\n";hello();// close the librarycout << "Closing library...\n";dlclose(handle);
}

hello.cpp:


#include <iostream>extern "C" void hello() {std::cout << "hello" << '\n';
}

Makefile:

# This file is part of the C++ dlopen mini HOWTO. You can find the complete
# HOWTO and/or updated versions at
# http://www.isotton.com/howtos/C++-dlopen-mini-HOWTO/
#
# Copyright 2002-2006 Aaron Isotton <aaron@isotton.com>
# Licensed under the GNU GPL.example1: main.cpp hello.so$(CXX) $(CXXFLAGS) -o example1 main.cpp -ldlhello.so: hello.cpp$(CXX) $(CXXFLAGS) -shared -o hello.so hello.cppclean:rm -f example1 hello.so.PHONY: clean

函数 hello 定义在 hello.cppas extern “C” 中;它通过 dlsym 调用加载到 main.cpp 中。该函数必须限定为 extern “C”,否则我们将不知道它的符号名称

extern “C” 和extern “C” {}声明的区别:

  • 第一种(内联)形式是具有 extern linkage 和 C language linkage的声明;第二个仅影响C language linkage。
1)函数声明
extern "C" int foo;
extern "C" void bar();等价于
extern "C" {extern int foo;extern void bar();
}2)变量声明
extern "C" int foo;extern "C" {int foo;
}

在第一种声明方式extern “C” int foo;中,只是单独将变量foo声明为按照C语言的链接规则进行编译和链接。这意味着foo变量在C++代码中仍然保持其原始的命名和类型。
而在第二种声明方式extern “C” {
int foo;
}中,使用了一个代码块将foo包裹起来。这种方式告诉编译器,代码块内的所有变量都按照C语言的链接规则进行处理。所以此时的foo变量也会在C++代码中具有C语言的链接规则。
总结来说,两种声明方式都可以达到使用C语言链接规则的目的,但第二种方式可以批量地将多个变量进行声明。

四、Loading Classes

加载类要困难一些,因为我们需要的是类的实例,而不仅仅是指向函数的指针。

我们无法使用 new 创建类的实例,因为类没有在可执行文件中定义,而且(在某些情况下)我们甚至不知道它的名称。

我们可以通过多态性来解决这个问题。我们在可执行文件中定义一个带有虚拟成员的接口基类,在模块中定义一个派生的实现类。一般来说,接口类是抽象的(如果一个类有纯粹的虚拟函数,它就是抽象的)。

由于类的动态加载通常用于插件(插件必须公开一个定义明确的接口),因此我们无论如何都必须定义一个接口和派生实现类。

接下来,我们在模块中定义了两个额外的辅助函数,即类工厂函数。其中一个函数创建一个类的实例,并返回一个指向该实例的指针。另一个函数接收一个指向由类工厂创建的类的指针,并将其销毁。这两个函数被限定为 extern “C”。

要使用模块中的类,请使用 dlsym 加载这两个工厂函数,就像加载 hello 函数一样;然后,我们就可以创建和销毁任意数量的实例了。

main.cc

#include "polygon.hpp"
#include <iostream>
#include <dlfcn.h>int main() {using std::cout;using std::cerr;// load the triangle libraryvoid* triangle = dlopen("./triangle.so", RTLD_LAZY);if (!triangle) {cerr << "Cannot load library: " << dlerror() << '\n';return 1;}// reset errorsdlerror();// load the symbolscreate_t* create_triangle = (create_t*) dlsym(triangle, "create");const char* dlsym_error = dlerror();if (dlsym_error) {cerr << "Cannot load symbol create: " << dlsym_error << '\n';return 1;}destroy_t* destroy_triangle = (destroy_t*) dlsym(triangle, "destroy");dlsym_error = dlerror();if (dlsym_error) {cerr << "Cannot load symbol destroy: " << dlsym_error << '\n';return 1;}// create an instance of the classpolygon* poly = create_triangle();// use the classpoly->set_side_length(7);cout << "The area is: " << poly->area() << '\n';// destroy the classdestroy_triangle(poly);// unload the triangle librarydlclose(triangle);
}

polygon.hpp:


#ifndef POLYGON_HPP
#define POLYGON_HPPclass polygon {
protected:double side_length_;public:polygon(): side_length_(0) {}virtual ~polygon() {}void set_side_length(double side_length) {side_length_ = side_length;}virtual double area() const = 0;
};// the types of the class factories
typedef polygon* create_t();
typedef void destroy_t(polygon*);#endif

triangle.cpp:


#include "polygon.hpp"
#include <cmath>class triangle : public polygon {
public:virtual double area() const {return side_length_ * side_length_ * sqrt(3) / 2;}
};// the class factoriesextern "C" polygon* create() {return new triangle;
}extern "C" void destroy(polygon* p) {delete p;
}

Makefile:

# This file is part of the C++ dlopen mini HOWTO. You can find the complete
# HOWTO and/or updated versions at
# http://www.isotton.com/howtos/C++-dlopen-mini-HOWTO/
#
# Copyright 2002-2006 Aaron Isotton <aaron@isotton.com>
# Licensed under the GNU GPL.example2: main.cpp polygon.hpp triangle.so$(CXX) $(CXXFLAGS) -o example2 main.cpp -ldltriangle.so: triangle.cpp polygon.hpp$(CXX) $(CXXFLAGS) -shared -o triangle.so triangle.cppclean:rm -f example2 triangle.so.PHONY: clean

参考

  • C++ dlopen mini HOWTO

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/155343.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言童年生活二三事(ZZULIOJ1091:童年生活二三事(多实例测试))

题目描述 Redraiment小时候走路喜欢蹦蹦跳跳&#xff0c;他最喜欢在楼梯上跳来跳去。 但年幼的他一次只能走上一阶或者一下子蹦上两阶。 现在一共有N阶台阶&#xff0c;请你计算一下Redraiment从第0阶到第N阶共有几种走法。 输入&#xff1a;输入包括多组数据。 每组数据包括一…

antd design 5 版本 文件上传

<UploadcustomRequest{customRequest}accept".csv" showUploadList{false}><Button icon{<UploadOutlined />}>上传 CSV 文件</Button></Upload> accept 代表限制的上传类型 也可设置 .excel // 文件上传 ( CSV ) const customReques…

Python开源自动化工具Playwright安装及介绍

一个非常强大的自动化项目叫 playwright-python 它支持主流的浏览器&#xff0c;包含&#xff1a;Chrome、Firefox、Safari、Microsoft Edge 等&#xff0c;同时支持以无头模式、有头模式运行&#xff0c;并提供了同步、异步的 API&#xff0c;可以结合 Pytest 测试框架 使用&…

Kafka(四)消费者消费消息

文章目录 如何确保不重复消费消息&#xff1f;消费者业务逻辑重试消费者提交自定义反序列化类消费者参数配置及其说明重要的参数session.time.ms和heartbeat.interval.ms和group.instance.id增加消费者的吞吐量消费者消费的超时时间和poll()方法的关系 消费者消费逻辑启动消费者…

万宾科技智能井盖传感器,提升市政井盖健康

市政井盖就是城市里不可或缺的基础设施之一&#xff0c;关于它的监测工作可马虎不得。它承载着保护市民的交通安全以及城市正常运转的重要使命。虽然现在城市化的速度很快&#xff0c;但是传统的市政井盖管理方式变得有些力不从心了。井盖的覆盖范围很广&#xff0c;如果单单依…

Python 安装win32com失败

今天进行服务器迁移&#xff0c; 中间有用的python调用win32com组件让docx转换成pdf。不出意外的话出意外了&#xff0c;pip安装win32com的时候各种安装不上&#xff0c; 今天处理完问题之后&#xff0c;记录一下&#xff0c;与人方便与己方便。 在cmd上面&#xff0c;一开始…

【0235】修改私有内存(private memory)中的MyBEEntry时,st_changecount值前后变化

上一篇: 【0234】PgBackendStatus 记录当前postgres进程的活动状态 1. pg_stat_activity中xxx实时信息如何实现? 客户端(eg:psql)在连接上postmaster之后,postmaster守护进程会fork()一个后端进场(backend process),之后此客户端的所有操作、交互均有此对应的Backen…

智慧安防监控系统EasyCVR(v3.4)开放协议的介绍及使用

安防视频监控系统EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台可拓展性强、视频能力灵活&#xff0c;能…

2023最新软件测试20个基础面试题及答案

什么是软件测试&#xff1f; 答案&#xff1a;软件测试是指在预定的环境中运行程序&#xff0c;为了发现软件存在的错误、缺陷以及其他不符合要求的行为的过程。 软件测试的目的是什么&#xff1f; 答案&#xff1a;软件测试的主要目的是保证软件的质量&#xff0c;并尽可能大…

Kotlin 核心语法,为什么选择Kotlin ?

Kotlin 是一个基于 JVM 的新的编程语言&#xff0c;由 JetBrains 开发。与Java相比&#xff0c;Kotlin的语法更简洁、更具表达性&#xff0c;而且提供了更多的特性。 Kotlin是使用Java开发者的思维被创建的&#xff0c;Intellij作为它主要的开发IDE。对于 Android开发者&#…

Docker基础知识总结

文章目录 1.Docker介绍2.Docker版本3.为什么要使用Docker4.Docker基础组件4.1 镜像&#xff08;Images&#xff09;4.2 容器&#xff08;Container&#xff09;和仓库&#xff08;Repository&#xff09; 5.Docker安装6.Docker run7.Dockerfile8.Docker commit9.镜像发布到镜像…

第三天 - 列表推导式详解

python 中列表推导式&#xff0c;主要完成快速赋值&#xff0c;快速生成列表、元组、字典等应用场景。是一个非常好用的工具。在一些排序过程还可以做为参数传入函数。 1.先来一个尝试一下 alist [x for x in range(1,10)]print(alist) # [1, 2, 3, 4, 5, 6, 7, 8, 9] 2. …

肖sir__linux讲解vim命令(3.1)

vim 命令 一、 vi/vim 编辑器共分为三种模式&#xff1a; 格式 &#xff1a;vim 文件名 命令模式&#xff08;Command mode&#xff09;&#xff0c;“ESC”或ctrlc键 输入模式&#xff08;Insert mode&#xff09; 底线命令模式&#xff08;Last line mode&#xff09; …

【数据结构】图的深度优先遍历

一.图的遍历 图的遍历需要解决的关键问题 1.在图中&#xff0c;如何选取遍历的起始顶点&#xff1f; 从编号小的顶点开始 在线性表中&#xff0c;数据元素在表中的编号就是元素在序列中的位置&#xff0c;因而其编号是唯一的。在树中&#xff0c;将节点按层序编号&#xff0…

亚马逊运营一定要用动/静态住宅IP代理吗?

作为全球最大的电商平台之一&#xff0c;亚马逊已经成为许多商家的首选销售平台。而代理IP作为近几天互联网的热门工具&#xff0c;在跨境电商界也起着非常强大的作用。那么在亚马逊运营中&#xff0c;适合动态住宅代理还是静态住宅代理呢&#xff1f;下面我们一起来探索&#…

猫罐头牌子哪个好一点?精选5款口碑好的猫罐头推荐!

猫罐头牌子哪个好一点&#xff1f;选择猫罐头是十分重要的事情&#xff0c;千万不能将就。因为&#xff0c;好的猫罐头不仅可以营养丰富&#xff0c;水分充足&#xff0c;适口性好&#xff0c;还能易吸收。而一旦选择错误&#xff0c;不仅无法达到上述效果&#xff0c;还可能产…

代码随想录算法训练营第28天|93.复原IP地址 78.子集 90.子集II

JAVA代码编写 93 .复原IP地址 有效 IP 地址 正好由四个整数&#xff08;每个整数位于 0 到 255 之间组成&#xff0c;且不能含有前导 0&#xff09;&#xff0c;整数之间用 . 分隔。 例如&#xff1a;"0.1.2.201" 和 "192.168.1.1" 是 有效 IP 地址&…

android查漏补缺(8)Android广播不同种类介绍

按照是否有序分类 1&#xff0c;普通广播&#xff08;无序广播&#xff09; 广播按照逻辑上同一时刻&#xff08;实际可能被CPU按照抢占式任务无序发给注册模块&#xff09;发送给注册模块 #发送方法&#xff1a; Context.sendBroadcast() 2&#xff0c;有序广播 广播按照…

申银万国期货通过ZStack Cube信创超融合一体机打造金融信创平台

信创是数字中国建设的重要组成部分&#xff0c;也是数字经济发展的关键推动力量。作为云基础软件企业&#xff0c;云轴科技ZStack产品矩阵全面覆盖数据中心云基础设施&#xff0c;ZStack信创云首批通过可信云《一云多芯IaaS平台能力要求》先进级&#xff0c;是其中唯一兼容四种…

基于单片机仓库温湿度监测报警系统仿真设计

**单片机设计介绍&#xff0c;基于单片机仓库温湿度监测报警系统仿真设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的仓库温湿度监测报警系统可以被设计成能够实时监测仓库内的温度和湿度&#xff0c;并根据预设…