如何用C实现动态调度表

2024-01-04

首先,我了解如何使用函数指针和字符串或其他查找来实现调度表,这不是挑战。

我正在寻找的是一种动态向该表添加条目的方法在编译时.

我希望的代码结构类型是这样的:

Strategy.h - 包含调度程序的函数定义和调度表定义 Strategy.c - 包含调度程序的代码

MyFirstStrategy.c - 包括 Strategy.h 并提供该策略的一个实现 MyOtherStrategy.c - 包括 Strategy.h 并提供该策略的第二个实现

这个想法是,将函数指针和策略名称插入到调度表中的代码不应该存在于 Strategy.c 中,而应该位于各个策略实现文件中,并且查找表应该在编译时以某种方式动态构建。

对于固定大小的调度表,可以按如下方式管理,但我想要一个动态大小的表,我不希望 Strategy.c 实现必须包含实现的所有头文件,并且我希望调度表在编译时构建,而不是运行时。

固定尺寸示例

策略.h

typedef void strategy_fn_t(int);
typedef struct {
    char           *strategyName;
    strategy_fn_t  *implementation;
} dispatchTableEntry_t;

MyFirstStrategy.h

#include "Strategy.h"

void firstStrategy( int param );

MyOtherStrategy.h

#include "Strategy.h"

void otherStrategy( int param );

策略.c

#include "Strategy.h"
#include "MyFirstStrategy.h"
#include "MyOtherStrategy.h"

dispatchTableEntry_t dispatchTable[] = {
    { "First Strategy", firstStrategy },
    { "Other Strategy", otherStrategy }
};
int numStrategies = sizeof( dispatchTable ) / sizeof(dispatchTable[0] );

我真正想要的是一些预处理器魔法,我可以将其插入策略实现文件中以自动处理此问题,例如

MyFirstStrategy.c

#include "Strategy.h"

void firstStrategy( int param );

ADD_TO_DISPATCH_TABLE( "First Strategy", firstStrategy );

有什么想法吗 ?


在具有 gnu 链接器和编译器或兼容的系统上,可以将某些对象放在不同的部分中。然后链接器将为该部分的开始和结束生成符号。使用它,您可以将所有结构放入不同对象的该部分中,链接器将在链接时合并这些部分,并且您可以将它们作为数组访问。如果您在共享库中执行此操作,那么这需要更多的摆弄,并且绝对不能在 ELF Linux/*BSD 之外移植。

我在 MacOS 和 a.out BSD 上做过类似的事情(尽管这个例子不起作用),但我丢失了该代码。以下是如何在 Linux 上执行此操作的示例:

$ cat link_set.c
#include <stdio.h>

struct dispatch_table {
    const char *name;
    void (*fun)(int);
};

#define ADD_TO_DISPATCH_TABLE(name, fun) \
    static const struct dispatch_table entry_##fun \
        __attribute__((__section__("link_set_dispatch_table"))) = \
        { name, fun }

int
main(int argc, char **argv)
{
    extern struct dispatch_table __start_link_set_dispatch_table;
    extern struct dispatch_table __stop_link_set_dispatch_table;
    struct dispatch_table *dt;

    for (dt = &__start_link_set_dispatch_table; dt != &__stop_link_set_dispatch_table; dt++) {
        printf("name: %s\n", dt->name);
        (*dt->fun)(0);
    }
    return 0;
}

void
fun1(int x)
{
    printf("fun1 called\n");
}
ADD_TO_DISPATCH_TABLE("fun 1", fun1);

void
fun2(int x)
{
    printf("fun2 called\n");
}
ADD_TO_DISPATCH_TABLE("fun 2", fun2);
$ cc -o link_set link_set.c
$ ./link_set
name: fun 1
fun1 called
name: fun 2
fun2 called
$

解释一下宏的作用。它创建一个 structdispatch_table,其名称我们希望是唯一的,因为您可能希望在一个对象中多次使用它(如果多次使用同一个函数,请找出其他方式来命名该结构)并使用gnu 扩展名来指定对象应该结束于哪个部分。如果我们将对象放入“some_section_name”中,则链接器将自动添加符号 __start_some_section_name 和 __end_some_section_name。然后我们可以在这些符号之间行走并获取我们放入该部分的所有结构。

更新了 MacOS 的工作示例:

#include <stdio.h>
#include <mach-o/ldsyms.h>
#include <mach-o/getsect.h>
#include <mach-o/loader.h>

struct dispatch_table {
        const char *name;
        void (*fun)(int);
};

#define ADD_TO_DISPATCH_TABLE(name, fun) \
    static const struct dispatch_table entry_##fun \
    __attribute__((__section__("__DATA,set_dt"))) = \
    { name, fun }

int
main(int argc, char **argv)
{
        struct dispatch_table *start;
        unsigned long sz;
        intptr_t s;
        int i;

        s = (intptr_t)getsectdata("__DATA", "set_dt", &sz);
        if (s == 0)
                return 1;
        s += _dyld_get_image_vmaddr_slide(0);
        start = (struct dispatch_table *)s;
        sz /= sizeof(*start);

        for (i = 0; i < sz; i++) {
                struct dispatch_table *dt = &start[i];
                printf("name: %s\n", dt->name);
                (*dt->fun)(0);
        }
        return 0;
}

void
fun1(int x)
{
        printf("fun1 called\n");
}
ADD_TO_DISPATCH_TABLE("fun 1", fun1);

void
fun2(int x)
{
        printf("fun2 called\n");
}
ADD_TO_DISPATCH_TABLE("fun 2", fun2);

此处该节必须称为“set_dt”,因为在此可执行格式中节名称的长度有限。

当然,如果你已经需要这个,你肯定明白这一切都是非常危险的,不可移植的,不能保证永远工作(我三年前的代码不能在当前版本的 macOS 上工作) ,没有类型或其他安全性,如果其他东西决定将东西放入同名的部分中,那么东西最终会变成非常漂亮的烟花。但这是一个非常巧妙的技巧。我在两个大型项目中使用了这种方法,它确实节省了大量工作,中央调度表不必在共享存储库中进行编辑,这曾经给每个人带来冲突。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何用C实现动态调度表 的相关文章

随机推荐

  • 替换字符串数组中的字符 Javascript

    我已经定义并填充了一个名为的数组vertices 我可以将输出打印到 JavaScript 控制台 如下所示 v 2 11733 0 0204144 1 0852 v 2 12303 0 0131256 1 08902 v 2 12307
  • 如何引用哈希中键的值

    我希望能够引用 a 中的键Hash这样 如果该键的值发生变化 那么引用它的任何内容也会像这样 hash hash 1 foo hash 2 hash 1 hash 1 bar puts hash 2 I want this to be ba
  • “gcloud 应用程序部署”命令不起作用

    我安装了 python 版本的谷歌应用程序引擎 并在控制台中创建了一个新项目 我按照终端中的说明进行操作 我使用命令gcloud app deploy在我的工作目录中然后我收到此错误 ERROR The application field
  • 通过AWS CLI使用jar包的AWS lambda update-function-code

    我正在尝试通过 AWS CLI 使用本地计算机上的 jar 更新我的 lambda 函数代码 aws lambda 具有更新 zip 文件的函数代码的命令 但不包含更新 jar 的函数代码的命令 我可以使用 s3 存储桶上传 但我需要从本地
  • 类型错误:需要全局模式字符串

    我正在尝试编译sass using gulp ruby sass但我得到了TypeError glob pattern string required 这就是我的gulpfile js好像 var gulp require gulp sas
  • 使用Python根据日期抓取表格

    since a week ago i have been trying to scrape a table from this site https www bi go id id moneter informasi kurs transa
  • “NOT NULL DEFAULT ''”有什么意义?

    我在我一直从事的项目中的数据库上的许多字段中都看到过这种情况 其中列将被定义为不为空 但默认值为空字符串 这样做有什么意义呢 如果允许空字符串 为什么不只允许字段为空呢 NULL 有特殊的行为 将任何内容与 NULL 进行比较都会返回一个N
  • 在合并配置之前如何测试 dependentabot

    在将它合并到我的存储库之前 有没有办法测试 dependentabot 是否按预期工作 我在一个相当大的团队工作 我想确保我可以在合并之前测试功能 我创建了一个分支 其中向我们的开发分支开放了 PR 有没有办法确保创建更新依赖项的 PR 并
  • 使用 TeamCity 构建后如何部署?

    我正在将 TeamCity 设置为我的构建服务器 我已经设置了我的项目 它正在从 subversion 正确更新 并且构建正常 下一个是什么 理想情况下 我希望将其自动部署到测试服务器 并手动部署到实时 临时服务器 解决这个问题的最佳方法是
  • 从不完整的 HTTP JSON 响应中完成 json 字符串

    有时我会从json api下载数据 它会中途中断 通常是由于网络超时或其他一些问题 但是 在这种情况下 我希望能够读取可用的数据 这是一个例子 response 200 message None params body timestamp
  • 将方法和函数存储在数组中[重复]

    这个问题在这里已经有答案了 我不知道它是否可行 但我可以将方法或函数存储在数组中吗 我现在知道多维数组并用它来存储我想要的许多数组 我现在想做的是将我创建的方法或函数存储在某个类中 因为我想将所有函数存储到某个类 然后如果我想使用循环则调用
  • 从 Web 服务器的 HTML 资源中播放 FLAC

    我正在运行一个 apache2 网站 您可以在其中上传无损文件 包括 wav 和 flac wav 文件可以立即使用 但不支持 flac 所以我想知道是否有什么方法可以在 JavaScript 的帮助下播放这些 flac 文件 发生的情况如
  • Vaadin 和 Spring MVC 集成

    我正在考虑将 Spring MVC 与 Vaadin 框架一起使用的可能性 是否有任何记录在案的方法可以使它们很好地协同工作 另外 将它们一起使用是个好主意吗 与表现有关 我将在专用服务器上运行该应用程序 为了让我的问题更清楚一些 我如何从
  • SQL Server 中“描述表”的等效项是什么?

    我有一个 SQL Server 数据库 我想知道它有哪些列和类型 我更喜欢通过查询来完成此操作 而不是使用像企业管理器这样的 GUI 有没有办法做到这一点 您可以使用sp columns http msdn microsoft com en
  • 引用 google-play-services 库

    我正在使用 Google Maps Android API v2 开发一个项目 当我开始引用 google play services lib 时遇到问题 我的项目图标上出现一个红色感叹号 并且 问题 选项卡中出现一条错误消息 我使用的是
  • EF Code First 不生成表

    我正在开发 EF Code First 站点 并且编写了我的类和上下文类 其来源是 using System using System Collections Generic using System Linq using System T
  • 使子元素(带填充)为父元素的 100% 宽度和高度

    是否可以使子元素 带填充 具有其父元素的 100 宽度和高度 html div div div css child padding 15px 我尝试将子项设置为 100 宽度 高度 但这会使子项由于填充而大于父项 我还尝试使子位置绝对并将顶
  • 修复通过 REST API 上传文件时 Artifactory 中的校验和

    我使用下面的代码通过 Artifactory 的 REST API 上传文件 我的问题是 当我通过 GUI 查看文件时 我收到以下消息 客户端未发布校验和值 如果您信任上传的内容 您可以通过单击 修复 来接受实际的校验和 校验和按钮 如何修
  • 如何使用响应大小的图像来防止卡顿并减少布局偏移?

    我的网站托管了大量各种尺寸的图像 这些图像具有响应能力 并且可以在从桌面到移动设备的所有浏览器宽度上更改大小 我在 Google Search Console 中看到我的 CLS 累积布局偏移 很差 为 0 25 秒 我的网站布局随着图像加
  • 如何用C实现动态调度表

    首先 我了解如何使用函数指针和字符串或其他查找来实现调度表 这不是挑战 我正在寻找的是一种动态向该表添加条目的方法在编译时 我希望的代码结构类型是这样的 Strategy h 包含调度程序的函数定义和调度表定义 Strategy c 包含调