【代码技巧】【C语言】鸿蒙 openHarmony 代码中的“多态”

  1. OsArmA32SyscallHandle
    当不同消息ID对应不同的处理函数时,c语言可以抽象函数指针,来归一化处理
    假如需要不同入参的处理函数时,鸿蒙的系统调用提供了另一种方法。
    源码链接
VOID OsArmA32SyscallHandle(TaskContext *regs)
{
    UINT32 ret;
    UINT8 nArgs;
    UINTPTR handle;
    UINT32 cmd = regs->reserved2;

    if (cmd >= SYS_CALL_NUM) {
        PRINT_ERR("Syscall ID: error %d !!!\n", cmd);
        return;
    }

    handle = g_syscallHandle[cmd];
    nArgs = g_syscallNArgs[cmd / NARG_PER_BYTE]; /* 4bit per nargs */
    nArgs = (cmd & 1) ? (nArgs >> NARG_BITS) : (nArgs & NARG_MASK);
    if ((handle == 0) || (nArgs > ARG_NUM_7)) {
        PRINT_ERR("Unsupported syscall ID: %d nArgs: %d\n", cmd, nArgs);
        regs->R0 = -ENOSYS;
        return;
    }

    OsSigIntLock();
    switch (nArgs) {
        case ARG_NUM_0:
        case ARG_NUM_1:
            ret = (*(SyscallFun1)handle)(regs->R0);		// 为何0入参的函数可以这样调用
            break;
        case ARG_NUM_2:
        case ARG_NUM_3:
            ret = (*(SyscallFun3)handle)(regs->R0, regs->R1, regs->R2);		// 同上 同下?? why
            break;
        case ARG_NUM_4:
        case ARG_NUM_5:
            ret = (*(SyscallFun5)handle)(regs->R0, regs->R1, regs->R2, regs->R3, regs->R4);		
            break;
        default:
            ret = (*(SyscallFun7)handle)(regs->R0, regs->R1, regs->R2, regs->R3, regs->R4, regs->R5, regs->R6);
    }

    regs->R0 = ret;
    OsSigIntUnlock();

    return;
}

上图中的系统调用有0~7个接口的情况,代码通过01、23、45、67来进行区分调用,
问题1:0和1时、2和3时入参个数不一样,为何能够编译通过??
问题2:函数调用为何能正常工作??

下边是我的理解
问题1:因为(*(SyscallFun1)handle)的强转将handle转化为了有一个入参的函数,所以就算handle对应SyscallFun0的类型,也不会编译报错
问题2:函数调用前,会将指定的入参入栈,在函数执行中,去栈中取所需的入参,因此,当handle是SyscallFun0的类型时,虽然regs->R0已经在栈中了,但是handle执行中并不会去使用它。
那么问题又来了,顺着这种思路,case就不需要区分了, 统一按照最长入参个数的函数类型来调用即可。
验证:

#include <stdio.h>
typedef unsigned int UINT32;
typedef UINT32 (*SyscallFun3)(UINT32, UINT32, UINT32);
typedef UINT32 (*SyscallFun2)(UINT32, UINT32);
typedef UINT32 (*SyscallFun1)(UINT32);
typedef UINT32 (*SyscallFun0)();
UINT32 fun0()
{
	printf("%s \n", __FUNCTION__);
}
UINT32 fun1(UINT32 a)
{
	printf("%s %d\n", __FUNCTION__, a);
}
UINT32 fun2(UINT32 a, UINT32 b)
{
	printf("%s %d %d \n", __FUNCTION__, a, b);
}
UINT32 fun3(UINT32 a, UINT32 b, UINT32 c)
{
	printf("%s %d %d %d\n", __FUNCTION__, a, b, c);
}
UINT32 *pArr[4] = {
	(UINT32 *)fun0,
	(UINT32 *)fun1,
	(UINT32 *)fun2,
	(UINT32 *)fun3,
};

int test(UINT32 nArgs)
{
	UINT32 * pFun = pArr[nArgs];
	switch (nArgs) {
		case 0:
		case 1:
		case 2:
		case 3:
			((SyscallFun3)pFun)(nArgs, nArgs, nArgs);	// 去掉switch 仅保留该句也是可行的
			break;
	}
}

int main()
{
   printf("Hello, World! \n");
   test(0);
   test(1);
   test(2);
   test(3);
   return 0;
}

实际输出:推荐一个在线C语言工具
Hello, World!
fun0
fun1 1
fun2 2 2
fun3 3 3 3

至于为何鸿蒙代码没有这样实现,应该是出于节省栈空间的考虑,毕竟连g_syscallNArgs这种变量都能省则省,8bit的数据拆成2个4bit来用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值