zoukankan      html  css  js  c++  java
  • AUTOSAR ArcticCore重构

    https://mp.weixin.qq.com/s/3f4emE-XSDI2EQGwKgL4Ig

     Arctic CoreAUTOSAR的实现,早期版本是开源的。

    基本问题

    ARM架构下对CAN driver的实现(arch/arm/arm_cm3/drivers/Can.c)中,有这样一段代码:

     

    Can_Arc_Hoh是个数组,数组每个元素含有Can_Arc_EOL标识是否是最后一个元素,最后一个元素为0,其他为1

    这里利用指针hoh--,然后++,然后使用do while循环来遍历数组每一个元素,遍历完Can_Arc_EOL1的元素亦即最后一个元素后结束循环。

    hoh--,后++ 是一个小技巧,在这里也很实用,不过也略显突兀和生硬,不对应问题逻辑。do while 循环也不常用。能否改善呢?

    这里对数组元素的Can_Arc_EOL标志的判断,在遍历完该数组元素之后进行,所以我们可以使用如下for循环替换:

    for (const Can_HardwareObjectType* hoh = canHwConfig->Can_Arc_Hoh;; hoh++) {
        /// do something
        if (hoh->Can_Arc_EOL) {
             break;
        }
    } 

    这样看比前面的do while循环,要自然一些,没有与解决问题逻辑不相关的代码。

    更进一步

    如果这样的do while循环只有一处,则到此为止。不过我们可以看到代码里有五六处这样的写法,如果每一处都改成for if break的写法,也觉得繁琐。有没有办法改进呢?

    如果对Linux内核代码较熟悉,则会发现Linux kernel中的循环,多使用for_each_entry这样的宏定义来简化对list, hlist等数据结构的操作,使用者只需关注业务逻辑,而无需关注数据结构。

    我们尝试使用定义for_each_HOH宏来屏蔽这里数组遍历的细节。因为宏定义只能定义for (;;)这一段,无法引入花括号内的代码段,所以if break的逻辑也要在for头里面实现,即如:

     因为for里面无法使用语句,只能使用表达式,所以这里for的第三段里的if else编译无法通过。聪明的人立马就能想到?三元表达式,可以与if else等效。但如其名字一样,问号三元表达式是一个表达式,而非语句,即expression, not statement. 所以代码变成了:

    #define for_each_HOH(hoh, config) 
    
            for (hoh = config->Can_Arc_Hoh;; 
    
                (hoh->Can_Arc_EOL ? break : hoh++))

     这里break不能作为三元表达式里的一项,编译无法通过。所以需要一个结束标识符,这里有两种方法:

    1. 引入变量就叫__EOL,代码如下:

    #define for_each_HOH(hoh, config) 
    
            for (boolean __EOL = false, hoh = config->Can_Arc_Hoh; !__EOL; 
    
                (hoh->Can_Arc_EOL ? __EOL = true : hoh++)) 

     可以发现__EOLhoh->Can_Arc_EOL 有一定的对应关系,即__EOL等于刚遍历过的数组元素的Can_Arc_EOL标志,可以简化为:

    #define for_each_HOH(hoh, config) 
    
            for (boolean __EOL = false, hoh = config->Can_Arc_Hoh; !__EOL; 
    
                __EOL = hoh->Can_Arc_EOL, hoh++)

     看到了意想不到的效果,三元表达式也不用了。只是最后多执行一次hoh++,还有一个假设:数组不为空,包含至少一个元素。假设成立才能给__EOL赋初值为false。这个假设应该是一直的假设,do while也需要这个假设。

    所以这种方法最后多执行一次hoh++。似乎又回到最初的do while多执行一次hoh--。不太一样,do whilehoh--纯属小技巧。而这里hoh++是问题逻辑的一部分,只是最后会多执行一次。

    2. 使用现有游标hoh作为结束标志,代码如下:

    #define for_each_HOH(hoh, config) 
    
            for (hoh = config->Can_Arc_Hoh; hoh != NULL; 
    
                (hoh->Can_Arc_EOL ? hoh = NULL : hoh++))

     好处是不需要额外定义变量,顺便可以判断第一个元素是否为NULL,即数组为空也可以成立,但这个只能看做是一个副作用,而不能作为好处。

    不好的地方是相比第一种方法多了一个判断。

    如何选择呢?见仁见智。

    最后代码变成了如下形式:

    #define for_each_HOH(hoh, config) 
    
            for (hoh = config->Can_Arc_Hoh; hoh != NULL; 
    
                (hoh->Can_Arc_EOL ? hoh = NULL : hoh++))
    
     
    
    const Can_HardwareObjectType* hoh;
    
    for_each_HOH(hoh, canHwConfig) {
    
        ///do something
    
    }

    简单明了,无论是五六处,还是十几处,只需要for_each_HOH即可,数组遍历细节被屏蔽。

    溯源

    到这里我们可以看到,这个问题其实很简单:

    1. 最基本的数组遍历;
    2. 每个数组元素包含一个标识符,标识自己是否最后一个元素。

    只能根据前一个元素的标识符来判断是否还有下一个元素需要遍历。

    步骤如下:

    1. 拿到一个数组元素;
    2. Do something;
    3. 是否最后一个元素,如果是,结束;
    4. 如果不是,重复1

    遍历数组最简单的办法是知道数组元素的个数,然后 for (i=0; i < NUMBER; i++) 遍历即可。

    问题在于“数组大小”是否固定,如果不固定,需要约定一个存放的位置。这里的数组大小应该是不固定的。

    能否使用 ARRAY_SIZE 宏,即 sizeof(array)/sizeof(array[0]) 来获取数组大小?

    这里不可以,结构体定义时是一个指针,而非数组。

    另外即使是数组,也有可能出问题。因为AUTOSAR中配置有可能是POST-BUILD配置,编译时并不知道大小和位置。

    PS.

    for_each_HOH(hoh, canHwConfig)

    for_each_HOH宏定义中,需要带hoh参数,即便C99可以在for头的括号中声明变量。不然在使用for_each_HOH时,hoh变量无法索引到,所以还是在使用for_each_HOH的位置声明hoh变量为佳。

  • 相关阅读:
    周4早上搜索引擎分析 crmim.com| MSCRM开发者之家
    Bat命令学习
    sqlserver日期函数
    ubunto应用软件
    sql for xml
    win7x64 连接oracle 客户端 vs 2010调试 提示“ORA12154: TNS: 无法解析指定的连接标识符 ”ORA06413 问题(转)
    CentOS Rsync服务端与Windows cwRsync客户端实现数据同步
    怎么引导2岁孩子洗手问题
    Libnfcinstallation
    Asterisk资料
  • 原文地址:https://www.cnblogs.com/wjcdx/p/8987730.html
Copyright © 2011-2022 走看看