如何用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实现动态调度表 的相关文章

  • 删除文件的最后 10 个字符

    我想删除文件的最后 10 个字符 说一个字符串 hello i am a c learner 是文件内的数据 我只是希望该文件是 hello i am a 文件的最后 10 个字符 即字符串 c learner 应在文件内消除 解决方案 将
  • WPF DataGrid 多选

    我读过几篇关于这个主题的文章 但很多都是来自 VS 或框架的早期版本 我想做的是从 dataGrid 中选择多行并将这些行返回到绑定的可观察集合中 我尝试创建一个属性 类型 并将其添加到可观察集合中 它适用于单个记录 但代码永远不会触发多个
  • 没有特殊字符的密码验证器

    我是 RegEx 的新手 已经进行了大量搜索 但没有找到任何具体内容 我正在编写一个验证密码字符串的正则表达式 可接受的字符串必须至少具有 4 种字符类型中的 3 种 数字 小写字母 大写字母 特殊字符 我对包含有一个想法 也就是说 如果这
  • 类型中的属性名称必须是唯一的

    我正在使用 Entity Framework 5 并且有以下实体 public class User public Int32 Id get set public String Username get set public virtual
  • 如何在 Cassandra 中存储无符号整数?

    我通过 Datastax 驱动程序在 Cassandra 中存储一些数据 并且需要存储无符号 16 位和 32 位整数 对于无符号 16 位整数 我可以轻松地将它们存储为有符号 32 位整数 并根据需要进行转换 然而 对于无符号 64 位整
  • 如何从 Visual Studio 将视图导航到其控制器?

    问题是解决方案资源管理器上有 29 个项目 而且项目同时具有 ASP NET MVC 和 ASP NET Web 表单结构 在MVC部分中 Controller文件夹中有大约100个子文件夹 每个文件夹至少有3 4个控制器 视图完全位于不同
  • free 和 malloc 在 C 中如何工作?

    我试图弄清楚如果我尝试 从中间 释放指针会发生什么 例如 看下面的代码 char ptr char malloc 10 sizeof char for char i 0 i lt 10 i ptr i i 10 ptr ptr ptr pt
  • 为什么 GCC 不允许我创建“内联静态 std::stringstream”?

    我将直接前往 MCVE include
  • 如何连接重叠的圆圈?

    我想在视觉上连接两个重叠的圆圈 以便 becomes 我已经有部分圆的方法 但现在我需要知道每个圆的重叠角度有多大 但我不知道该怎么做 有人有主意吗 Phi ArcTan Sqrt 4 R 2 d 2 d HTH Edit 对于两个不同的半
  • 如何在 C++ 中标记字符串?

    Java有一个方便的分割方法 String str The quick brown fox String results str split 在 C 中是否有一种简单的方法可以做到这一点 The 增强分词器 http www boost o
  • 如何使从 C# 调用的 C(P/invoke)代码“线程安全”

    我有一些简单的 C 代码 它使用单个全局变量 显然这不是线程安全的 所以当我使用 P invoke 从 C 中的多个线程调用它时 事情就搞砸了 如何为每个线程单独导入此函数 或使其线程安全 我尝试声明变量 declspec thread 但
  • C++ 多行字符串原始文字[重复]

    这个问题在这里已经有答案了 我们可以像这样定义一个多行字符串 const char text1 part 1 part 2 part 3 part 4 const char text2 part 1 part 2 part 3 part 4
  • 访问外部窗口句柄

    我当前正在处理的程序有问题 这是由于 vista Windows 7 中增强的安全性引起的 特别是 UIPI 它阻止完整性级别较低的窗口与较高完整性级别的窗口 对话 就我而言 我想告诉具有高完整性级别的窗口进入我们的应用程序 它在 XP 或
  • WPF 数据绑定到复合类模式?

    我是第一次尝试 WPF 并且正在努力解决如何将控件绑定到使用其他对象的组合构建的类 例如 如果我有一个由两个单独的类组成的类 Comp 为了清楚起见 请注意省略的各种元素 class One int first int second cla
  • 如何获取 EF 中与组合(键/值)列表匹配的记录?

    我有一个数据库表 其中包含每个用户 年份组合的记录 如何使用 EF 和用户 ID 年份组合列表从数据库获取数据 组合示例 UserId Year 1 2015 1 2016 1 2018 12 2016 12 2019 3 2015 91
  • x:将 ViewModel 方法绑定到 DataTemplate 内的事件

    我基本上问同样的问题这个人 https stackoverflow com questions 10752448 binding to viewmodels property from a template 但在较新的背景下x Bind V
  • 编译时展开 for 循环内的模板参数?

    维基百科 here http en wikipedia org wiki Template metaprogramming Compile time code optimization 给出了 for 循环的编译时展开 我想知道我们是否可以
  • 使用特定参数从 SQL 数据库填充组合框

    我在使用参数从 sql server 获取特定值时遇到问题 任何人都可以解释一下为什么它在 winfom 上工作但在 wpf 上不起作用以及我如何修复它 我的代码 private void UpdateItems COMBOBOX1 Ite
  • C# 中最小化字符串长度

    我想减少字符串的长度 喜欢 这串 string foo Lorem ipsum dolor sit amet consectetur adipiscing elit Aenean in vehicula nulla Phasellus li
  • 指针和内存范围

    我已经用 C 语言编程有一段时间了 但对 C 语言还是很陌生 有时我对 C 处理内存的方式感到困惑 考虑以下有效的 C 代码片段 const char string void where is this pointer variable l

随机推荐

  • 替换字符串数组中的字符 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 包含调