繁体   English   中英

C99:静态库中的动态调度

[英]C99: Dynamic dispatch in a static library

假设我正在开发一个数学库,并且希望它能够检测用户的计算机是否支持SSE(以及哪个版本),然后基于该库,将为同一个API函数调用单独的内部函数。 我可以想到三种实现方法:

  1. 库中具有全局函数指针,并让用户在其源代码中调用mathInit()。 完成后,找出硬件细节,然后将功能指针分配给不同的功能。

  2. 相同,除了没有全局函数指针外,将它们放在由mathInit()返回的结构中。 这样,用户将必须调用math.vec3Add(...)或类似名称。

  3. 与1相同,但不使用全局指针,而是将mathInit()设为宏,以便函数指针在用户的main()函数中具有局部作用域(并要求从main()调用mathInit())。 当然,它将在标题中。

这些方法是否可取? 还有其他更好的方法吗?

恕我直言,这很大程度上是基于意见的。

我的观点是,数学库应公开其内部工作的最少详细信息,并且在可能的情况下,不应要求复杂的函数指针,数据结构甚至宏来处理用户代码。

我会选择(1),并假设您将函数指针完全隐藏在库中,即通过库代码中的间接调用它们。

(3)绝对是最糟糕的选择,因为它对用户代码施加了不直接明显的限制。 调试用户代码时,它也可能会产生非显而易见的问题/观察。

(2)这是表示库的一种非常不常见的方法,并且至少需要中等C的流利程度,并且可能会使非专家C用户望而却步。

您还可以将hasSSE函数与SSE和非SSE函数一起公开, hasSSE用户决定使用什么功能。 但是不确定(1)是否会有任何好处。

我的建议是为每个指令集(例如Xnosse.o Xsse3.o Xsse4.o等)编译一个单独的单元,并对这些指令使用自动调度程序。 用户需要为自己的PC获得最佳性能,而无需关心内部细节。

由于您编写的代码是在库中运行的,因此可以使用将在库加载时调用的init函数来自动决定加载时间。 您还可以使此决定仅在首次调用函数时才运行,这是用于延迟绑定。

这是一个代码示例(仅适用于gcc!)

编制单位:

//Xnosse.c
void do_some_math_stuff_no_sse(int x, int y)
{
    ...do some sophisticated math stuff with no sse support
}
void do_some_other_math_stuff_no_sse(int x, int y)
{
    ...do some other sophisticated math stuff with no sse support
}

//Xsse3.c
void do_some_math_stuff_sse3(int x, int y)
{
    ...do some sophisticated math stuff with sse3 support
}
void do_some_other_math_stuff_sse3(int x, int y)
{
    ...do some other sophisticated math stuff with sse3 support
}

//Xsse4.c
void do_some_math_stuff_sse4(int x, int y)
{
    ...do some sophisticated math stuff with sse4 support
}
void do_some_other_math_stuff_sse4(int x, int y)
{
    ...do some other sophisticated math stuff with sse4 support
}

现在到图书馆:

//my_math.h
/* Following definitions are in my_math.c */
extern void (*do_some_math_stuff)(int x, int, y);
extern void (*do_some_other_math_stuff)(int x, int y);

//my_math.c
void not_set(int x, int y)
{
    // If you don't want to use the constructor for any reason,
    // say you want lazy binding, this will do the trick as our
    // functions do_math_stuff and do_other_math_stuff are initialized
    // to this one
    setup();
}

void (*do_some_math_stuff)(int x, int, y) = not_set;
void (*do_some_other_math_stuff)(int x, int y) = not_set;

int detect_sse()
{
    ..Do runtime detection of sse version
}

/* The following function will be called when your library loads */
void __attribute__ ((constructor)) setup(void) 
{
    if (detect_sse() == 0)
    {
        do_some_math_stuff = do_some_math_stuff_no_sse;
        do_some_other_math_stuff = do_some_other_math_stuff_no_sse;
    }
else if (detect_sse() == 3)
    {
        do_some_math_stuff = do_some_math_stuff_sse3;
        do_some_other_math_stuff = do_some_other_math_stuff_sse3;
    }
else if (detect_sse() == 4)
    {
        do_some_math_stuff = do_some_math_stuff_sse4;
        do_some_other_math_stuff = do_some_other_math_stuff_sse4;
    }
}

如果您想要延迟绑定,请从安装程序中删除构造函数decorater并使用以下命令进行编译:

gcc -Wall -shared -fPIC -o libmy_math.so my_math.c Xnosse.c Xsse3.c Xsse4.c

如果要在库加载时运行动态调度程序,请对gcc使用以下附加参数:

gcc -Wall -shared -Wl,-init,setup -fPIC -o libmy_math.so my_math.c Xnosse.c Xsse3.c Xsse4.c

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM