python

在 python 当中,一切都是对象。函数也是第一等公民。

如果要使用函数名称字符串发起调用,目标在于通过字符串找到函数对象。

函数

def func(a):print("global function func", a)

显示的从全局命名空间中查找

python 当中,全局的命名空间可使用内置函数`global`获取,`global` 返回一个全局的名字对象字典。

globals()["func"](3)

使用内置的解释器

脚本语言都是解释执行,而且一般都提供一个解释器接口。比如 `python,javascript,shell` 中的 `eval`.`lua`自带的`loadstring`. `redis`当中也使用`eval` 来执行`lua`脚本。

eval("func")(3)

将字符串转换成字节码

`python` 内置的`compile` 函数能够将源码字符串转换成字节码,字节码对象再交由解释器执行,这其实是上一种方式的细化。

code = compile("func('hello')","_", "eval")
eval(code)

方法

class T:def __init__(self):passdef f(self, a):print("method f", a)
obj = T()

普通方法需要通过对象来调用,所以首要条件要获取这样一个对象。获取目的对象的方式可以使用上边提到的 3 中获取全局变量的方法。

剩下的工作在于通过方法字符串结合目标对象找到方法对象。

`getattr`

`getattr`触发属性的查找。

getattr(obj, "f")(3)

`operator`

内置的`operator`触发属性的查找。

import operator
operator.attrgetter("f")(obj)(3)

`operator.methodcaller` 更是直接发起方法调用。

operator.methodcaller("f", 3)(obj)

C/C++

C函数

其实最终都是根据符号(字符串名称)来查找,在 `python` 当中,找的是对象;在 `C/C++` 当中找的地址罢了。

//oo.c
#include <stdio.h>void func()
{printf("this is func\n");
}int main()
{char* funcname = "func";//如何根据 funcname 来调用func 函数?return 0;
}

我们知道,`Linux`上的`elf` 可执行文件在运行时,会构建一个**全局符号表**。全局符号表通过 `elf`自身的动态符号表`.dynsym` 和所有直接和间接依赖的动态库中的 `.dynsym` 来构建,当然里边涉及很多细节,比如全局符号介入(global symbol interpose)等。获取了全局符号表之后,就能从全局符号表当中查找到函数符号的对应地址。

void *dlopen(const char *filename, int flags);

第一个参数传 NULL 时,返回的就是全局符号表。

所以只需要包含 `#include <dlfcn.h>`,代码也很明白。

void* handle = dlopen(NULL, RTLD_LAZY);if(handle == NULL){printf("%s\n", dlerror());return 1;}void* (*fptr)() = dlsym(handle, "func");  if(fptr != NULL){fptr();}else{printf("%s\n", dlerror());}

编译执行

gcc oo.c -ldl -o oo.out && ./oo.out

等等,出错了,好像也没这么简单。

./oo.out: undefined symbol: func

提示未找到符号。

readelf --dyn-syms oo.out

看看动态符号表当中,还真没这个符号。

其实这牵扯到另外一个问题,主模块(编译成 .out 的那个二进制模块)当中的符号,和普通的动态库 `.so`当中的不同。普通的动态库中的符号,只要没有`static` 限制为未局部的,都会成为导出符号,出现在 `.dynsym` 当中。但是,**主模块呢,只有在其他模块,比如 `A .so`中使用和主模块中同名的符号(不论这个符号在`A.so` 还是 `B.so` 当中是否有定义)**,主模块的符号才会成为导出符号,出现在 `.dynsym` 当中。

这个动作由链接器完成,使用 `-Wl,--export-dynamic` 链接器选项即可,或者使用 `-rdynamic` 选项来表达相同的含义。

gcc -Wl,--export-dynamic oo.c -ldl -o oo.out && ./oo.out
#this is func

`C++` 类方法

//o.cpp
#include <stdio.h>
#include <malloc.h>class T
{int x;T(int i);void f();
};T::T(int i)
{x = i;
}
void T::f()
{printf("x is %d\n", this->x);
}
T* getobj()
{return static_cast<T*>(malloc(sizeof(T)));
}
void freeobj(void *p)
{free(p);
}

C++ 的源代码可以编译成`.so` 动态库,给 C 调用。到了 `.so` 这一层,就只用考虑 `abi`.

g++ -fpic -shared o.cpp -o libo.so

在`abi`的维度上,只要兼容,任何语言都能实现互操作。

//maino.cpp
#include <stdio.h>
#include <dlfcn.h>int main(int argc, char* argv[], char* en[])
{void* handle = dlopen(NULL, RTLD_LAZY);if(handle == NULL){printf("%s\n", dlerror());return 0;}void* (*getobj)() = dlsym(handle, "_Z6getobjv");  //T* getobj()if(getobj != NULL){void* obj = getobj();void (*ctor)(void*, int i) = dlsym(handle, "_ZN1TC1Ei");  //T::T(int)ctor(obj, 4);void (*f)() = dlsym(handle, "_ZN1T1fEv");  //void T::f()f(obj);void (*freeobj)(void*p) = dlsym(handle, "_Z7freeobjPv"); //void freeobj(void *p)freeobj(obj);}else{printf("%s\n", dlerror());}return 0;
}

构建并执行:

gcc maino.c ./libo.so -ldl -o maino.out && ./maino.out
#x is 4

