[英]C99: Dynamic dispatch in a static library
假设我正在开发一个数学库,并且希望它能够检测用户的计算机是否支持SSE(以及哪个版本),然后基于该库,将为同一个API函数调用单独的内部函数。 我可以想到三种实现方法:
库中具有全局函数指针,并让用户在其源代码中调用mathInit()。 完成后,找出硬件细节,然后将功能指针分配给不同的功能。
相同,除了没有全局函数指针外,将它们放在由mathInit()返回的结构中。 这样,用户将必须调用math.vec3Add(...)或类似名称。
与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.