如何编写包装C variadic函数的动态加载程序?

发布于 2025-02-11 03:49:01 字数 1031 浏览 3 评论 0 原文

为了避免在运行时取决于库,我编写了一个动态加载程序,该加载程序使用 dlopen / dlsym 在运行时形成库。

要在构建时间链接,我使用包装函数,这些功能将 dlsym 设置的功能指针调用。

我遇到了variadic函数的问题,那里似乎没有一种动态加载功能并将其转发为变异参数的方法。

有没有办法编写支持不支持varargs的包装库……

  • 取决于知道variadic参数的数量或以哨兵值结束参数。
  • 通过更改调用variadic函数的代码(在这种情况下我无法控制)来依赖于解决问题的工作。

似乎可以支持这一点,但是大多数现有的答案都表明,在我的用例中,以不实际的方式解决问题。

对于上下文,我包装的功能签名是:

struct wl_proxy *wl_proxy_marshal_flags(
        struct wl_proxy *proxy,
        uint32_t opcode,
        const struct wl_interface *interface,
        uint32_t version,
        uint32_t flags,
        ...);

此功能通过生成的代码调用(请参阅 Wayland-Scanner ),尽管我宁愿不专门提出这个问题。


请注意,已经提出了类似的问题,例如:

,但他们建议在我的用例中使用 vfprintf 诸如使用 vfprintf

In order to avoid depending on a library at run-time I have written a dynamic loader that uses dlopen / dlsym to load functions form a library at run-time.

To link at build time I use wrapper functions which call into the function pointers set by dlsym.

I've run into a problem with variadic functions, where there doesn't seem to be a way to dynamically load the function and have it forward the variadic arguments.

Is there a way to write a wrapper library that supports varargs that doesn't...

  • Depend on knowing the number of variadic arguments or ending the arguments with a sentinel value.
  • Depend on working around the problem by changing the code which calls into the variadic function (which I happen not to have control over in this case).

It seems like this might be supported but most existing answers suggest to workaround the problem in a way that isn't practical in my use case.

For context the function signature I'm wrapping is:

struct wl_proxy *wl_proxy_marshal_flags(
        struct wl_proxy *proxy,
        uint32_t opcode,
        const struct wl_interface *interface,
        uint32_t version,
        uint32_t flags,
        ...);

This function is called by generated code (see wayland-scanner), although I rather not make this question specifically about Wayland.


Note that similar questions have been asked already, such as:

But they suggest alternatives such as using vfprintf which don't work in my use case.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

メ斷腸人バ 2025-02-18 03:49:01

通常的方法是使variadic函数仅在真实函数上以 va_list 作为参数作为界面。此模式(这是 fprintf vfprintf 在标准库中工作的方式),通过使用 va_list 。

在这里您可以写:

struct wl_proxy *vwl_proxy_marshal_flags(
        struct wl_proxy *proxy,
        uint32_t opcode,
        const struct wl_interface *interface,
        uint32_t version,
        uint32_t flags,
        va_list params) {
    // actual code
    ...
}
struct wl_proxy *wl_proxy_marshal_flags(
        struct wl_proxy *proxy,
        uint32_t opcode,
        const struct wl_interface *interface,
        uint32_t version,
        uint32_t flags,
        ...) {
    va_list params
    va_start(params, flags);
    struct wl_proxy *cr = vwl_proxy_marshal_flags(proxy, opcode,
        interface, version, flags, params);
    va_end(params);
    return cr;
}

The usual way is to make the variadic function a mere interface on a real function taking a va_list as a parameter. This pattern (which is the way fprintf and vfprintf work in the Standard Library) provides a smooth way to forward a dynamic number of arguments by calling directly the function using the va_list.

Here you could write:

struct wl_proxy *vwl_proxy_marshal_flags(
        struct wl_proxy *proxy,
        uint32_t opcode,
        const struct wl_interface *interface,
        uint32_t version,
        uint32_t flags,
        va_list params) {
    // actual code
    ...
}
struct wl_proxy *wl_proxy_marshal_flags(
        struct wl_proxy *proxy,
        uint32_t opcode,
        const struct wl_interface *interface,
        uint32_t version,
        uint32_t flags,
        ...) {
    va_list params
    va_start(params, flags);
    struct wl_proxy *cr = vwl_proxy_marshal_flags(proxy, opcode,
        interface, version, flags, params);
    va_end(params);
    return cr;
}
暖阳 2025-02-18 03:49:01

假设variadic参数列表中的所有项目均为相同的类型,那么您可以使用此技巧:

将实际函数实现为非variadic的函数,占据数组和大小。以及任意数量的非广播论点。示例:

void actual_func (int this, double that, size_t argc, int argv[argc])
{
  printf("this:%d that:%f\n", this, that);
  for(size_t i=0; i<argc; i++)
    printf("%d ", argv[i]);
}

此处 可以是任何类型的任何参数,与示例中的固定参数相对应。 argc argv 分别是大小和数组。

然后,我们可以编写一个variadic宏来将variadic调用转换为一个普通函数调用:

#define COUNT_ARGS(...) ( sizeof((int[]){__VA_ARGS__}) / sizeof(int) )
#define func(this,that,...) actual_func(this, that, COUNT_ARGS(__VA_ARGS__), (int[]){__VA_ARGS__})

