搜索
bottom↓
楼主: Gorgon_Meducer

[交流][专题]再谈菜单技术 2018-03-14 Update

  [复制链接]

出0入296汤圆

 楼主| 发表于 2018-3-26 19:18:21 来自手机 | 显示全部楼层
gamep 发表于 2018-3-26 18:18
UBOUND(c_tTopMenuItems),                            //!< menu item count

是这个吗?

不是这个。如果你不知道定义,我回头补上去。

  1. #define UBOUND(__ARRAY)       (sizeof(__ARRAY)/(sizeof(__ARRAY[0])))
复制代码

出0入0汤圆

发表于 2018-3-27 08:08:05 | 显示全部楼层
Gorgon_Meducer 发表于 2018-3-26 19:18
不是这个。如果你不知道定义,我回头补上去。

知道这种用法,没搜索到这个定义,还以为故意留的,我想简单了。

出0入0汤圆

发表于 2018-3-28 09:33:24 | 显示全部楼层
Gorgon_Meducer 发表于 2018-3-26 19:18
不是这个。如果你不知道定义,我回头补上去。

老师你好!!!!你这个思想好像和这个思想一致 。不知道我分析的对不对  发不了链接 等级不够  搜索  LCD菜单程序(多层菜单)

出0入0汤圆

发表于 2018-3-28 17:28:02 | 显示全部楼层
const menu_t c_tTopMenu = {
    (menu_item_t *)c_tTopMenuItems,                                    //!< menu item list
    UBOUND(c_tTopMenuItems),                            //!< menu item count
    NULL,                                               //!< top menu has no parent
    top_menu_engine,                                    
};

最后一个逗号是不是不需要

出0入296汤圆

 楼主| 发表于 2018-3-28 20:42:33 来自手机 | 显示全部楼层
