【问题标题】:How to stringify macro having array as #define a_macro {5,7,7,97}?如何将具有数组的宏字符串化为#define a_macro {5,7,7,97}?
【发布时间】:2012-07-27 14:24:59
【问题描述】:

请通过。

#define _VERSION_ 1.4
#define DEFAULT_NETWORK_TOKEN_KEY { 3, 6, 5, 100}

//上面的宏不能改,只能改下面

#define STR_VALUE(arg)      #arg
#define FUNCTION_NAME(name) STR_VALUE(name\r)
#define TEST_FUNC      #AP started v _VERSION_
#define TEST_FUNC_NAME FUNCTION_NAME(TEST_FUNC)

#define QUOTE_X(t)#t
#define QUOTE(t)QUOTE_X(t)
#define ABC 100 //{ 3, 6, 5, 100}
#define MYSTR "The value of ABC is"     

const uint8 startMsg[] =  MYSTR " " QUOTE(ABC);

结果:ABC的值为100

const uint8 startMsg[] =  TEST_FUNC_NAME;

results: #AP started v 1.4 (回车) // 我也想去掉 v 和 1.4 之间的空格

我想要

const uint8 startMsg[] = ?? ;

应该导致#AP started [3.6.5.100] v1.4(回车)或#AP started [3,6,5,100] v1.4(回车) return) 或类似的。

我正在开发 SOC 芯片,需要在启动时展示它。紧迫的。 :)

------问题的答案是------

#define NETTOKENKEY(a,b,c,d)  "[" #a "." #b "." #c "." #d "]"
#define GENNETTOKENKEY(z)    NETTOKENKEY(z) 

#define STRINGIZER(arg)     #arg
#define STR_VALUE(arg)      STRINGIZER(arg)
#define AP_VERSION_STR      "#AP started v" STR_VALUE(_VERSION_)



#define AP_NETVERSION_STR   "#AP started " \
                            GENNETTOKENKEY(DEFAULT_NETWORK_TOKEN_KEY_VALUES) \
                            " v" STR_VALUE(_VERSION_) **"\r"**

const uint8 startMsg[] = AP_NETVERSION_STR ;

【问题讨论】:

  • 具体来说,什么是“在启动时显示这个”。你在跑步和交流吗?如果是这样,只需在运行时执行。是否有人加载您的 hex 文件并在内存中寻找必须以特定格式存储此神奇字符串的特定位置?如果是这样,那么预处理器是您唯一的解决方案(AFAIK),您必须更加详细地完成上面显示的内容。
  • 感谢大家的努力。但是仍然在寻找更多的知识。

标签: c macros embedded


【解决方案1】:

C 预处理器是一个相当简单的文本替换程序,我认为它无法完成您需要的操作并生成编译时常量字符串 if 它是绝对不可能更改DEFAULT_NETWORK_TOKEN_KEY 宏。

转换“数组”表示法特别困难——事实上,不改变数组定义宏的前提可能意味着它是不可能的。

如果可以定义新的宏并重新定义 DEFAULT_NETWORK_TOKEN_KEY 以使其产生与以往相同的值,那么您可以获得所需的结果。

为了说明,注意你可以写:

#define x             { 3, 45, 5, 49}
#define f4(a,b,c,d)   #a " - " #b ":" #c "/" #d
#define y(z)          f4(z)

y(x)

预处理后,会产生:

"{ 3" " - " "45" ":" "5" "/" "49}"

请注意,大括号是参数字符串的一部分。现在,如果你能做到:

#define DEFAULT_NETWORK_TOKEN_KEY_VALUES 3, 6, 5, 100
#define DEFAULT_NETWORK_TOKEN_KEY { DEFAULT_NETWORK_TOKEN_KEY_VALUES}

(我保留了你的不对称间距,虽然我认为这不是真的必要),那么你可以使用:

#define NETTOKENKEY(a,b,c,d)  "[" #a "." #b "." #c "." #d "]"
#define GENNETTOKENKEY(z)    NETTOKENKEY(z)

GENNETTOKENKEY(DEFAULT_NETWORK_TOKEN_KEY_VALUES)

获取字符串“[3.6.5.100]”(对于我的示例中的值)。

摆脱v1.4 之间的空格相对容易:

#define STRINGIZER(arg)     #arg
#define STR_VALUE(arg)      STRINGIZER(arg)
#define AP_VERSION_STR      "#AP started v" STR_VALUE(_VERSION_)

AP_VERSION_STR

将这些拼凑在一起产生:

#define AP_NETVERSION_STR   "#AP started " \
                            GENNETTOKENKEY(DEFAULT_NETWORK_TOKEN_KEY_VALUES) \
                            " v" STR_VALUE(_VERSION_)

static const char version[] = AP_NETVERSION_STR;

如果您想在末尾添加 '\r',请将 "\r" 添加到 AP_NETVERSION_STR 宏定义的末尾。字符串连接非常很有用!

但是,这是基于能够“更改”DEFAULT_NETWORK_TOKEN_KEY 的定义,以便可以像这样格式化。如果没有这种改变,我认为你做不到。


测试是必要的!

#define _VERSION_ 1.4
#define DEFAULT_NETWORK_TOKEN_KEY_VALUES 3, 6, 5, 100
#define DEFAULT_NETWORK_TOKEN_KEY { DEFAULT_NETWORK_TOKEN_KEY_VALUES}