辅助宏 count_args 计算variadic项目的数量 - 它们都必须是 int 或它行不通。这给出了数组大小。然后,以变色的参数初始化了以复合文字形式的临时数组,然后传递给该函数。完整的示例:

#include <stdio.h>

void actual_func (int this, double that, size_t argc, int argv[argc])
{
  printf("this:%d that:%f\n", this, that);
  for(size_t i=0; i<argc; i++)
    printf("%d ", argv[i]);
}

#define COUNT_ARGS(...) ( sizeof((int[]){__VA_ARGS__}) / sizeof(int) )
#define func(this,that,...) actual_func(this,that,COUNT_ARGS(__VA_ARGS__),(int[]){__VA_ARGS__})

int main (void) 
{
  func(1, 1.0f, 1, 2, 3);
}

这与整数分配具有相同的类型安全性,含义是SO-SO,但它比Variadic函数更安全。

Assuming all items in the variadic argument list are of the same type, then you can use this trick:

Implement the actual function as a non-variadic one, taking an array and size. As well as any number of non-variadic arguments. Example:

void actual_func (int this, double that, size_t argc, int argv[argc])
{
  printf("this:%d that:%f\n", this, that);
  for(size_t i=0; i<argc; i++)
    printf("%d ", argv[i]);
}

Here this and that can be any parameters of any type, corresponding to the fixed parameters in your examples. The argc and argv is the size and array respectively.

We can then write a variadic macro to translate the variadic call into a plain function call:

#define COUNT_ARGS(...) ( sizeof((int[]){__VA_ARGS__}) / sizeof(int) )
#define func(this,that,...) actual_func(this, that, COUNT_ARGS(__VA_ARGS__), (int[]){__VA_ARGS__})

The helper macro COUNT_ARGS counts the number of variadic items - they all have to be int or it won't work. This gives the array size. Then a temporary array in the form of a compound literal is initialized with the variadic arguments, then passed to the function. Full example:

#include <stdio.h>

void actual_func (int this, double that, size_t argc, int argv[argc])
{
  printf("this:%d that:%f\n", this, that);
  for(size_t i=0; i<argc; i++)
    printf("%d ", argv[i]);
}

#define COUNT_ARGS(...) ( sizeof((int[]){__VA_ARGS__}) / sizeof(int) )
#define func(this,that,...) actual_func(this,that,COUNT_ARGS(__VA_ARGS__),(int[]){__VA_ARGS__})

int main (void) 
{
  func(1, 1.0f, 1, 2, 3);
}

This has the same type safety as integer assignment, meaning so-so, but it is way safer than variadic functions.

说谎友 2025-02-18 03:49:01

坏消息:我认为没有一个通用(与平台无关的)解决方案。

好消息:Wayland的开发人员似乎已经想到了这个问题,这就是为什么他们创建了 _array _ 函数,例如 Wayland-client-core.h

struct wl_proxy *
wl_proxy_marshal_flags(struct wl_proxy *proxy, uint32_t opcode,
                       const struct wl_interface *interface,
                       uint32_t version,
                       uint32_t flags, ...);

struct wl_proxy *
wl_proxy_marshal_array_flags(struct wl_proxy *proxy, uint32_t opcode,
                             const struct wl_interface *interface,
                             uint32_t version,
                             uint32_t flags,
                             union wl_argument *args);

以前的函数调用后者的函数:

WL_EXPORT struct wl_proxy *
wl_proxy_marshal_flags(struct wl_proxy *proxy, uint32_t opcode,
                       const struct wl_interface *interface, uint32_t version,
                       uint32_t flags, ...)
{
        union wl_argument args[WL_CLOSURE_MAX_ARGS];
        va_list ap;

        va_start(ap, flags);
        wl_argument_from_va_list(proxy->object.interface->methods[opcode].signature,
                                 args, WL_CLOSURE_MAX_ARGS, ap);
        va_end(ap);

        return wl_proxy_marshal_array_flags(proxy, opcode, interface, version, flags, args);
}

Bad news: I don't think there is an universal (platform-independent) solution.

Good news: Wayland's developers seem to have thought of this problem, that's why they created _array_ functions, e.g. in wayland-client-core.h:

struct wl_proxy *
wl_proxy_marshal_flags(struct wl_proxy *proxy, uint32_t opcode,
                       const struct wl_interface *interface,
                       uint32_t version,
                       uint32_t flags, ...);

struct wl_proxy *
wl_proxy_marshal_array_flags(struct wl_proxy *proxy, uint32_t opcode,
                             const struct wl_interface *interface,
                             uint32_t version,
                             uint32_t flags,
                             union wl_argument *args);

The former function calls the latter:

WL_EXPORT struct wl_proxy *
wl_proxy_marshal_flags(struct wl_proxy *proxy, uint32_t opcode,
                       const struct wl_interface *interface, uint32_t version,
                       uint32_t flags, ...)
{
        union wl_argument args[WL_CLOSURE_MAX_ARGS];
        va_list ap;

        va_start(ap, flags);
        wl_argument_from_va_list(proxy->object.interface->methods[opcode].signature,
                                 args, WL_CLOSURE_MAX_ARGS, ap);
        va_end(ap);

        return wl_proxy_marshal_array_flags(proxy, opcode, interface, version, flags, args);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文