【问题标题】:How to create a universal library function in C?如何在 C 中创建通用库函数?
【发布时间】:2020-05-21 12:25:25
【问题描述】:

我想管理具有相同“通用”功能的各种实体。这些实体被示意性地声明为:

typedef struct {
  prefix_t pfx;
  int  i1;
  int  i2;
  int  i3;
} entity_t;

即用于内务处理的 typedef 结构前缀(包含链接指针、标志等)和有效负载。每个实体类型都有一个这样的类型声明。

管理函数只需要获取一个指向前缀的指针,这很容易,并且需要一个函数来“探索”有效负载以返回一个有意义的数量用于管理。

以二叉树管理为例:

void TreeInsert (prefix_t *a, prefix_t *b, int (*compare) (prefix_t *, prefix_t *));

在程序内部,我有这样的调用:

if (0 > compare(a, b)) {
   // a comes before b
} else {
  // a is the same as b or comes after
};

没关系。库函数编译没有错误也没有警告。

但很明显,比较函数不能只引用前缀。它需要探测有效载荷才能有用:

int comp_entity (entity_t *a, entity_t *b) {
   return a->i1 - b->i1;
}

编译器在以下行给出 comp_entity 警告:

TreeInsert (&a->pfx, &b->pfx, comp_entity);

由于库函数用于许多不同的“实体”,比较函数不能在调用时进行类型转换。不能为前缀键入比较函数的参数,否则无法访问有效负载。

我应该只为了将比较传递给库函数而定义一个特定的函数类型吗?比如:

typedef int (func_cast *) (prefix_t *, prefix_t*);

TreeInsert (&a->pfx, &b->pfx, (func_cast)comp_entity);

我宁愿避免这种情况。这可能吗?

注意:

我找到了create universal function pointer to any type C languageHow do I quiet the C compiler about a function pointer takes any number of arguments?,但他们没有提供解决方案。

【问题讨论】:

    标签: c function pointers compiler-warnings


    【解决方案1】:

    您的比较函数知道真正的类型应该是什么,因此您将使用类型prefix_t * 声明参数并将参数转换为函数内部:

    int comp_entity (prefix_t *a, prefix_t *b) {
       entity_t *ea = (entity_t *)a;
       entity_t *eb = (entity_t *)b;
       return ea->i1 - eb->i1;
    }
    

    【讨论】:

    • 我想到了这个解决方案。我不喜欢在专用于entity_t 的模块中编写的“复杂性”。我们“人为地”声明参数prefix_t 以立即对它们进行类型转换。如果没有适当的评论,未来的维护者会扯掉他们的头发。另外,我认为编译器不够聪明,无法意识到这是一个“假”演员,而且我担心在这样一个出于性能原因应该很短的小型实用程序上指令的总数会急剧增加。 -- 还有什么想法吗?
    • @ajlittoz 这种类型的转换在这样的回调函数中很常见。如果您查看qsort 的手册页,您会看到同样的内容。至于性能,那是过早的优化。除非您发现可测量的差异,否则您可能不应该担心它。此外,强制转换通常没有运行时成本。
    • 单独投射没有成本,但我们将 a 复制到 ea 并将 b 复制到 eab。每个与减法的成本大致相同,因此增加了 3 倍。我承认与通话和退货成本相比,这可能并不重要。主要反对意见是关于“软件质量”。几个月后阅读代码时,为什么将类型为 prefix_t 的参数用作 entity_t?原因隐藏在一个简单的#include library.h 下,其文本不会立即在屏幕上可见。这就是为什么我最终认为调用中的(func_cast)comp_entity 比强制参数更可取。
    • @ajlittoz 函数转换调用未定义的行为,因为参数类型不兼容。所以不是一个好主意。此外,编译器非常擅长优化,所以除非你找到可衡量的效果,否则不要担心性能。
    • @ajlittoz 您还可以使参数的类型 void * 并使其工作相同(您也不需要显式强制转换),它可能使代码更具自我记录性.
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多