搜索
bottom↓
回复: 36

[代码][交流]基于C预处理器的ProtoThread实现

  [复制链接]

出0入0汤圆

发表于 2012-12-13 21:11:59 | 显示全部楼层 |阅读模式
本帖最后由 dr2001 于 2012-12-15 07:40 编辑

这是一个非常奇怪的代码写法。
代码的核心目的在于:使用基于×标准C×的Switch/Case结构描述ProtoThread/CoRoutine结构时,能够使得Case的值从0开始依序递增,让编译器能够实现switch的跳转表优化。

这是一个毁誉参半的代码描述方法:
1、由于会在代码中插入大量的include,所以,代码的可读性下降,让不了解的人读起来,很感觉奇怪。
2、恰好由于ProtoThread的Stackless的描述方法,但凡插入include的地方,都是潜在发生问题的地方,能够提醒编码者注意。
3、include插入的是×预处理宏和代码×,这个操作本身也是有风险的。
总之,代码中奇怪的地方,显示了它的不安全性;不仅是include本身,对上下文的代码和变量都有影响。比基于宏的实现多了某种提示作用;同时达成了优化的效果。

这个描述值得思考的地方:C语言的预处理器PreProcessor是有计算能力的。
C++的模板是图灵完备的,因此有Boost库来利用模板进行Meta Programming。
进而有人挖掘了C预处理器的处理能力,详情可以参考Boost库的PreProcessor部分。

就我目前的理解来说,C语言预处理器的计算能力仅体现在#if/#elif的值判断上
当预处理器遇到这两个条件时,会对条件本身进行宏替换,然后×计算×表达式的值,判断True/False。
于是,计算的实质就是进行一系列的真伪判断,定义相应的结果宏,最后进行组合。

计算的核心宏是:ptCounter.ci

  1. #if     (((_ptCounter+1)/0x10)&0xF) == 0x0
  2. #undef  _ptNew0x10
  3. #define _ptNew0x10 0
  4. #elif   (((_ptCounter+1)/0x10)&0xF) == 0x1
  5. #undef  _ptNew0x10
  6. #define _ptNew0x10 1
  7. ... ...
  8. #elif   (((_ptCounter+1)/0x10)&0xF) == 0xF
  9. #undef  _ptNew0x10
  10. #define _ptNew0x10 F
  11. #else
  12. #error  "ptCounter: UnExpected Error."
  13. #endif

  14. #if     ((_ptCounter+1)&0xF) == 0x0
  15. #undef  _ptNew0x01
  16. #define _ptNew0x01 0
  17. #elif   ((_ptCounter+1)&0xF) == 0x1
  18. #undef  _ptNew0x01
  19. #define _ptNew0x01 1
  20. ... ...
  21. #elif   ((_ptCounter+1)&0xF) == 0xF
  22. #undef  _ptNew0x01
  23. #define _ptNew0x01 F
  24. #else
  25. #error  "ptCounter: UnExpected Error."
  26. #endif

  27. #undef  _ptCounter
  28. #define _ptCounter _ptCTR_Make(_ptNew0x10, _ptNew0x01)
复制代码
特别注意在什么时候#undef宏。取消定义早了,会导致后续的宏替换出问题。

典型的ProtoThread代码如下:

  1. pt_t    ctrl[1] = {ptStatReset};
  2. void ptFunc1(ptArg, uint8_t msg) {
  3. #   include ptEnter
  4.     if(msg == 0) {
  5.         printf("A");
  6. ... ...
  7.     } else if (msg == 2) {
  8.         printf("C");
  9. #       include ptStall
  10.     } else if (msg == 3) {
  11.         printf("D");
  12. #       include ptYield
  13.         printf("E");
  14. ... ...
  15.         printf("H");
  16. #       include ptStall
  17.     }
  18. #   include ptLeave
  19. }
复制代码
这是不带返回值的情况。
带有返回值的情况因为include不能传递参数,所以必须用额外的宏进行定义。具体参考附件中的代码。

附件中的宏支持GCC的Labels as Values和基于标准C的Switch/Case两种实现。会根据__GNUC__宏的预定义情况自动选择对应的宏。
基于标准C的宏实现可以在以下GCC命令行编译通过:
gcc -O2 -std=gnu99 -pedantic -Wall -Wextra ptinc_demo.c
基于GCC的Label as Values的宏,不支持-pedantic参数,会有警告。