gamep 发表于 2018-3-28 17:28
const menu_t c_tTopMenu = {
    (menu_item_t *)c_tTopMenuItems,                                    / ...

可以不要,但留下不错。
很细心,但也不是这个问题。

出0入296汤圆

 楼主| 发表于 2018-3-28 20:43:08 来自手机 | 显示全部楼层
wendajie123 发表于 2018-3-28 09:33
老师你好!!!!你这个思想好像和这个思想一致 。不知道我分析的对不对  发不了链接 等级不够  搜索  LC ...

很开心我们的想法一致。

出0入0汤圆

发表于 2018-3-28 21:43:49 | 显示全部楼层

出0入0汤圆

发表于 2018-3-29 10:35:07 | 显示全部楼层


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2018-3-29 13:31:33 | 显示全部楼层
膜拜大神,又学习了

出0入0汤圆

发表于 2018-3-29 14:15:46 | 显示全部楼层
#define __declare_menu_item_template(__NAME)                                    \
    typedef struct __##__NAME __NAME;
#define declare_menu_item_template(__NAME)                                      \
        __declare_menu_item_template(__NAME)


请问大师,这种预定义为什么要用两个预定义实现??

#define declare_menu_item_template(__NAME)                                    \
    typedef struct __##__NAME __NAME;

直接这样写一个不好吗??

出0入296汤圆

 楼主| 发表于 2018-3-29 21:11:10 | 显示全部楼层
本帖最后由 Gorgon_Meducer 于 2018-3-29 21:15 编辑


前两个个都木有说对,最后一个回答正确,鼓掌。然鹅,还有一个!
更新楼主位。

出0入296汤圆

 楼主| 发表于 2018-3-29 21:13:40 | 显示全部楼层
gamep 发表于 2018-3-29 14:15
#define __declare_menu_item_template(__NAME)                                    \
    typedef struct ...


直接写不好。如果你填写的内容里还包涵别的宏,直接写就坑了……

出0入0汤圆

发表于 2018-3-29 23:01:34 来自手机 | 显示全部楼层
佩服,学习了

出0入0汤圆

发表于 2018-3-30 10:24:55 | 显示全部楼层
Gorgon_Meducer 发表于 2018-3-28 20:43
很开心我们的想法一致。

老师你看了吗?那个架构

出0入0汤圆

发表于 2018-3-30 10:33:19 | 显示全部楼层
switch(this.tState) {

里面没有

default:


不知道算不算?

出0入0汤圆

发表于 2018-3-30 10:39:57 | 显示全部楼层

强帖收藏学习

出0入296汤圆

 楼主| 发表于 2018-3-30 21:33:10 来自手机 | 显示全部楼层
wendajie123 发表于 2018-3-30 10:24
老师你看了吗?那个架构

你觉得呢?

出0入296汤圆

 楼主| 发表于 2018-3-30 21:33:32 来自手机 | 显示全部楼层
gamep 发表于 2018-3-30 10:33
switch(this.tState) {

里面没有

呃……不算吧

出0入50汤圆

发表于 2018-3-30 22:07:15 来自手机 | 显示全部楼层
楼主说的这种管理技术的模式,让我想起当前国内排名第一的台湾大润发超市的人员储备制度,他们领导层管理最牛逼也最让下属害怕的,就是谁走了马上有人顶上,有多人也是有足够人员顶,除非集体罢工,^_^楼主也道出了国内很多人忽略的一个严重问题:很多公司的设计永远在夯地基,前面的人造了一层楼,后面的推倒再造一遍,一直在低层次上重复劳动,GDP是很多,但是高度上进展小。给楼主点赞!

出0入296汤圆

 楼主| 发表于 2018-3-31 11:26:48 来自手机 | 显示全部楼层
ilikemcu 发表于 2018-3-30 22:07
楼主说的这种管理技术的模式,让我想起当前国内排名第一的台湾大润发超市的人员储备制度,他们领导层管理最 ...

兄弟是明眼人,其中的哀叹和绝望想必你也能体会吧。

出0入50汤圆

发表于 2018-3-31 15:37:25 | 显示全部楼层
Gorgon_Meducer 发表于 2018-3-31 11:26
兄弟是明眼人,其中的哀叹和绝望想必你也能体会吧。

因为曾经在日企呆过几年,开始的时候,日本人的一些做法让人觉得匪夷所思,感觉非常呆板,死脑筋,死教条,一点都不灵活。

后来,一个偶然的机会,听了或者说看了一部台湾余世维的《成功经理人》的讲座,里边就是讲到日本公司训练新人的做法,终于豁然开朗,虽然表面看起来日本人的效率很低,但是从长远看,他们做的每一件事,基本上都会有一个积累,经验、技术、管理,等等各方面,都会有积累,而且这些是一个可以传承继承的积累,前面的人在一个事情上看似非常低效率工作了很长时间的结果,后面的人可以毫无怀疑直接拿来用,并且会一直用下去,直到有更好的替代。这样的优点是前面的低效投入,在未来的很长的时间里一直在产生效益,在这个效益消失之前所产生的总效益,和前面看似傻乎乎的低效率投入做一个比较,其投入产出比远远高于自以为聪明的国人的那种所谓灵活机动!

日本人就是这表面的低效率,实际的高效率,太特么可怕了。以现在国人的思维,想干过日本人,简直是看不到希望。

出0入296汤圆

 楼主| 发表于 2018-3-31 17:07:59 | 显示全部楼层
ilikemcu 发表于 2018-3-31 15:37
因为曾经在日企呆过几年,开始的时候,日本人的一些做法让人觉得匪夷所思,感觉非常呆板,死脑筋,死教条 ...

是这样的。但故事有后续,关于日本企业的这种做法,其实长久以来一直有一种怀疑的声音,
也就是,前期的低效率是否有办法改进?答案是肯定的,现在欧美企业很多用的就是改进版
本,使得日本企业这种做法的改进版得到充分应用,且前期的效率问题也得到了充分的改善。

出0入0汤圆

发表于 2018-4-1 07:34:41 来自手机 | 显示全部楼层
牛B杠杠的

出0入0汤圆

发表于 2018-4-2 10:56:23 | 显示全部楼层

。。。应该看了吧  !!!!!!只是想确定下思路框架是不是一样

出0入0汤圆

发表于 2018-4-2 11:30:39 | 显示全部楼层
字数补丁

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入296汤圆

 楼主| 发表于 2018-4-2 17:10:40 | 显示全部楼层

呃,并不是这个问题,我这个格式是正确的。

出0入0汤圆

发表于 2018-4-4 14:45:13 | 显示全部楼层
本帖最后由 chenguanghua 于 2018-4-4 15:37 编辑
  1. struct __default_menu_item_t   {
  2.     //! inherit from base class menu_item_t
  3.     menu_item_t;
  4.     //! depends on your application, you can add/remove/change following members
  5.     char                *pchTitle;                      //!< Menu Title
  6.     char                *pchDescription;                //!< Description for this menu item
  7.     char                chShortCutKey;                  //!< Shortcut Key value in current menu
  8. };
复制代码


①此处继承基类直接这样写: menu_item_t;
没这么用过,请教学习一下

②另外一处不明白的地方:
结构体定义含有chShortCutKey,初始化为何不用这项
  1. default_menu_item_t c_tTopMenuItems[] = {
  2.     {
  3.         top_menu_item_a_handler,
  4.         NULL,                                           //!< child menu
  5.         "Top Menu A",
  6.         "This is Top Menu A",
  7.     }                                          
  8. };
复制代码

出0入296汤圆

 楼主| 发表于 2018-4-4 17:16:02 | 显示全部楼层
本帖最后由 Gorgon_Meducer 于 2018-4-4 17:17 编辑
chenguanghua 发表于 2018-4-4 14:45
①此处继承基类直接这样写: menu_item_t;
没这么用过,请教学习一下


关于匿名结构体的使用,我在这个帖子里有详细讨论

结构体的初始化应该是基础知识了,省略的部分会被自动初始化为0——因为例子中我确实没有用到这个内容,所以就用默认的0了。

出0入0汤圆

发表于 2018-4-4 18:56:19 | 显示全部楼层
Gorgon_Meducer 发表于 2018-4-4 17:16
关于匿名结构体的使用,我在这个帖子里有详细讨论

结构体的初始化应该是基础知识了,省略的部分会被自动 ...


惭愧,好几年没写程序了,对您的菜单架构比较感兴趣,还请不吝赐教
另外一个问题:.ptCurrentMenu  这是什么用法,特别是这前面的这个点(.)
  1. static menu_engine_cb_t s_tMenuDemo = {
  2.     .ptCurrentMenu = &menu(TopMenu),
  3. };
复制代码

出0入0汤圆

发表于 2018-4-4 19:04:49 | 显示全部楼层
Gorgon_Meducer 发表于 2018-3-15 06:26
故意埋了一个不影响编译和功能的笔误在里面,看看谁真正的读了代码。

大神,多了一个#endif算不算?

出0入0汤圆

发表于 2018-4-4 19:59:04 来自手机 | 显示全部楼层
收藏学习,感谢大师

出0入296汤圆

 楼主| 发表于 2018-4-4 21:27:04 | 显示全部楼层
擦鞋匠 发表于 2018-4-4 19:04
大神,多了一个#endif算不算?

哪里多了?求指出,如果有,估计是笔误,我会更正。

出0入296汤圆

 楼主| 发表于 2018-4-4 21:29:58 | 显示全部楼层
chenguanghua 发表于 2018-4-4 18:56
惭愧,好几年没写程序了,对您的菜单架构比较感兴趣,还请不吝赐教
另外一个问题:.ptCurrentMenu  这是 ...

这是C99(如果我没有记错的话)引入的一种初始化结构体的方法……这种方法甚至可以用来初始化数组——也就是你可以指定只初始化哪个数组元素。

结构体初始化了解一下。

出0入0汤圆

发表于 2018-4-5 07:07:30 | 显示全部楼层
  1. struct __default_menu_item_t   {
  2.     //! inherit from base class menu_item_t
  3.     menu_item_t;
  4.     //! depends on your application, you can add/remove/change following members
  5.     char                *pchTitle;                      //!< Menu Title
  6.     char                *pchDescription;                //!< Description for this menu item
  7.     char                chShortCutKey;                  //!< Shortcut Key value in current menu
  8. };

  9. struct __menu {
  10.     menu_item_t        *ptItems;                        //!< menu item list
  11.     uint_fast8_t        chCount;                        //!< menu item count
  12.     menu_t             *ptParent;                       //!< parent menu;
  13.     menu_engine_t      *fnEngine;                       //!< engine for process current menu
  14. };
复制代码

__menu作为容器, ptItems的定义采用default_menu_item_t是不是比较好?
当然menu_item_t也没问题,因为 pchTitle 这些不是必须的。

出0入296汤圆

 楼主| 发表于 2018-4-5 17:12:00 | 显示全部楼层
chenguanghua 发表于 2018-4-5 07:07
__menu作为容器, ptItems的定义采用default_menu_item_t是不是比较好?
当然menu_item_t也没问题,因为 p ...

如果使用default_menu_item_t在menu_t中,那么menu_t就失去了基类的扩展性——因为绑定死了某一个派生类,
如果别人实现了自己的xxxxx_menu_item_t类型,是不是就没法正确使用menu_t了?(因为假设按照你的建议,
menu_t里面使用了default_menu_item_t而不是 menu_item_t)

出0入0汤圆

发表于 2018-4-5 23:44:44 | 显示全部楼层
Gorgon_Meducer 发表于 2018-4-5 17:12
如果使用default_menu_item_t在menu_t中,那么menu_t就失去了基类的扩展性——因为绑定死了某一个派生类 ...


请教default_menu_item_t和menu_t的问题,其实是看到这块代码,结构体指针的强制类型转换
如果要使用pchTitle,pchDescription这些成员,结构体指针的强制类型转换会不会出现数据访问的不安全性?
  1. static menu_engine_cb_t s_tMenuDemo = {
  2.     .ptCurrentMenu = &menu(TopMenu),
  3. };
复制代码

出0入296汤圆

 楼主| 发表于 2018-4-6 03:39:00 来自手机 | 显示全部楼层
chenguanghua 发表于 2018-4-5 23:44
请教default_menu_item_t和menu_t的问题,其实是看到这块代码,结构体指针的强制类型转换
如果要使用pch ...

具体举个例子?

出0入0汤圆

发表于 2018-4-6 07:14:54 | 显示全部楼层
本帖最后由 chenguanghua 于 2018-4-6 10:20 编辑
  1. 转:
  2. 比如在一个项目中,有大量的数据结构,他们都是双向链表,但又想共用一套对链表的操作算法,这怎么做到呢,C中又没有C++中的继承,不然我可以继承一父(类中只有两个指针,一个向前一个向后),而其算法可以写在你类中的虚函数中,供子类使用。如:

  3. class Links
  4. {
  5. public:
  6.     Links* back;
  7.     Links* forword;

  8.     virtual Add(){ ... };
  9.     virtual Del(){ ... };
  10.     virtual Ins(){ ... };
  11.     virtual Print() =0;
  12.     ....
  13. };

  14. 于是对于特定的数据结构我们可以:
  15. class mylinks : public Links
  16. {
  17. public:
  18.     char* myname;
  19.     char sex;
  20.     int  age;
  21.     ...
  22.     virtual Print(){ .... }
  23. };

  24. 对其操作时都可以使用你类的泛型算法。
  25. 在C中,该如何做呢?我们用C中的指针和强制类型转可以做到。

  26. 下面是我总结出来的一个小的程序,体现了用指针的弹性来实现这一继承的效果:
  27. (我在Liniux下的GCC调试通过)

  28. #include <stdio.h>

  29. /* 双向链表 (类似于父类)*/
  30. typedef struct hLinks{
  31.    struct hLinks *bwLink;
  32.    struct hLinks *fwLink;
  33. } hLinks;

  34. /*
  35. * 一个使用双向链表的结构
  36. *     (类似于子类)
  37. */
  38. typedef struct hEnt{
  39.    hLinks links;
  40.    int hData;
  41.    char key[10];
  42. } hEnt;

  43. /*
  44. *  双向链插入泛型算法 (类似于父数中的成员函数)
  45. */
  46. Insert( hLinks* elem, hLinks *dest )
  47. {
  48.    if ( !elem || !dest ) return;

  49.    elem->bwLink = dest->bwLink;
  50.    elem->fwLink = dest;
  51.    dest->bwLink->fwLink = elem;
  52.    dest->bwLink = elem;
  53. }

  54. /*
  55. *  打印 (类似于子类重载父类的成员函数)
  56. */
  57. PrintLink( hLinks *h )
  58. {
  59.     hEnt *p ;

  60.     for( p = ( hEnt* ) h->fwLink;     /*  <-----------把hLink再转回来  */
  61.          p != ( hEnt* ) h;
  62.          p = ( hEnt* )( (hLinks*)p )->fwLink )
  63.     {
  64.        printf("hData=[%d], key=[%s]/n", p->hData, p->key);
  65.     }
  66. }

  67. main()
  68. {
  69.    hLinks head;
  70.    hEnt a[4];
  71.    int i;

  72.    head.bwLink = &head;
  73.    head.fwLink = &head;

  74.    for(i=0;i<4;i++)
  75.    {
  76.       a[i].hData = i*10;
  77.       sprintf(a[i].key,"id=%d", i);

  78.       /*  使用泛型算法构造双向链  */
  79.       Insert( (hLinks *) &a[i], (hLinks *) &head ); /*   <-----注意这个强制转换  */
  80.    }

  81.    PrintLink( (hLinks *) &head ); /*   <-------也注意这个强制转换  */
  82. }
复制代码

出0入0汤圆

发表于 2018-4-6 07:29:43 | 显示全部楼层
本帖最后由 chenguanghua 于 2018-4-6 10:21 编辑


可以看出,在前两个内存单元中两个结构体存储的内容是相同的,当然不管相不相同计算机是不管的,
当hLinks类型转换成hEnt类型时,计算机就将原结构体看做是hEnt类型的。
转换后的hEnt类型结构体的前面两个内存单元的内容就是hLinks中的前两个单元内容,
而hEnt的后两个内存单元中的内容取得是hLinks后的两个单元(这两个单元不是hLinks类型的成员,而是别的内容,所以如果转换后的hEnt要访问hData和key的话是不安全的!)。

/*----------------------------------------*/
续:对宏的定义不熟悉,理解错了
/*----------------------------------------*/

如果在default_menu_engine想通过指针访问pchDescription这些成员,是否可以通过结构体指针进行强制类型转换得到?
比如:
LCD_disp(((default_menu_item_t *)(this.ptCurrentMenu->ptItems[this.chCurrentItemIndex]))->pchDescription);                  

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2018-4-6 08:16:09 | 显示全部楼层
学习,学习

出0入296汤圆

 楼主| 发表于 2018-4-6 17:53:12 | 显示全部楼层
本帖最后由 Gorgon_Meducer 于 2018-4-6 18:29 编辑
chenguanghua 发表于 2018-4-6 07:29
可以看出,在前两个内存单元中两个结构体存储的内容是相同的,当然不管相不相同计算机是不管的,
当hLinks ...


我明白你的意思了,其实这里涉及到一个本质上的问题:C语言不是 Type-Safe 的,对于强制类型转换,如果不在使用时
加以一定的规则限制,否则无论如何都会出现你担心的问题。实际上,规则很简单:

规定,强制类型转换只能从派生类向基类进行,反之则禁止——除非你对传入指针的类型非常确信(然而这一点通常是很
难做到的)。

在这个例子中,default_menu_item_t就是 menu_item_t 的派生类,因此,用menu_item_t 来访问仅属于 menu_item_t
的成员是安全的;同时绝对禁止通过 menu_item_t 类型的指针经过强制类型转化以后来访问派生类的成员。

如果你看我另外一个帖子,就会知道,其实还有另外一个安全的做法:
https://www.amobbs.com/thread-5582609-1-1.html?_dsign=4d743ffc

注意这里:



  1. //! \brief macro for inheritance

  2. #define INHERIT_EX(__TYPE, __NAME)  \
  3.             union {                 \
  4.                 __TYPE  __NAME;     \
  5.                 __TYPE;             \
  6.             };

  7. /*! \note When deriving a new class from a base class, you should use INHERIT
  8. *        other than IMPLEMENT, although they looks the same now.
  9. */
  10. #define INHERIT(__TYPE)             INHERIT_EX(__TYPE, base__##__TYPE)

  11. /*! \note You can only use IMPLEMENT when defining INTERFACE. For Implement
  12. *        interface when defining CLASS, you should use DEF_CLASS_IMPLEMENT
  13. *        instead.
  14. */
  15. #define IMPLEMENT(__INTERFACE)      INHERIT_EX(__INTERFACE, base__##__INTERFACE)

  16. /*! \note if you have used INHERIT or IMPLEMENT to define a CLASS / INTERFACE,
  17.           you can use OBJ_CONVERT_AS to extract the reference to the inherited
  18.           object.
  19.   \*/
  20. #define OBJ_CONVERT_AS(__OBJ, __INTERFACE)  (__OBJ.base__##__INTERFACE)

复制代码


当我们要访问基类/接口时(这里支持多重继承),使用  OBJ_CONVERT_AS(__OBJ, __INTERFACE) 宏,而不是
直接强制类型转换,这种方法就从根本上避免了由 强制类型转换带来的入侵问题——也就是说:

我们要尽可能在C语言的面向对象实践中遵守  Type-Safe 的规则。

进一步说,就是借由  INHERIT() 和 IMPLEMENT() 宏实现的接口/类继承,我们在访问时也要通过原有类型来进行,
而不是强制类型转换——这就是OBJ_CONVERT_AS() 存在的本意——当然,支持多个接口也是用途之一。

另外,对于你说的 链表操作,你提到了C++的泛型,其实C的宏配合一定代码模板实现出来的效果是一样的:

比如你看这个链表操作的例子:

https://github.com/GorgonMeducer ... s/template/t_list.h

比如,你看这个堆的泛型(C source code + macro),就是用了上述链表进行实现的,同时这个堆的泛型还能根据不同的目标类型进行专用堆类的生成。

https://github.com/GorgonMeducer ... ervice/memory/epool

在上述的例子中,你可以注意到,我遵守了“只允许将派生类向基类进行强制类型转换”的原则——在派生类中,指针都是指向派生类,而不是
基类的,这就避免了出现你说的问题。

关于C语言的面向对象实践,欢迎进行讨论。


更进一步说,在C语言中构建 type-safe 的面向对象实践,当年深入浅出MFC给出过详细的分析,简单说就是在每一个抽象类的结构体里,
第一个成员都会包含一个指向最基础的基类的指针,又叫 type 类,大体定义如下:


  1. typedef const struct __type_t type_t;
  2. struct {
  3.     type_t *ptBase;                                           //!< 类型的继承链
  4.     const char *chTypeName;
  5.     void *constructor(void *, ...);                          //!< 构造函数
  6.     void *destructor(void *);                                //!< 析构函数
  7. };
复制代码



为了让这一工作变得更自然,通常会定义一个object类:

  1. typedef struct {
  2.     type_t   *ptType;
  3. } object_t
复制代码


基于这一结构,所有的 abstract 类 都会 继承object 类,而所有abstract 类的派生类则无需这么做,只需要将object类的ptType指针重新初始化即可。

可以看出,type是借助 type_t 静态成员实现的一个链表,这个链表在编译时刻完成初始化。借助这个链表,我们可以实现  is_type_of() 这样的操作,甚至还能借助获取
type类型实现动态的构造和析构,如果你更进一步的对 type_t 进行继承和派生,甚至可以把派生类的虚函数表一起放到派生出来的 xxxx_type_t
里面(也就是放在constructor和destructor后面)……

最后的最后,借助 is_type_of ,我们能完成最基本的 type-safe 的检查——如果你觉得很有必要的话——这只不过是一个简单的指针判断而已。

说了这么多,其实已经把C++的物理层面的实现说的差不多了……这种时候,如非必要,真心不如直接用C++。我觉得没有必要,所以只做了很基本
的事情——制定规则,遵守规则,尽可能不乱用强制类型转换。



最后针对你的具体问题:

如果在default_menu_engine想通过指针访问pchDescription这些成员,是否可以通过结构体指针进行强制类型转换得到?
比如:
LCD_disp(((default_menu_item_t *)(this.ptCurrentMenu->ptItems[this.chCurrentItemIndex]))->pchDescription);                  

答案是:不行,因为default_menu_engine 只知道什么是menu_item_t,并不知道default_menu_item_t的存在,因此不应该出现基类的成员函数访问
派生类的成员变量的问题。

如果你要访问default_menu_item_t,那么理论上你应该有一个针对 default_menu_item_t 的 menu_engin函数,由于menu_t 是 menu_item_t 的容器,
menu_t 里面有一个指针指向专门针对当前容器内 menu_item_t 的对应 menu_engine(这就是面向对象的意义,数据和方法打包),因此在当前的menu
下,用专用的menu_engine处理对应的 xxxx_menu_item_t 是肯定安全的。楼主位的例子中, default_menu_engine只是一个范例,它展示了基本的菜单
导航操作的处理,你可以注意到,里面连 显示函数都没有调用,所以它真的只是一个伪代码——用来做demo演示这个菜单的数据结构如何工作的。

出0入4汤圆

发表于 2018-4-9 16:28:27 | 显示全部楼层
虽然不合时宜,我还是想说全文我只关注到了小姐姐一段。

出0入0汤圆

发表于 2018-4-9 16:33:45 | 显示全部楼层
厉害兄弟!

出0入0汤圆

发表于 2018-4-9 19:55:41 | 显示全部楼层
收藏 学习

出0入0汤圆

发表于 2018-4-9 21:23:56 来自手机 | 显示全部楼层
哎 水平不行 各种不懂  都是裸奔加状态机  标志  都是随意定义只要自己看得懂

出0入296汤圆

 楼主| 发表于 2018-4-10 18:55:20 来自手机 | 显示全部楼层
lqzhw 发表于 2018-4-9 21:23
哎 水平不行 各种不懂  都是裸奔加状态机  标志  都是随意定义只要自己看得懂 ...

这也是裸奔加状态机啊。哪里看不懂,我给你详细说说。

出0入0汤圆

发表于 2018-4-10 22:22:00 | 显示全部楼层
大神的东西就是精辟

出0入0汤圆

发表于 2018-4-11 17:58:18 | 显示全部楼层
请问大师,在多级菜单中,想实现,在任意菜单,长按菜单键返回最顶层菜单。
怎么实现才比较优雅呢?

QP的方案是用层次状态机做类似设计。但是,QP相关的组件相对少些。
很想听听大师对层次状态机的讲解和意见。

另外,本主题中讲到的菜单都包含菜单项。在实时数据显示界面,就是数据在更新,
好像不含菜单项。是否就不属于本主题的讨论范围?

出0入296汤圆

 楼主| 发表于 2018-4-11 23:07:48 | 显示全部楼层
gamep 发表于 2018-4-11 17:58
请问大师,在多级菜单中,想实现,在任意菜单,长按菜单键返回最顶层菜单。
怎么实现才比较优雅呢?

你说的问题基本在93楼有讨论。
层次状态机不就是子状态机调用么……不知道你想知道啥……

长按回到顶层,抛开长按(按键扫描或者按键消息),回到顶层……不就是顺着父指针一路向上么……
你具体遇到什么问题了呢?

出0入0汤圆

发表于 2018-4-12 14:27:17 | 显示全部楼层
我再慢慢领悟

出0入0汤圆

发表于 2018-4-12 16:22:56 | 显示全部楼层
正在找牛逼的菜单程序,感谢感谢,有空试试

出0入0汤圆

发表于 2018-4-12 16:26:31 | 显示全部楼层
LZ开贴讲解,厉害啊!

出0入0汤圆

发表于 2018-4-12 16:29:55 | 显示全部楼层
有没有结合按键KEY与菜单menu的交换程序吗?

出0入296汤圆

 楼主| 发表于 2018-4-12 19:53:37 来自手机 | 显示全部楼层
hy2515131 发表于 2018-4-12 16:29
有没有结合按键KEY与菜单menu的交换程序吗?

看 default_menu_engine, 在楼主位

出0入0汤圆

发表于 2018-4-15 08:40:41 来自手机 | 显示全部楼层
Gorgon_Meducer 发表于 2018-4-12 19:53
看 default_menu_engine, 在楼主位

请教傻大侠:我想把结构体同时用于独立定义和结构体嵌套,并且嵌套时用匿名结构,应该怎么操作。
例如:
typedef struct {}childstr;
typedef struct {childstr cstr;}fatherstr;
此时嵌套的结构体不是匿名的,调用起来太麻烦。

出0入0汤圆

发表于 2018-4-15 09:05:16 | 显示全部楼层
Gorgon_Meducer 发表于 2018-4-12 19:53
看 default_menu_engine, 在楼主位

多谢多谢!

出0入0汤圆

发表于 2018-4-15 09:51:08 | 显示全部楼层
感觉菜单、GUI这些还是适合用面向对象的思路来写

出0入296汤圆

 楼主| 发表于 2018-4-15 21:17:38 | 显示全部楼层
gao_hailong 发表于 2018-4-15 08:40
请教傻大侠:我想把结构体同时用于独立定义和结构体嵌套,并且嵌套时用匿名结构,应该怎么操作。
例如:
...

答案在142楼里提到的几个宏,不明白再问。

出0入296汤圆

 楼主| 发表于 2018-4-15 21:18:30 | 显示全部楼层
wangyeqing333 发表于 2018-4-15 09:51
感觉菜单、GUI这些还是适合用面向对象的思路来写

没错,就是要用OO的思想

出0入0汤圆

发表于 2018-4-21 18:41:38 来自手机 | 显示全部楼层
赞。            

出0入0汤圆

发表于 2018-4-21 20:25:06 来自手机 | 显示全部楼层
Gorgon_Meducer 发表于 2018-4-15 21:17
答案在142楼里提到的几个宏,不明白再问。

直接用声明,不加变量名就行了,谢谢大侠。

出0入0汤圆

发表于 2018-4-21 20:48:45 | 显示全部楼层
多谢多谢!

出0入0汤圆

发表于 2018-5-16 11:32:43 | 显示全部楼层
常看常新

出0入0汤圆

发表于 2018-5-22 20:24:32 | 显示全部楼层
纽币,很幸运能够学习前辈的编程思想。

出0入4汤圆

发表于 2018-5-23 07:20:46 | 显示全部楼层
rtos下怎么玩

出0入0汤圆

发表于 2018-5-23 07:30:28 来自手机 | 显示全部楼层
好好学习,认真听课

出0入296汤圆

 楼主| 发表于 2018-5-23 18:40:42 | 显示全部楼层

没有区别啊……你的具体担心点在哪里?可以详细描述下你的疑惑么?

出0入4汤圆

发表于 2018-5-23 23:53:13 来自手机 | 显示全部楼层
任务循环里要加延时吗,延时多长

出0入296汤圆

 楼主| 发表于 2018-5-24 00:27:24 | 显示全部楼层
本帖最后由 Gorgon_Meducer 于 2018-5-24 00:33 编辑
ztrx 发表于 2018-5-23 23:53
任务循环里要加延时吗,延时多长



你说的任务循环是超级循环么?还是说在RTOS下任务里面的超级循环?
为什么要加延时?目的是什么?

如果加延时的目的是为了让出处理器时间给其它任务,我想说,这是很通用的多任务协作问题,跟菜单引擎没有任何关系。

认真说起来,在多任务环境下,应该是不需要你说的这个延迟的。因为多任务环境下,运行菜单的任务应该是受按键队列
驱动的——如果按键队列为空,则阻塞该任务,如果按键队列不为空,则读取按键并进行菜单操作——这里值得注意的是,
最好把显示和菜单任务分离开,菜单任务只是个后台骨架,而显示(尤其是你需要有动态效果的情况下)应该由一个独立的
任务来完成,这个任务接受来自菜单的消息来决定如何处理前台显示的问题。我觉得,如果说延时,恐怕是这个前台显示需
要延时——主要是处理类似gif的效果,或者是滚动字幕之类的。

从这个角度来说,负责菜单的任务是不需要任何延时的,它是完全由数据流(按键队列)驱动的。这是一个典型的生产者消
费者模型。

希望这个回答解决了你的疑问。如果还有什么问题,请提的具体一些。

出0入4汤圆

发表于 2018-5-24 07:50:29 来自手机 | 显示全部楼层
谢谢,学习 了

出0入0汤圆

发表于 2018-7-5 20:59:55 | 显示全部楼层
谢谢分享,收藏学习!

出0入0汤圆

发表于 2018-7-11 09:18:21 | 显示全部楼层
大师一出必是精品啊

出0入0汤圆

发表于 2018-7-11 12:49:21 | 显示全部楼层
感谢分享,学习了!

出0入0汤圆

发表于 2018-7-11 15:47:40 | 显示全部楼层
先收藏再细看

出0入0汤圆

发表于 2018-7-18 21:03:35 | 显示全部楼层
有个问题想请教一下,每个菜单内都有一个需要调整的参数,如何把按键的动作(加或者减)与菜单关联起来?有什么好的解决方案吗?

出0入4汤圆

发表于 2018-7-19 16:23:06 | 显示全部楼层
大师强帖必须顶!!!
数据结构真的很重要

出0入296汤圆

 楼主| 发表于 2018-7-19 18:00:27 | 显示全部楼层
cgbabc 发表于 2018-7-18 21:03
有个问题想请教一下,每个菜单内都有一个需要调整的参数,如何把按键的动作(加或者减)与菜单关联起来?有 ...

菜单只是个导航,你看到的所谓菜单里有个值要修改那是错觉。
要修改的值实际上是通过窗体里的控件来实现的,而真正的菜单只是帮你打开这个窗体而已。
如果你一直试图把菜单和窗体混淆来看……估计就没法降低菜单的耦合度和复杂度了。

出0入0汤圆

发表于 2018-7-19 19:23:09 | 显示全部楼层
Gorgon_Meducer 发表于 2018-7-19 18:00
菜单只是个导航,你看到的所谓菜单里有个值要修改那是错觉。
要修改的值实际上是通过窗体里的控件来实现 ...

好吧,我以前一都按菜单就是一个参数来看待,看来需要改变这种观念。还需要另外实现一个窗体的程序来实现调整参数。

出0入296汤圆

 楼主| 发表于 2018-7-20 18:40:28 | 显示全部楼层
cgbabc 发表于 2018-7-19 19:23
好吧,我以前一都按菜单就是一个参数来看待,看来需要改变这种观念。还需要另外实现一个窗体的程序来实现 ...

分开以后,你会发现更多模块化的机会。

出0入0汤圆

发表于 2018-7-23 19:06:31 | 显示全部楼层
看不明白,如果用emwin还要这么写吗?

出0入296汤圆

 楼主| 发表于 2018-7-24 17:53:49 | 显示全部楼层
R8C 发表于 2018-7-23 19:06
看不明白,如果用emwin还要这么写吗?

用已有的架构就不用这么写了。这个主要是从原理上帮助一些好奇的人,现代的比较先进的嵌入式GUI从原理上
说是如何处理类似的事情的——当然很多系统用了消息机制,不过本质上与这个结构并不冲突。

出0入0汤圆

发表于 2018-7-24 21:03:56 | 显示全部楼层
谢谢回帖,明白了

出0入0汤圆

发表于 2018-7-24 22:23:32 | 显示全部楼层
先收藏,慢慢消化

出0入0汤圆

发表于 2018-8-14 14:07:48 | 显示全部楼层
楼主菜单技术精湛,最近在研究开源遥控器,无线协议和摇杆混控都能看懂,但是菜单部分完全没有头绪,不知道楼主收能帮忙看看:https://github.com/DeviationTX/deviation/tree/master/src/gui
这个菜单还是很不错的,在开源遥控领域,Deviation也是老大哥,他的GUI貌似是为每一个Button注册回调,然后菜单怎么做的,就完全看不明白了。

出0入0汤圆

发表于 2018-8-14 14:08:46 | 显示全部楼层
Gorgon_Meducer 发表于 2018-7-24 17:53
用已有的架构就不用这么写了。这个主要是从原理上帮助一些好奇的人,现代的比较先进的嵌入式GUI从原理上
...

楼主,可以一起讨论下这个吗?

楼主菜单技术精湛,最近在研究开源遥控器,无线协议和摇杆混控都能看懂,但是菜单部分完全没有头绪,不知道楼主收能帮忙看看:https://github.com/DeviationTX/deviation/tree/master/src/gui
这个菜单还是很不错的,在开源遥控领域,Deviation也是老大哥,他的GUI貌似是为每一个Button注册回调,然后菜单怎么做的,就完全看不明白了。

出0入296汤圆

 楼主| 发表于 2018-8-14 21:06:15 | 显示全部楼层
ailibuli 发表于 2018-8-14 14:08
楼主,可以一起讨论下这个吗?

楼主菜单技术精湛,最近在研究开源遥控器,无线协议和摇杆混控都能看懂, ...

没有认真看,实在没有时间,但我猜它所谓的菜单只是一个窗体吧……既然给每个按钮都加了回调,那更有可能就只是一个普通的窗体了。

出0入0汤圆

发表于 2018-8-15 17:31:03 | 显示全部楼层
好吧,看到和大神的差距了

出0入0汤圆

发表于 2018-8-17 09:09:01 | 显示全部楼层
这个菜单结构帮助了不少人

出0入0汤圆

发表于 2018-8-23 17:31:20 | 显示全部楼层
后排听课,瞻仰大神的帖子

出0入0汤圆

发表于 2018-9-2 00:25:21 | 显示全部楼层
标记  复制+粘贴

出0入0汤圆

发表于 2018-9-28 22:37:50 | 显示全部楼层
争取早点能看懂...  

出0入296汤圆

 楼主| 发表于 2018-9-28 22:53:41 | 显示全部楼层

争取早点用起来是比较实际的。

出0入0汤圆

发表于 2018-9-28 23:03:01 | 显示全部楼层

强帖收藏学习

出0入0汤圆

发表于 2018-10-21 09:47:35 | 显示全部楼层
谢谢大神的好贴。说下自己读后的感受:
对于初学者来说,读懂并且使用确实很难,要花大量的时间,如果能以一个实例来展开(就想前面的说的出一个工程),会很快掌握。
这个帖子,对高手来说写的太详细,对初学者来说又有些晦涩难懂。
高不成,低不就。

出0入0汤圆

发表于 2018-10-21 09:51:02 | 显示全部楼层
真心想用,真心不好懂。

出0入296汤圆

 楼主| 发表于 2018-10-22 17:33:50 | 显示全部楼层
LB342342 发表于 2018-10-21 09:51
真心想用,真心不好懂。

其实你要相信我,这里我已经尽最大努力把内容写得让初学者好懂了。但看来效果并不尽如人意,
这里,我需要你的帮助,你能说出第一个让你觉得不好懂得地方么?我从那里开始尝试进行改进。

一步一步的,如果你不介意,我就可以借助你的帮助,把教程写得非常简单。其实你说用一个例子,
我讲真的, 我从头开始进行推导这个框架怎么来的,本身就是一个例子了……并且后面也的确提供
了例子——比如单级菜单的例子和两级菜单的例子。我想知道的是,这里缺失的是什么?

出0入0汤圆

发表于 2018-10-22 17:41:18 | 显示全部楼层
我先 留个名字 然后 好好 研究一下 啊

出0入0汤圆

发表于 2018-10-24 09:31:11 | 显示全部楼层
楼主| 发表于 前天 17:33 | 只看该作者
其实你要相信我,
****************************
是我没有静下心来理解,只想着拿来就往里套用。
现在在静静的看了。
就开头部分而言,前面的推理倒把我绕糊涂了,其实前面的就是铺垫,直接从这里开始看,到时号理解点,看不懂了,再往前面看。

因此,前面的默认模板就可以用极为简单的形式进行描述:



typedef struct __menu_item  menu_item_t;
typedef struct __menu      menu_t;

typedef fsm_rt_t menu_item_handler_t(menu_item_t *);

struct __menu_item {
    menu_item_handler_t *fnHandle;                      //!< handler
    menu_t              *ptChild;                       //!< Child Menu
};


declare_menu_item_template(default_menu_item_t)

def_menu_item_template(default_menu_item_t)
    //! depends on your application, you can add/remove/change following members
    char                *pchTitle;                      //!< Menu Title
    char                *pchDescription;                //!< Description for this menu item
    char                chShortCutKey;                  //!< Shortcut Key value in current menu
end_def_menu_item_template(default_menu_item_t)
复制代码

再次谢谢你的耐心。

出0入0汤圆

发表于 2018-10-24 12:03:06 | 显示全部楼层
试着把代码放到.h和.c文件,然后编译,会发现许多没有定义的标识符,然后开始在文章里找标识符的出处。
对初学者来说,要是最后给一个最终版的.c和.h文件内容。就比较好用好懂了吧。
感觉有用的信息,要在里面找,然后整合到一起。

出0入0汤圆

发表于 2018-10-24 15:07:35 | 显示全部楼层
我试着在MDK的STM32一个项目里加入一个.h和一个.c文件编译:
.H:
#ifndef __MENU__
#define __MENU__
#include "stm32f1xx_hal.h"

#ifndef __FSM_RT_TYPE__
#define __FSM_RT_TYPE__
//! \name finit state machine state
//! @{
typedef enum {
    fsm_rt_err          = -1,    //!< fsm error, error code can be get from other interface
    fsm_rt_cpl          = 0,     //!< fsm complete
    fsm_rt_on_going     = 1,     //!< fsm on-going
    fsm_rt_wait_for_obj = 2,     //!< fsm wait for object
    fsm_rt_asyn         = 3,     //!< fsm asynchronose complete, you can check it later.
} fsm_rt_t;
//! @}

#endif


typedef struct __menu_item  menu_item_t;
typedef struct __menu      menu_t;

typedef fsm_rt_t menu_item_handler_t(menu_item_t *);

struct __menu_item {
    menu_item_handler_t *fnHandle;                      //!< handler
    menu_t              *ptChild;                       //!< Child Menu
};

typedef struct __menu_engine_cb menu_engine_cb_t;
typedef fsm_rt_t menu_engine_t(menu_engine_cb_t *);

struct __menu {
    menu_item_t        *ptItems;                        //!< menu item list
    uint8_t        chCount;                        //!< menu item count
    menu_t             *ptParent;                       //!< parent menu;
    menu_engine_t      *fnEngine;                       //!< engine for process current menu
};



typedef struct __default_menu_item_t  default_menu_item_t;

struct __default_menu_item_t   {

    //! inherit from base class menu_item_t
    menu_item_t;
//..\User\0aa_menu\MENU.h(48): error:  #3093: anonymous structs are only supported in --gnu mode, or when enabled with #pragma anon_unions

    //! depends on your application, you can add/remove/change following members
    char                *pchTitle;                      //!< Menu Title
    char                *pchDescription;                //!< Description for this menu item
    char                chShortCutKey;                  //!< Shortcut Key value in current menu
};

#endif
/////////////////////////////////
.C:
#include "MENU.h"

extern fsm_rt_t top_menu_engine(menu_engine_cb_t *ptThis);

extern fsm_rt_t top_menu_item_a_handler(menu_item_t *ptItem);
extern fsm_rt_t top_menu_item_b_handler(menu_item_t *ptItem);
extern fsm_rt_t top_menu_item_c_handler(menu_item_t *ptItem);

extern const menu_t c_tTopMenu;

default_menu_item_t c_tTopMenuItems[] = {
    {
        top_menu_item_a_handler,
        NULL,                                           //!< child menu
        "Top Menu A",
        "This is Top Menu A",
    },
    {
        top_menu_item_b_handler,
        NULL,                                           //!< child menu
        "Top Menu B",
        "This is Top Menu B"
    },
    {
        top_menu_item_c_handler,
        NULL,                                           //!< child menu
        "Top Menu C",
        "This is Top Menu C"
    }
};

const menu_t c_tTopMenu = {
    (menu_item_t *)c_tTopMenuItems,                                    //!< menu item list
    UBOUND(c_tTopMenuItems),                            //!< menu item count   UBOUND未定义
..\User\0aa_menu\menu.c(34): error:  #59: function call is not allowed in a constant expression
    NULL,                                               //!< top menu has no parent
    top_menu_engine,                                    
};


fsm_rt_t top_menu_item_a_handler(menu_item_t *ptItem)
{
    return fsm_rt_cpl;
}

fsm_rt_t top_menu_item_b_handler(menu_item_t *ptItem)
{
    return fsm_rt_cpl;
}

fsm_rt_t top_menu_item_c_handler(menu_item_t *ptItem)
{
    return fsm_rt_cpl;
}

fsm_rt_t top_menu_engine(menu_engine_cb_t*ptThis)
{
    return fsm_rt_cpl;
}

编译后,出现3个错误,上面红色部分。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-27 23:10

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表