有没有办法从varargs函数的参数中检索浮点数?

时间:2010-05-20 16:10:24

标签: c objective-c variadic-functions

如果函数是使用明确说明参数类型的原型定义的,例如

void somefunc(int arg1, float arg2);

但实现为

void somefunc(int arg1, ...) { ... }

是否可以使用va_arg来检索浮点数?它通常被禁止这样做,因为varargs函数具有隐式类型提升,例如float to double,因此尝试检索未启动类型是不受支持的,即使使用未启动类型调用函数对更具体的函数原型也是如此。

这样做的原因是在运行时检索不同类型的参数,作为obj-c解释器的一部分,其中一个函数将被重用于所有不同类型的方法。

这最好是独立于架构(如果没有其他相同的代码可以在模拟器和设备上运行),尽管如果没有办法做到这一点,那么将接受设备特定的修复。

编辑: 忘了具体提到:函数知道参数的类型和数量(它查找要使用SEL _cmd参数进行地图查找解释的代码)

4 个答案:

答案 0 :(得分:6)

您几乎必须使用每个架构的程序集才能执行此操作。首先,你不能使用varargs,因为 - 正如你暗示的那样 - varargs的调用ABI与非varargs的调用ABI不同;参数编码方式不同,寄存器状态在调用边界上不同。

最简单的方法是创建大量的存根函数,以及您需要的所有论证变体。然后,每个存根都会获取特定的参数,并将它们捆绑到更通用的内容中,以便代码更常用。

如果你不想走那条路,那么你将不得不知道参数的类型,特定目标ABI的参数的编码规则,然后你需要编写代码到当你的通用蹦床被召唤时,有效地将争论从他们的隐藏孔中撕掉。

你需要完成所有这些,同时通过无意的寄存器使用来破坏任何参数。您可能会发现我的write-up of objc_msgSend()间接有用,因为它准确描述了Objective-C如何处理这个问题(提示:它不遗余力地触及第一个以外的任何参数二)。

答案 1 :(得分:3)

您可以执行以下操作:

static inline uint32_t floatparam (float f) {
  union { uint32_t u; float f; } u;
  u.f = f;
  return u.u;
}

然后总是这样调用你的函数:

fn(5, floatparam(0.5f), floatparam(1.1f), ...);

在你的功能中,你可以做

va_list val;

va_start (val, arg1);
while (<more arguments>) {
  union { float f; uint32_t u; } u;
  u.u = va_arg (val, uint32_t);
  // float is now in u.f
}

这避免了不必要的类型促销问题,并且不需要汇编语言。

当然,你不应该误导你的功能。如果它是一个varargs函数,它是一个varargs函数,句点。正如bbum所说,ABI是不同的。

答案 2 :(得分:2)

如果在显示时声明并定义了函数,则无法执行任何操作,尤其是在与体系结构无关的方式中。代码坏了。它不起作用。 C中的可变函数和非可变函数之间绝对没有兼容性。这些函数具有完全不同的性质,它们属于两个完全不同的非交叉世界。

如果在您的特定实现/体系结构中,这两个可以某种方式被迫一起工作,这是您的实现的具体细节。在这种情况下,您必须研究您的特定架构。

答案 3 :(得分:0)

您可以随时丢弃varargs,而是将所有参数编码为字符串。您的功能界面保持不变,您可以完全自由地解释数据。然而,这不是一个漂亮的解决方案,因为你实际上是在实现一个脚本解析器。