如果要观察可以使用:
宏替换:gcc -O2 -std=gnu99 -Wall -Wextra -E -DPREPROC ptinc_demo.c
反汇编:gcc -O2 -std=gnu99 -Wall -Wextra -c ptinc_demo.c 然后objdump即可。


以下是ARM编译的结果片段,基于标准C的(ptinc.h的CFG_PT_FORCE_STD_C = 1),Cortex-M3。Keil MDK也能得到相似的结果。

  1. 命令行:
  2. arm-none-eabi-gcc -O2 -std=gnu99 -Wall -Wextra -march=armv7-m -mthumb -c ptinc_demo.c
  3. arm-none-eabi-objdump -d ptinc_demo.o
  4. 反汇编结果:
  5. 00000000 <ptFunc1>:
  6.    0:   b510            push    {r4, lr}
  7.    2:   7803            ldrb    r3, [r0, #0]
  8.    4:   4604            mov     r4, r0
  9.    6:   2b04            cmp     r3, #4
  10.    8:   d808            bhi.n   1c <ptFunc1+0x1c>
  11. >>  查表跳转。
  12.    a:   e8df f003       tbb     [pc, r3]
  13.    e:   0903            .short  0x0903
  14.   10:   0a12            .short  0x0a12
  15.   12:   09              .byte   0x09
  16.   13:   00              .byte   0x00
复制代码
当然,如果Switch数量少,以及行所在位置差异不大等特定情况,编译器也会进行跳转表优化,但依然效率不高。


另外,该宏经过改造,可以用在其它需要静态递增赋值的场合。缺点是一个变量名需要改造一次,不能一劳永逸。



本帖子中包含更多资源

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

x

阿莫论坛20周年了!感谢大家的支持与爱护!!

曾经有一段真挚的爱情摆在我的面前,我没有珍惜,现在想起来,还好我没有珍惜……

出0入0汤圆

发表于 2012-12-13 23:44:18 | 显示全部楼层
这写的代码不容易读啊,,,

出0入296汤圆

发表于 2012-12-14 10:57:35 | 显示全部楼层
本帖最后由 Gorgon_Meducer 于 2012-12-14 12:43 编辑

我觉得需要解释一下隐藏在
#include ptEnter
#include ptStall
这些宏下面的内容,否则即便给出ptCounter.ci的内容也不容易懂啊……

看完了,这个技术太巧妙了!学会了,哈哈,解决了一个困扰我很久的问题~这样以后我的宏系统会更飘逸的~ 我感觉自己正在踏上混乱C语言的不归路……
既然学会了,我就尝试解释一下核心的宏标号计算部分。

很多时候,我们知道宏似乎是有一定计算能力的,比如考虑下面的代码:

  1. //! 在配置文件的某个地方...
  2. #define REG_ITEM_COUNT    (13u)
  3. ...
  4. //! 在代码的具体实现之前
  5. //! 假设每一个REG_ITEM消耗2个BIT,所以4个REG_ITEM消耗一个字节,下面计算具体消耗的字节数,不足一个字节的按照一个字节计算
  6. #define REQ_ITEM_COUNT_BYTE     ((((REG_ITEM_COUNT) / 2) + 3) / 4)

  7. //! 所以在代码上我们可以这么写
  8. uint8_t s_chRequests[REQ_ITEM_COUNT_BYTE] = {0};
复制代码
上面的代码是没有任何问题,体现了宏自动计算的强大。但这种强大是表面上的,宏其实并没有聪明到让我们可以忘乎所以的地步,比如下
面的场合:


  1. #define __REQ_INIT(__COUNT, __VALUE)        (__VALUE),

  2. uint8_t s_chRequests[REQ_ITEM_COUNT_BYTE] = {MREPEAT(REQ_ITEM_COUNT_BYTE, __REQ_INIT, 0xFF)};
复制代码
这里我解释一下,MREPEAT是一个很有用的宏,他的作用是把另外一个宏,比如这里的__REQ_INIT重复指定的次数,比如这里的
REQ_ITEM_COUNT_BYTE次,这里被重复的宏__REQ_INIT需要接受一个参数,也就是这里的0xFF。所以很容易看出,这个代码的
意图就是根据REQ_ITEM_COUNT_BYTE的数量,将所有的字节都初始化为0xFF。
问题出来了,宏MREPEAT实际上只能接受实际的数值,也就是说,这里的REQ_ITEM_COUNT_BYTE必须是一个常数,而不能是任
何表达式,这是宏MREPEAT的内容限制的,不相信,我们可以看看MREPEAT的内容片段:


  1. #define TPASTE2( a, b)                            a##b

  2. /*! \brief Macro repeat.
  3. *
  4. * This macro represents a horizontal repetition construct.
  5. *
  6. * \param count  The number of repetitious calls to macro. Valid values range from 0 to MREPEAT_LIMIT.
  7. * \param macro  A binary operation of the form macro(n, data). This macro is expanded by MREPEAT with
  8. *               the current repetition number and the auxiliary data argument.
  9. * \param data   Auxiliary data passed to macro.
  10. *
  11. * \return       <tt>macro(0, data) macro(1, data) ... macro(count - 1, data)</tt>
  12. */
  13. #define MREPEAT(count, macro, data)         TPASTE2(MREPEAT, count)(macro, data)

  14. #define MREPEAT0(  macro, data)
  15. #define MREPEAT1(  macro, data)       MREPEAT0(  macro, data)   macro(  0, data)
  16. #define MREPEAT2(  macro, data)       MREPEAT1(  macro, data)   macro(  1, data)
  17. #define MREPEAT3(  macro, data)       MREPEAT2(  macro, data)   macro(  2, data)
  18. #define MREPEAT4(  macro, data)       MREPEAT3(  macro, data)   macro(  3, data)
  19. ...
  20. #define MREPEAT254(macro, data)       MREPEAT253(macro, data)   macro(253, data)
  21. #define MREPEAT255(macro, data)       MREPEAT254(macro, data)   macro(254, data)
  22. #define MREPEAT256(macro, data)       MREPEAT255(macro, data)   macro(255, data)
复制代码
则上面的代码实际上会被展开为:

  1. uint8_t s_chRequests[REQ_ITEM_COUNT_BYTE] = {
  2.     TPASTE2(MREPEAT, REQ_ITEM_COUNT_BYTE)(__REQ_INIT, 0xFF)
  3. };
复制代码
前面的代码编译肯定会报告错误因为宏系统并不会将REQ_ITEM_COUNT_BYTE计算后的值作为常量替换到表达式中,相反,宏系统会保持
原汁原味的一种文字替换,因此,上面的代码实际会被进一步替换为

  1. uint8_t s_chRequests[REQ_ITEM_COUNT_BYTE] = {
  2.     MREPEAT##REQ_ITEM_COUNT_BYTE(__REQ_INIT, 0xFF)
  3. };
复制代码
也就是

  1. uint8_t s_chRequests[REQ_ITEM_COUNT_BYTE] = {
  2.     MREPEAT ((((REG_ITEM_COUNT) / 2) + 3) / 4)(__REQ_INIT, 0xFF)
  3. };
复制代码
这就是预编译的可能结果,于是到了编译阶段编译器就会把MREPEAT当成一个函数,首先,这个函数显然不存在,于是一个implicit funciton declaration
的warning会首先扔出来,紧接着到了Link阶段,系统发彪了,说找不到函数MREPEAT在xxxx.o……当然,这个过程是我YY的,具体编译器会有不同
的行为,但不管怎么样,宏的计算能力在我们最信任它的时候掉了链子,因为我们期望的结果是这样的:

  1. uint8_t s_chRequests[REQ_ITEM_COUNT_BYTE] = {
  2.     MREPEAT4(__REQ_INIT, 0xFF)
  3. };
复制代码
然后这个宏会被进一步展开,最终成为4个0xFF。

=============================华丽的分割线========================================
原本到这里,故事就结束了,是的,我死心了,决定把这个作为常识了,放弃了……哎……就这样吧……
然而,今天的楼主有一次震彻了我死灰一般的大叔之心……是的,故事还可以继续!
=============================华丽的分割线========================================


有时候我们会想,要是宏的计算支持变量就好了,这样我们可以在预编译的过程中获得更大的灵活度,之前我们认为这是很难做到的,现在看来这只是一个思路
问题。假设我们通过下面的方法定义了一个宏,并且期望它能起到变量的效果:

  1. #define _ptCounter    0x00
复制代码
这个宏变量的名字叫做_ptCounter , 并有一个初始值0x00,当然实际上这个初始值可以是任意的单子节数值,接下来,我们要将这个_ptCounter的值拆分成高
4bit和低4bit,并独立定义两个宏分别对应高4bit和低4bit:
比如,这里通过宏_ptNew0x10来表示高4bit

  1. #if     (((_ptCounter+1)/0x10)&0xF) == 0x0
  2. #undef  _ptNew0x10
  3. #define _ptNew0x10 0
  4. #elif   (((_ptCounter+1)/0x10)&0xF) == 0x1
  5. #undef  _ptNew0x10
  6. #define _ptNew0x10 1
  7. #elif   (((_ptCounter+1)/0x10)&0xF) == 0x2
  8. #undef  _ptNew0x10
  9. #define _ptNew0x10 2
  10. #elif   (((_ptCounter+1)/0x10)&0xF) == 0x3
  11. #undef  _ptNew0x10
  12. #define _ptNew0x10 3
  13. #elif   (((_ptCounter+1)/0x10)&0xF) == 0x4
  14. #undef  _ptNew0x10
  15. #define _ptNew0x10 4
  16. #elif   (((_ptCounter+1)/0x10)&0xF) == 0x5
  17. #undef  _ptNew0x10
  18. #define _ptNew0x10 5
  19. #elif   (((_ptCounter+1)/0x10)&0xF) == 0x6
  20. #undef  _ptNew0x10
  21. #define _ptNew0x10 6
  22. #elif   (((_ptCounter+1)/0x10)&0xF) == 0x7
  23. #undef  _ptNew0x10
  24. #define _ptNew0x10 7
  25. #elif   (((_ptCounter+1)/0x10)&0xF) == 0x8
  26. #undef  _ptNew0x10
  27. #define _ptNew0x10 8
  28. #elif   (((_ptCounter+1)/0x10)&0xF) == 0x9
  29. #undef  _ptNew0x10
  30. #define _ptNew0x10 9
  31. #elif   (((_ptCounter+1)/0x10)&0xF) == 0xA
  32. #undef  _ptNew0x10
  33. #define _ptNew0x10 A
  34. #elif   (((_ptCounter+1)/0x10)&0xF) == 0xB
  35. #undef  _ptNew0x10
  36. #define _ptNew0x10 B
  37. #elif   (((_ptCounter+1)/0x10)&0xF) == 0xC
  38. #undef  _ptNew0x10
  39. #define _ptNew0x10 C
  40. #elif   (((_ptCounter+1)/0x10)&0xF) == 0xD
  41. #undef  _ptNew0x10
  42. #define _ptNew0x10 D
  43. #elif   (((_ptCounter+1)/0x10)&0xF) == 0xE
  44. #undef  _ptNew0x10
  45. #define _ptNew0x10 E
  46. #elif   (((_ptCounter+1)/0x10)&0xF) == 0xF
  47. #undef  _ptNew0x10
  48. #define _ptNew0x10 F
  49. #else
  50. #error  "ptCounter: UnExpected Error."
  51. #endif
复制代码
同理,这里用宏_ptNew0x01对应低4bit:

  1. #if     ((_ptCounter+1)&0xF) == 0x0
  2. #undef  _ptNew0x01
  3. #define _ptNew0x01 0
  4. #elif   ((_ptCounter+1)&0xF) == 0x1
  5. #undef  _ptNew0x01
  6. #define _ptNew0x01 1
  7. #elif   ((_ptCounter+1)&0xF) == 0x2
  8. #undef  _ptNew0x01
  9. #define _ptNew0x01 2
  10. #elif   ((_ptCounter+1)&0xF) == 0x3
  11. #undef  _ptNew0x01
  12. #define _ptNew0x01 3
  13. #elif   ((_ptCounter+1)&0xF) == 0x4
  14. #undef  _ptNew0x01
  15. #define _ptNew0x01 4
  16. #elif   ((_ptCounter+1)&0xF) == 0x5
  17. #undef  _ptNew0x01
  18. #define _ptNew0x01 5
  19. #elif   ((_ptCounter+1)&0xF) == 0x6
  20. #undef  _ptNew0x01
  21. #define _ptNew0x01 6
  22. #elif   ((_ptCounter+1)&0xF) == 0x7
  23. #undef  _ptNew0x01
  24. #define _ptNew0x01 7
  25. #elif   ((_ptCounter+1)&0xF) == 0x8
  26. #undef  _ptNew0x01
  27. #define _ptNew0x01 8
  28. #elif   ((_ptCounter+1)&0xF) == 0x9
  29. #undef  _ptNew0x01
  30. #define _ptNew0x01 9
  31. #elif   ((_ptCounter+1)&0xF) == 0xA
  32. #undef  _ptNew0x01
  33. #define _ptNew0x01 A
  34. #elif   ((_ptCounter+1)&0xF) == 0xB
  35. #undef  _ptNew0x01
  36. #define _ptNew0x01 B
  37. #elif   ((_ptCounter+1)&0xF) == 0xC
  38. #undef  _ptNew0x01
  39. #define _ptNew0x01 C
  40. #elif   ((_ptCounter+1)&0xF) == 0xD
  41. #undef  _ptNew0x01
  42. #define _ptNew0x01 D
  43. #elif   ((_ptCounter+1)&0xF) == 0xE
  44. #undef  _ptNew0x01
  45. #define _ptNew0x01 E
  46. #elif   ((_ptCounter+1)&0xF) == 0xF
  47. #undef  _ptNew0x01
  48. #define _ptNew0x01 F
  49. #else
  50. #error  "ptCounter: UnExpected Error."
  51. #endif
复制代码
到这一步,我们就获得了两个宏_ptNew0x10和_ptNew0x01分别代表一个自己的高4bit和低4bit,接下来,就要借助另外一个宏的帮助将
这两个宏组合起来形成一个新的常数——是的,其实仍然是字面上的常数,但不管怎么样,预编译系统都是看“字面上的意思的”。

  1. #define _ptCTR_Cons(_X, _Y)     0x ## _X ## _Y
  2. #define _ptCTR_Make(_X, _Y)     _ptCTR_Cons(_X, _Y)
复制代码
这个宏就是将_X和_Y代表的内容组合在一起,比如_ptCTR_Make(A,B)实际上最后组合的结果就是0xAB,有了这个宏的帮助,下面的内容
就很明了:

  1. #undef  _ptCounter
  2. #define _ptCounter _ptCTR_Make(_ptNew0x10, _ptNew0x01)
复制代码
这两行代码意义非凡,尤其是第一句#undef _ptCounter,这是我们站在xx城楼上向预编器庄严宣誓“旧的_ptCounter已经被打倒了!”,
然后第二句说“新的_ptCounter成立了!”——从此_ptCounter由0x00变成了0x01。

旧的桎梏被推翻了,新的秩序正在建立中,可谓百废待兴啊……只听xx城楼下密密麻麻的原代码 哗……哗……哗……可谓举国欢庆啊!

接下来首先需要考虑两个问题,第一宏变量的命名问题,如你所见,前面的红变量叫做_ptCounter,并且下面起到核心支撑作用的条件编译也都
建立在_ptCounter这个名称的基础之上,那如果我们想换一个名字怎么办呢?立马就有人说,那还不简单,再定义一重呗,例如:

  1. //! 这是我们自己定义的宏变量
  2. #define EXAMPLE_MACRO_VAR     0x04

  3. //!在使用前面提到的支持系统之前先做一个替换
  4. #define _ptCounter    EXAMPLE_MACRO_VAR
  5. ...

  6. //! 在新的_ptCounter被定义以后,我们也更新EXAMPLE_MACRO_VAR
  7. #undef EXAMPLE_MACRO_VAR
  8. #define EXAMPLE_MACRO_VAR _ptCounter
复制代码
很好,很强大……但似乎有一个问题……由于.c文件是独立编译的,如果一个.c的某一个代码块里面同时用到了两个宏变量,难么显然
最后大家都会对应到_ptCounter上,这就冲突了。看来这里存在潜在的共享问题。不过不用怕,新世界一穷二白,但不缺乏勤劳善良的程序员
于是有人提出用人海战术——即,通过古老的ctrl+C和ctrl+v法则(其实我想写大x,由于你懂的原因,最后换了一个很不爽的词,因此fuck那个
xx大x)定义一揽子宏变量及其支撑预编译系统,比如_ptCounter00,_ptCounter01...这是利国利民的大事啊,就像xx水坝一样,一次施工,
造福千年——这样我们就可以在同一个.c的同一个代码块里面使用若干个宏变量了——一般不会超过8个吧?就定义8个好了……好奢侈……

又有人提出,一个字节似乎太少了,还应该支持2个字节和4个字节的宏变量,这也是利国利民的大好事,于是大家一致决定制定如下的命名规则
MACRO_CNT_Un_xx
这里n表示位数,xx表示这是第几个变量(为了支持同一个代码块里面多个宏变量),例如
MACRO_CNT_U8_00
MACRO_CNT_U16_01
...
还有人提出,是否可以开发PC工具自动生成所需的支撑系统,放在需要的模块里面,同时还可以解除每次只增加1的限制……P民(Programmer民),
只有P民能爆发出如此的智慧……让敌人暴露在P民战争的汪洋大海中!

出0入0汤圆

发表于 2012-12-14 11:17:46 | 显示全部楼层
学习,学习!!!

出0入0汤圆

 楼主| 发表于 2012-12-14 12:16:43 | 显示全部楼层
Gorgon_Meducer 发表于 2012-12-14 10:57
我觉得需要解释一下隐藏在
#include ptEnter
#include ptStall

莫非……你不会……递归Include宏吧。。。那就翻了。

因为CPP是顺序处理,应该没什么问题,回头我来尝试尝试,这个有趣味了。
这个的细节和CPP自身的限制还需要在思考思考。
已经开始有新收获了。哈哈。

出0入296汤圆

发表于 2012-12-14 12:18:50 | 显示全部楼层
dr2001 发表于 2012-12-14 12:16
莫非……你不会……递归Include宏吧。。。那就翻了。

因为CPP是顺序处理,应该没什么问题,回头我来尝试 ...

你一个苹果,我一个苹果,交换以后还是两个苹果(有没有虫就不知道了)。
你一个点子,我一个点子,交换以后就是更多的点子

出0入0汤圆

发表于 2012-12-16 12:03:05 | 显示全部楼层
看第一眼被打晕,等回过神继续看。

出0入0汤圆

发表于 2012-12-16 12:12:23 | 显示全部楼层
Gorgon_Meducer 发表于 2012-12-14 10:57
我觉得需要解释一下隐藏在
#include ptEnter
#include ptStall

表述能力太强大了。

出0入0汤圆

发表于 2012-12-16 12:24:21 | 显示全部楼层
不错,支持总结

出0入0汤圆

发表于 2012-12-16 14:01:01 | 显示全部楼层
晕菜~~~~~~~为了那一点点的效率,把代码搞成这样!!!!
简直就是得不偿失; 用宏来实现这样的计算, 本来就是 宏设计者想避免的东西;

出0入0汤圆

发表于 2012-12-16 14:59:44 | 显示全部楼层
本帖最后由 ifree64 于 2012-12-16 15:21 编辑

这就是传说中的大牛用代码写代码。

楼主的核心思路是,利用include、if、elif,重定义一个宏的值,使得每包含一次,这个宏的值就自动加1,将ProtoThread中利用__LINE__定义的状态变为用自动递增的宏从0开始按照自然顺序递增的顺序;
以使得编译器生成的switch代码可以利用查找表优化。

既然c++的模板也具有元编程的能力,可以用模板来实现类似的效果吗?

出0入0汤圆

 楼主| 发表于 2012-12-16 17:07:20 | 显示全部楼层
C预处理器能达成的东西,C++的预处理器加上模板展开一定可以达成并且实现的更好。

但是我对C++的预处理器和模板展开的流程还没什么深入研究,具体如何实现,能让代码多么优雅现在还是未知数。Boost库应该可以作为某种参考。

出0入296汤圆

发表于 2012-12-16 18:54:01 | 显示全部楼层
onepower 发表于 2012-12-16 14:01
晕菜~~~~~~~为了那一点点的效率,把代码搞成这样!!!!
简直就是得不偿失; 用宏来实现这样的计算, 本来就是 宏 ...

工具而已,如果适用这个工具的小群体都能用好这个工具,那么不过就是个工具,犯不着为他那么纠结,
用就好了。它既不会改变代码的逻辑也不会改变你的编码思想,不是么?

出0入0汤圆

发表于 2012-12-17 12:11:23 | 显示全部楼层
#define _ptCTR_Cons(_X, _Y)     0x ## _X ## _Y
这句解决我多时的困扰

出0入0汤圆

发表于 2013-1-19 16:08:42 | 显示全部楼层
c语言的define用法

出0入0汤圆

发表于 2013-1-19 19:33:13 | 显示全部楼层
学习、学习!受用了

出0入0汤圆

发表于 2013-1-31 10:39:26 | 显示全部楼层
也许是我的水平太低了,没有理解为什么要这样使用宏,能不能解释下?

出0入0汤圆

发表于 2013-1-31 10:44:41 | 显示全部楼层
mark 傻孩子的精品讲解

出0入0汤圆

发表于 2013-2-20 21:10:48 | 显示全部楼层
傻孩子的精品讲解

出0入0汤圆

发表于 2013-3-12 14:30:48 | 显示全部楼层
不会用啊。

出0入0汤圆

发表于 2013-3-12 14:42:27 | 显示全部楼层
mark!                        

出0入0汤圆

发表于 2013-5-12 20:42:36 | 显示全部楼层
你们都是牛人  吾辈还得隐匿200年啊

出0入0汤圆

发表于 2013-5-12 22:12:28 | 显示全部楼层
学习可以参考下,但是要我需要去维护这样的代码,我会骂人的,

出0入0汤圆

发表于 2014-9-17 14:27:34 | 显示全部楼层
的确很取巧,但不要让我来维护这类代码~~

出0入0汤圆

发表于 2014-9-17 22:06:18 | 显示全部楼层
可以参考下,

出0入0汤圆

发表于 2014-9-19 20:47:55 | 显示全部楼层
傻孩子的精品讲解

出50入8汤圆

发表于 2014-9-19 22:35:10 | 显示全部楼层
强大的宏,学习了

出0入0汤圆

发表于 2014-9-22 17:35:53 | 显示全部楼层
MARK

以后再来看

出0入0汤圆

发表于 2014-9-24 14:10:00 | 显示全部楼层
mark  学习  

出0入0汤圆

发表于 2014-9-29 22:07:38 | 显示全部楼层
Gorgon_Meducer 发表于 2012-12-14 10:57
我觉得需要解释一下隐藏在
#include ptEnter
#include ptStall

兄弟无私奉献,好好学习天天向上

出0入0汤圆

发表于 2014-10-1 16:03:45 | 显示全部楼层
高大上的东西

出0入42汤圆

发表于 2015-1-4 22:53:24 | 显示全部楼层
学无止境!

出0入0汤圆

发表于 2015-6-24 16:05:23 | 显示全部楼层
好资料,看3遍都不过瘾。

出0入0汤圆

发表于 2017-6-23 12:58:21 | 显示全部楼层
刚开始了解。

出0入296汤圆

发表于 2017-10-22 02:13:40 | 显示全部楼层
发现ARM Compiler 5好像并不支持

#include ptEnter

这样的语法……也就是说MDK和DS-5都没法这样玩……

出0入0汤圆

 楼主| 发表于 2017-10-23 10:10:02 | 显示全部楼层
Gorgon_Meducer 发表于 2017-10-22 02:13
发现ARM Compiler 5好像并不支持

#include ptEnter


5是Clang还是6是?或者选项问题?

C99-N1256 6.10.2-4和6.10.2-8说明include的目标文件的宏替换是符合标准的。

出0入296汤圆

发表于 2017-10-24 23:00:11 | 显示全部楼层
dr2001 发表于 2017-10-23 10:10
5是Clang还是6是?或者选项问题?

C99-N1256 6.10.2-4和6.10.2-8说明include的目标文件的宏替换是符合标 ...

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

本版积分规则

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

GMT+8, 2024-8-25 14:05

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

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