**几个细节的阐述**

1. C++ 中存在的名称修饰(name mangling),包含在动态符号表当中的都是名称修饰之后的名字。装饰之后的名字可以通过 `objdump`来获取,使用 `c++filt` 验证。
2. 类 T 中所有的内容全部都是 `private`访问控制的,但是这些访问控制都是给编译器编译期来使用限制的,运行时的动态链接都是靠符号来查找。

通过函数名称字符串发起调用/函数名反射相关推荐

  1. python通过字符串来调用函数

    导读 有时候我们想要通过字符串来直接调用函数,方便通过输入的参数来直接控制调用的函数 常规操作 def function1():print("function1")def func ...

  2. 通过函数名调用函数和通过函数指针调用函数有什么区别呢?为什么调用函数指针没有直接调用函数效率高?

     1.通过函数名调用函数和通过函数指针调用函数有什么区别呢? 首先函数名.函数指针都表示代码段的起始地址. 1)调用函数的时候必须指定函数名,可是当有时候不确定具体调用哪个函数,当某些事件发生后才 ...

  3. 【优化版】(终稿)C++实现科学计算器主函数代码(含调用函数)

    代码仅供作业小组人员使用 以下链接为经小组成员完成改善得到的中间结果各版本 (终稿)C++实现科学计算器主函数代码(含调用函数)_m0_57453166的博客-CSDN博客 (初稿)C++实现科学计算 ...

  4. (终稿)C++实现科学计算器主函数代码(含调用函数)

    代码仅供作业小组人员使用 (非完成版)C++实现科学计算器主函数代码,(不含调用函数)_克蕾尔的博客-CSDN博客 (初稿)C++实现科学计算器主函数代码(含调用函数)_Honeyseaaa的博客-C ...

  5. main函数外也可以调用函数

    在写程序的时候,我们都知道一般一个程序肯定有一个主函数,它是真个程序的入口地址,也就是程序在这里开始执行,就像要进入一个大房子的大门,我们必须要打开这个门,才能进入整个房子里面去!并且可能我们都觉得一 ...

  6. 【不懂】js中必须使用字符串形式调用函数的情况

    js中使用某些函数时必须使用函数的字符串形式,直接调用则会出现问题,比如下面的echarts的调整尺寸的方法,scaleX的第一个框可以使用,todo里面的则会导致页面无法调整尺寸. 在这篇文章里发现 ...

  7. Shell函数返回值、删除函数、在终端调用函数

    Shell 也支持函数.Shell 函数必须先定义后使用. Shell 函数的定义格式如下: function_name () {list of commands[ return value ] } ...

  8. Shell函数:Shell函数返回值、删除函数、在终端调用函数

    函数可以让我们将一个复杂功能划分成若干模块,让程序结构更加清晰,代码重复利用率更高.像其他编程语言一样,Shell 也支持函数.Shell 函数必须先定义后使用. Shell 函数的定义格式如下: f ...

  9. python调用函数示例_python 动态调用函数实例解析

    1. 根据字符串名称 动态调用 python文件内的方法eval("function_name")(参数) 2. 根据字符串 动态调用类中的静态方法,getattr(ClassNa ...

最新文章

  1. navicat对mysql的备份
  2. java7 javascript引擎_Java7中脚本引擎的一般用法,共三种方法获得JavaScript引擎:名称、文件扩展名、MIME类型 | 学步园...
  3. AI也能写高考作文?我们用清华刚刚开源的「九歌」试了试
  4. H3C无线 AC网关式部署无线开局(WEB版)
  5. Android Message 及其使用
  6. pip在安装模块时提示Read timed out
  7. Tkinter的listbox组件
  8. sqlite3:not found 解决方法
  9. openapi and light-4j
  10. 如何利用魔棒工具抠图_10秒教你如何用PS魔棒工具抠图
  11. hdu1728 逃离迷宫
  12. 风儿轻轻地吹,沙儿轻轻地飘
  13. 利用HomeKit、智汀家庭云,让不同生态智能家居实现互联互通
  14. 启鸿蒙什么意思,鸿蒙是什么?大多数人可能都理解错了
  15. 《穹顶之下》全文整理
  16. 看看一位清华计算机专业的学生怎么看LINUX与WINDOWS的
  17. PHP——后端跨平台脚本语言
  18. SpringBoot项目中自动加载datasourceConfig配置导致启动失败
  19. mac系统安装ae打不开,显示意外退出的解决方法
  20. 数据库_mysql数据库引擎_数据库索引

热门文章

  1. 【计算机网络基础 七】输入URL到浏览器发生了什么
  2. 使用Jasypt对SpringBoot配置文件加密(数据源为SpringBoot默认的数据源HikariDataSource)
  3. 江苏计算机一级证书考试试题,2016年江苏省计算机一级考试试题
  4. excel 数据不全
  5. 给windows右键,添加快捷键
  6. 达芬奇调色DaVinci Resolve Studio18v18.1.4 2023中文版更新发布,支持intel/M1/M2芯片
  7. 地震勘探原理(六)之地震组合方法原理
  8. 微信小程序获取当前日期及时间
  9. nginx 之 http 转 https (两种方式)
  10. 踩坑NVIDIA Jetson TX2、Ubuntu16.04、ROS Kinetic安装