如果函数是使用明确说明参数类型的原型定义的,例如
void somefunc(int arg1, float arg2);
但实现为
void somefunc(int arg1, ...) { ... }
是否可以使用va_arg来检索浮点数?它通常被禁止这样做,因为varargs函数具有隐式类型提升,例如float to double,因此尝试检索未启动类型是不受支持的,即使使用未启动类型调用函数对更具体的函数原型也是如此。
这样做的原因是在运行时检索不同类型的参数,作为obj-c解释器的一部分,其中一个函数将被重用于所有不同类型的方法。
这最好是独立于架构(如果没有其他相同的代码可以在模拟器和设备上运行),尽管如果没有办法做到这一点,那么将接受设备特定的修复。
编辑: 忘了具体提到:函数知道参数的类型和数量(它查找要使用SEL _cmd参数进行地图查找解释的代码)
答案 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,而是将所有参数编码为字符串。您的功能界面保持不变,您可以完全自由地解释数据。然而,这不是一个漂亮的解决方案,因为你实际上是在实现一个脚本解析器。