码迷,mamicode.com
首页 > 其他好文 > 详细

有一次接口设计

时间:2018-07-28 21:58:15      阅读:187      评论:0      收藏:0      [点我收藏+]

标签:and   nbsp   占用   分配   未来   col   内存分配   相互   解决   

小李最近手头在做的task,需要暴露新的接口出去给客户。

========================我是正文分割线=============================

<<<<<<<需求>>>>>>>

----------------------------------------------------------------------------------------------------------

需要暴露一个汽车特征点的接口,输入是一张图像,输出是汽车上的特征点,landmark。

----------------------------------------------------------------------------------------------------------

so easy?输入基本不用管,输出那就定义一个结构体不就完事了吗?假设这一代的算法支持一辆车100个ladmark点。over了。

版本1

typed struct LM

{

uint x[100];

uint y[100];

}

 

方法很简单,问题很明显:

1.如果算法升级了,支持的特征点数变多了,怎么破?直接把点数写死是不是不太好?

2.将来怎么扩展?如果需要支持更多的东西,比如车的颜色,型号等等呢?

 

好,所以小李就跑去跟同事讨论,最后得到了一个高灵活度的接口

版本2

typedef struct LM

{

struct Header

     {

          uint LMCount;

          uint x_offset; //4 bytes for each x

          uint y_offset; //4 bytes for each y

     }

     UINT   nValue;

}

定义了一个头,一个数据段,在header里面指定了landmark的个数,然后是x坐标相对于数据段起始地址的偏移量,这样,用户在调用接口的时候,拿到这个结构之后,一看,哦,有100个特征点,然后从nValue开始第0x00就是x的坐标,一个x占用4 bytes,一共去拿100个x,嗯,y也是一样的,就结束了。

小李觉得不错,你看,将来不管算法是支持多少个特征点,都不会存在兼容性的问题,蛮好蛮好,就提交给老板了。

老板一看,小李你这搞得啥玩意,根本不知道这个x和y是在内存中怎么layout的,不就是个xy坐标吗,整的这么复杂,我都看不懂用户怎么看得懂,打回去,让小李搞个简单点的。

 

郁闷的小李又开始苦思冥想,不让用offset的方式,又不能把数量写死,那就这样吧

版本3

typed struct LM

{

    uint reserved[4];

    struct LMdata

    {

        uint x;

        uint y;

    }data[1];

}

让用户通过一个新的接口先去拿一下landmark的数量,然后拿到数量后,分配内存,之后用户拿到这个结构体之后,就可以去拿[x,y]了。这样,数量没有写死,结构清晰,还加了一个reserved,便于后续扩展。

老板觉得小李的方法不错,然后reserved[4]的可扩展性有限,建议改成指针,于是就有了版本4

版本4

typed struct LM

{

    struct LMdata

    {

        uint x;

        uint y;

    }data[1];

   void* future;

}


小李和老板都没有看出这个结构体有啥问题,就决定找技术老王来看看,没啥问题就done了。

老王很快指出了4个问题

1.当用户在使用这个结构体的时候,定义LM plandmark,那么plandmark.future是啥?应该是第二个LMdata,而不是真正的furture,这样的定义一定是不可以接受的

2.这个结构体用户在拿去用的时候,不知道是什么样的layout,不知道lmdata究竟又多少个,这个结构体本身不独立

3.在用户和算法的dll之间传数据的时候,future是作为一个指针存在的,那么用户的这个指针是在用户的进程里面有效的,如果我们这个dll不跟用户在同一个进程里面,那这个指针传递是很不靠谱的

4.void在32位系统里面是4个byte,在64位系统里面是8个byte,如果恰好是app和dll之间的位数不一致,那么对于这个地址的解析也会不一样,肯定是有大问题的

 

小李傻了,原来把一个指针引入到接口的结构体里面有这么多的问题。啥也不说了,开始改吧。

1好说,直接把future指针放到结构体开头就好了;2也好说,加一个LMcount就行,3的话目前是可以保证的,4的话可以用void64解决

版本5

typed struct LM

{

pvoid64 future;// provide by user, numm for now

uint LMcount;

    struct LMdata

    {

        uint x;

        uint y;

    }data[1];

}

这个结构体出来之后,做linux的小林路过看了一眼,pvoid64这个在linux里面并没有定义,作为一个跨OS的接口,这样显然不合适

小李(几乎崩溃,思维不清):那则么办啊,用unsigned long long *可以吗?

小林:当然不行了,unsigned long long*是什么意思啊,他指的是指向的是一个long long类型的变量,但是指针本身的长度不变啊,32位里面是32位,64位里面是64位。

小李:不是把,让我想想。。。。我们为什么一定要用void指针啊?

小林:void指针又叫做无类型指针,可以通过强转转成其他类型的指针,这个pvoid和pvoid64其实是说指针的长度,像这种定义其他的类型都是做不到的。

小李:oh my gosh,为啥linux里面没有这么好用的pvoid64?那现在怎么办?

小林:只好把指针定义为unsigned long long类型,用的时候再转成指针了

版本6

typed struct LM

{

unsigned long long future_ptr;// provide by user, numm for now

uint LMcount;

    struct LMdata

    {

        uint x;

        uint y;

    }data[1];

}

小李,老板,老王, 小林一起review了这个接口,终于通过了。

 

=================我是干货==========================

1.定义这种结构体需要考虑存储的有效性,即structure尽量是4个bytes对齐,剩下的可以用reserved去填充

2.基本的要求是尽量直接传数据,然后用户能够清晰的知道结构的的layout,不能太复杂;用户有方式知道分配多大的buffer,然后拿到这个Buffer之后能够简单的解析,比如A.b需要就是指向b的,不能有歧义,所以可扩展大小的放在结构体最后,之后不要再加其他的了

3.可扩展性,如果有些参数随着算法升级会有变化,就需要考虑可扩展性

4.API中不能出现pviod这种含混不清的字眼,极有可能在32位和64位相互调用的时候出错

5.结构体的独立性,用户拿到这个结构体就可以开始解析,不需要借助其他的接口再去拿什么值

6.跨平台的接口,考虑linux,比如pvoid64这个在linux里面就没有,要使用常见的类型

7.使用指针要慎重!!!!要考虑是否能保证在一个进程里面

 

 

====================我是花絮========================

review完后,小李心想,这个接口虽然定义了这么久,但是其实定义的并不好,老王之前提到的不在同一个进程当中,就是一个很严重的潜在问题,所以在未来的接口定义中尽量不要使用指针。

小李上网查了查别人定义的接口,发现在java下定义接口好简单啊,不用考虑内存分配的问题,C++果然需要考虑的很多。在c++下有什么好的解决这种类似问题的方法吗?学习一下微软,发现可以采用定义version的方法,其实每次根据这个version就可以知道是算法的第几个版本,然后用不同的struct去转换,这个方法应该是比较好的。

typed struct LM

{

    struct header

    {

        uint version;

        uint size;

    }

    uint data;

}

 

typed struct LM_1

{

    struct header

    {

        uint version;

        uint size;//read only

    }

    UINT  type;

    UINT  position;//add whatever future feature here

    struct LMdata

    {

        uint x;

        uint y;

    }data[100];//could be 200 in v2, 300 in v3, as you wish :)

}

在code里面这样转换就好了

if version == 1

(LM_1*) plm = (LM_1*)p_landmark

else if .....

===============我是彩蛋=======================

有一次接口设计

标签:and   nbsp   占用   分配   未来   col   内存分配   相互   解决   

原文地址:https://www.cnblogs.com/sunny-li/p/9383331.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!