C语言实现反射

摘要:
高级语言的反射机制只是简单地通过字符串类型获得相应的类或函数。Strcmp){returncallbacks[i].fn();}}这种方法的不便之处在于,当要映射的函数分散在不同的文件中时,每次添加新映射时都需要修改数组和头文件。2.使用自定义段gcc支持__attribute__,在指定的数据段中放置函数和变量。换句话说,编译器可以帮助我们完成向1中的数组添加成员的操作。使用此机制,回调函数可以在任何文件中声明,而无需修改其他文件。

高级语言的反射机制,简单来说就是可以通过字符串型获取对应的类或者函数。

  1. 基础形式,c语言结构化编程基础实现
    1)声明
    typedef void (*callback)(void);
    
    typedef struct {
        const char *name;
        callback fn;
    }callback_t;
    
    void f0();
    void f1();
    callback_t callbacks[] = {
        { "cmd0", f0},
        {"cmd1", f1},
    }
    
    void f0()
    {
    }
    
    void f1()
    {
    }

2)调用

    void do_callback(const char *name) 
    {
        size_t i;
        for (i = 0; i < sizeof(callbacks) / sizeof(callbacks[0]); i++) {
            if (!strcmp(callbacks[i].name, name)) {
                return callbacks[i].fn();
           }
        }
    }

这种方式的不便之处在于,当需要映射的函数因分散在不同文件时,每增加一个新的映射都需要修改这个数组,以及头文件。

2 利用自定义段
gcc支持通过使用__attribute__((section())),将函数、变量放到指定的数据段中。
也就是说,可以让编译器帮我们完成1中向数组添加成员的动作。
借助此机制,回调函数可以在任意文件申明,不需要修改其他文件。
自定义段的起始和结束地址,可以通过变量__start_SECTIONNAME和__stop_SECTIONNAME得到
例如通过__attribute__((section("ss"))定义自定义段,其开始地址为&__start_ss,结束地址为&__stop_ss

以实际代码示例:
文件a.c:

#define SEC __attribute__((__section__("ss"), aligned(sizeof(void*))))

void f1 (int a, int b)
{
    printf("%s %d %d %d
", __func__, __LINE__, a, b); 
}

// 编译器会自动提供__start_ss,__stop_ss标志段ss的起止地址
extern size_t __start_ss;
extern size_t __stop_ss;

typedef struct {
    void (*p)(int, int);
} node_t;

// 结构体变量a位于自定义段ss
SEC node_t a = { 
    .p = f1, 
};

int main(int argc, char **argv)
{
   int a = 0, b = 0;
    node_t *p;
    // 遍历段ss,执行node_t结构中的p指向的函数
    for (p = &__start_ss; p < &__stop_ss;p++) {
        p->p(a, b);
        a+=1;b+=2;
    }
}

文件b.c:

#include <stdio.h>
#define SEC __attribute__((__section__("ss"), aligned(sizeof(void*))))

void ff(int a, int b)
{
    printf("%s %d %d %d
", __func__, __LINE__, a, b); 
}

typedef struct {
    void (*p)(int, int);
} node_t;

// 结构体变量m位于自定义段ss
SEC node_t m = { 
    .p = ff, 
};

编译指令:
gcc a.c b.c

执行结果:
0x559ae9a88010 0x559ae9a88010 0x559ae9a88030
f1 8 0 0
0x559ae9a88028 0x559ae9a88010 0x559ae9a88030
ff 6 3 6

免责声明:文章转载自《C语言实现反射》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇程序集定位加载相关(Windows)VMware虚拟机安装Linux系统下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Linux C语言中gotoxy函数

在Linux中很多函数都不能使用,gotoxy就是其中的一个 实现方法: void gotoxy(int x,int y) { printf("%c[%d;%df",0x1B,y,x); }  解析: ANSI转义序列,很多类unix的终端,包括linux控制台都解释ANSI转义序列,转义符就是ESC,ASCII码是0x1b,比如...

我的全栈之路-C语言基础之C语言概述与开发环境搭建

我的全栈之路-C语言基础之C语言概述与开发环境搭建 我的全栈之路 1.1 信息技术发展趋势 1.2 浅谈计算机系统架构 1.2.1 计算机系统架构概述 1.2.2 计算机硬件系统 1.2.3 计算机软件系统 1.3 程序和指令 1.4 计算机编程语言发展史 1.5 程序的编译和解释 1.6 计算机语言应用场景 1.7 C语言概览 1.7.1 C...

python configparser模块

一、configparser介绍 configparser模块主要用于读取配置文件(*.ini),导入方法:from configparser import ConfigParser 二、configparser初始化 目录结构: api_test - common - config -apiConfig.ini 配置文件所在目录 -readConfig....

C语言分配4k对齐内存

分配内存,按4k字节对齐。亦即分配的内存指针指向的地址,低12位(二进制)均为0。 假设要分配size字节内存。基本思路就是先分配size+4k-1字节的内存,然后在起始的4k字节里,找到4k对齐的那个地址(即低12位为0),作为对齐内存首地址,返回。 当然为了避免内存泄漏,要保留初始分配的内存地址,在使用完内存后,需要free掉分配的所有内存。 #inc...

Delphi指针的用法

DELPHI指针的使用 大家都认为,C语言之所以强大,以及其自由性,很大部分体现在其灵活的指针运用上。因此,说指针是C语言的灵魂,一点都不为过。同时,这种说法也让很多人产生误解,似乎只有C语言的指针才能算指针。Basic不支持指针,在此不论。其实,Pascal语言本身也是支持指针的。从最初的Pascal发展至今的Object Pascal,可以说在指针运用...

java 利用反射调用静态方法的示例

内容简介 主要介绍使用反射的机制来调用执行类中的静态方法。 静态方法 public class GisUtil { private final static Logger logger = LoggerFactory.getLogger(GisUtil.class); public static ExportMethodResu...