#define NETTOKENKEY(a,b,c,d)  "[" #a "." #b "." #c "." #d "]"
#define GENNETTOKENKEY(z)    NETTOKENKEY(z)

GENNETTOKENKEY(DEFAULT_NETWORK_TOKEN_KEY_VALUES)

#define STRINGIZER(arg)     #arg
#define STR_VALUE(arg)      STRINGIZER(arg)
#define AP_VERSION_STR      "#AP started v" STR_VALUE(_VERSION_)

AP_VERSION_STR

#define AP_NETVERSION_STR   "#AP started " \
                            GENNETTOKENKEY(DEFAULT_NETWORK_TOKEN_KEY_VALUES) \
                            " v" STR_VALUE(_VERSION_)

AP_NETVERSION_STR

当通过gcc -E 运行时,我们需要的是经过温和清理的输出(删除了空白行和#line 控件):

"[" "3" "." "6" "." "5" "." "100" "]"

"#AP started v" "1.4"

"#AP started " "[" "3" "." "6" "." "5" "." "100" "]" " v" "1.4"

【讨论】:

  • 是的,我什至建议(在预处理器领域)为每个八位字节制作单独的#defines,然后您可以选择是否将它们与 、 组成一个数组或与.制作一些人类可读的字符序列,我们通常将其识别为 IPv4 地址。
  • @JoshPetitt:一旦我们打破了更改DEFAULT_NETWORK_TOKEN_KEY 的禁忌,就会有各种各样的可能性。是的,从单独的组件构建列表可能是明智的。在这个禁忌被打破之前,我不认为预处理器可以提供帮助。
  • 我正在研究它.. 似乎将是答案。 “#AP 开始”然后版本和令牌可以到达任何地方,最后(回车)。我得到了#AP 开始 v 1.4 [3.6.5.100] 直到现在空间仍然没有。尝试,
  • 像魔术一样工作。 #define NETTOKENKEY(a,b,c,d) "[" #a "." #b“。” #C ”。” #d "]" #define GENNETTOKENKEY(z) NETTOKENKEY(z) #define STRINGIZER(arg) #arg #define STR_VALUE(arg) STRINGIZER(arg) #define AP_VERSION_STR "#AP 开始 v" STR_VALUE(VERSION i>) #define AP_NETVERSION_STR "#AP 开始" \ GENNETTOKENKEY(DEFAULT_NETWORK_TOKEN_KEY_VALUES) \ " v" STR_VALUE(VERSION) const uint8 startMsg[] = AP_NETVERSION_STR ;
  • @JonathanLeffler,我明白你的意思了。在这种情况下,我可能会定义两个带有注释的#defines 来“编辑这两个”。丑陋,但会解决问题。顺便说一句,你能给我一个 +1 链接到 stringify。我现在的代表让我有点害怕:-)
【解决方案2】:

如果可以,为什么要使用宏:

uint arr[] = DEFAULT_NETWORK_TOKEN_KEY;
float v = VERSION;

sprintf(buffer, "#AP started [%u.%u.%u.%u] v%f", arr[0], arr[1], arr[2], arr[3], v); 

【讨论】:

  • 如果只是在运行时打印数据,这是最好的解决方案。 (如果 OP 需要它作为编译对象中的 const 字符串,则需要另一种解决方案)
  • 如果它是一块具有 4KB RAM 的硬件(请参阅对 Joel Falcou 回答的评论),sprintf() 可能不会成为一个选项。
  • 我不想使用 sprintf。它对我来说很重。但对于启动它可能会。看看我有没有空间。
  • @JonathanLeffler,如果您使用具有非常简单格式的精简库(即省略了浮点数,以及一些更奇特的格式选项),他也许可以使用它。如果这是整个程序中唯一的用途,那很可能是一种浪费。
  • 好的,我明白了,尽管我仍然认为有比预处理器更好的工具。如果您至少有 printf,则可以打印它。如果它是裸机,我希望您至少有自己的打印功能,因此(因为如果您定义数组 x[] = DEFAULT_NETWORK_TOKEN_KEY; )您知道该数字在内存中的位置,并且可以再次打印它们(借助您的 ' to_str')。
【解决方案3】:

阅读 C 预处理器中的 # 和 ##。你在一些非常简单的东西上放了很多包装

http://gcc.gnu.org/onlinedocs/cpp/Stringification.html

顺便说一句,AFAIK 您将无法使用预处理器将您的 {} 变成 []

【讨论】:

    【解决方案4】:

    您最好使用Boost.Preprocessor 提供的预处理器数据结构。那里的所有宏都在标准 C89 中工作。

    【讨论】:

    • 我现在做不到。而且 Boost.Preprocessor 对我的 4K RAM 板来说太大了。
    • 您没有在 4K RAM 板上运行 C 预处理;你在开发机器上运行它。输出被下载到您的电路板,或烧入 EEPROM,或其他任何东西。
    • 我要生成一个hex文件并下载到芯片的flash中。
    • 预处理器在编译前运行,输出没问题,Boist.PP只是一堆宏。
    猜你喜欢
    • 1970-01-01
    • 2017-08-27
    • 2013-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-15
    • 2013-06-23
    相关资源
    最近更新 更多