我怎样才能实现下的动态调度表 [英] How can I implement a dynamic dispatch table in C

查看:82
本文介绍了我怎样才能实现下的动态调度表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

First of all, I understand how to implement a dispatch table using function pointers and a string or other lookup, that's not the challenge.

我正在寻找一些方法来动态条目添加到在编译时这个表的

What I'm looking for is some way to dynamically add entries to this table at compile time.

该型code结构,我希望是这样的:

The type of code structure I'd hope for is something like:

Strategy.h - 包含调度和调度表定义函数定义
Strategy.c - 包含code为调度员

Strategy.h - contains function definition for the dispatcher and dispatch table definition Strategy.c - contains code for dispatcher

MyFirstStrategy.c - 包括Strategy.h,并提供一个实现战略
MyOtherStrategy.c - 包括Strategy.h并提供第二实施stratgy的

MyFirstStrategy.c - includes Strategy.h and provides one implementation of the strategy MyOtherStrategy.c - includes Strategy.h and provides a second implementation of the stratgy

的想法是,code插入函数指针和策略名称到调度表不应该生活在Strategy.c,但应该是在各个战略实施文件和查找表应该得到某种动态编译构造时间。

The idea is that the code to insert function pointers and strategy names into the dispatch table should not live in Strategy.c but should be in the individual strategy implementation files and the lookup table should be somehow dynamically constructed at compile time.

有关固定大小的调度表,这可以如下管理,但我希望有一个动态调整表,我不想在Strategy.c实施必须包括对所有实施的头文件,我会喜欢在编译时构建的调度表,而不是运行时间。

For a fixed size dispatch table, this could be managed as below but I want a dynamically sized table, I don't want the Strategy.c implementation to have to include all of the header files for the implementations and I'd like the dispatch table to be constructed at compile time, not run time.

固定大小示例

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 );

Strategy.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] );

我真正想要的是一些preprocessor神奇,我可以插入到战略实施文件自动例如处理这

What I really want is some preprocessor magic which I can insert into the strategy implementation files to handle this automatically e.g.

#include "Strategy.h"

void firstStrategy( int param );

ADD_TO_DISPATCH_TABLE( "First Strategy", firstStrategy );

有什么想法?

推荐答案

在与GNU链接器和编译器或东西兼容的系统,它可能把某些对象的不同部分。然后,链接器将生成段的开始和结束的符号。使用,你可以把你所有的结构成不同的对象节,链接器将在链接时合并这些部分,你可以访问它们作为一个数组。这需要更多的摆弄,如果你的共享库这样做的,绝对不是便携式ELF的Linux之外/ * BSD。

On systems with gnu linker and compiler or something compatible, it's possible to put certain objects in different sections. The linker will then generate symbols for the start and end of the section. Using that you can put all your structs into that section in different objects, the linker will consolidate those sections when linking and you can access them as an array. This requires a lot more fiddling if you're doing this in shared libraries and is definitely not portable outside of ELF Linux/*BSD.

我已经做了类似的事情(尽管这个例子不能正常工作)在MacOS和a.out的BSD,但我已经失去了code。下面是如何做到这一点在Linux上工作的例子:

I've done a similar thing (although this example will not work) on MacOS and a.out BSD, but I've lost that code. Here's an example of how to do it that works on 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
$

要解释宏做什么。它创建,我们希望是独一无二的,因为你可能要多次使用它在一个对象(如果你使用相同的函数多次,想出一些其他的方式来命名结构)名称的结构dispatch_table,并将它与定义GNU扩展到指定对象应以结束该节。如果我们把对象变成some_section_name,那么连接器会自动添加符号__start_some_section_name和__end_some_section_name。然后,我们可以将这些符号间游走,并得到所有我们把该部分的结构。

To explain what the macro does. It creates a struct dispatch_table with a name that we hope is unique since you might want to use it multiple times in one object (if you use the same function multiple times, figure out some other way to name the struct) and defines it with the gnu extension to specify which section the object should end up with. If we put the objects into "some_section_name" then the linker will automatically add symbols __start_some_section_name and __end_some_section_name. We can then walk between those symbols and get all the structs we put into that section.

更新,适用于MacOS工作的例子:

Updated with a working example for 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在这里,因为节名称长度受限这种可执行格式。

The section has to be called "set_dt" here because section names are limited in length in this executable format.

当然,如果你已经尽量为需要这个你一定明白,这一切都是非常危险的,不可移植,不能保证永远工作来(在code,我从三年前有没有工作的MacOS的当前版本),没有类型或其它安全,如果别的东西决定把东西放到具有相同名称的东西节将在很pretty烟花结束。但是,这是一个非常巧妙的方法。我的两个大型项目中使用这种方法,它确实节省了大量的工作中心调度表没有在使用的给大家冲突的共享资源库进行编辑。

Of course, if you've come as far as needing this you surely understand that this is all very dangerous, unportable, not guaranteed to work ever (the code I had from three years ago didn't work on a current version of macos), has no type or other safety and if something else decides to put things into a section with the same name things will end up in very pretty fireworks. But it's a very neat trick. I use this method in two large projects and it really saves a lot of work central dispatch tables don't have to be edited in a shared repository which used to give everyone conflicts.

这篇关于我怎样才能实现下的动态调度表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