用于返回重复一定次数的字符串的 C 预处理器宏

Posted

技术标签:

【中文标题】用于返回重复一定次数的字符串的 C 预处理器宏【英文标题】:C preprocessor macro for returning a string repeated a certain number of times 【发布时间】:2012-01-22 23:57:20 【问题描述】:

有人知道任何 C99 预处理器魔法,它允许创建一个由另一个重复 N 次的字符串组成的字符串吗?

例如

STRREP( "%s ", 3 )

变成

"%s %s %s "

预处理后。

我唯一能想到的就是这样

#define STRREP( str, N ) STRREP_##N( str )    
#define STRREP_0(str) ""
#define STRREP_1(str) str
#define STRREP_2(str) str str
#define STRREP_3(str) str str str
...

效果很好,但很难看,因为我必须手动为每个重复长度定义一个宏。我想将它与可变参数宏和返回显示here 的宏参数数量的宏一起使用。

【问题讨论】:

我很确定这是不可能的。在此处查看另一个类似的问题 - ***.com/questions/319328/… 谢谢你,@mattjgalloway。你似乎是对的。使用预处理器在纯 C99 中没有可变递归长度的方法。所以我的想法似乎是唯一的(丑陋的!)方式。 【参考方案1】:

既然它是一个宏,而 N 又是一个数字常量,那这个怎么样?

#include <stdio.h>

#define REP0(X)
#define REP1(X) X
#define REP2(X) REP1(X) X
#define REP3(X) REP2(X) X
#define REP4(X) REP3(X) X
#define REP5(X) REP4(X) X
#define REP6(X) REP5(X) X
#define REP7(X) REP6(X) X
#define REP8(X) REP7(X) X
#define REP9(X) REP8(X) X
#define REP10(X) REP9(X) X

#define REP(HUNDREDS,TENS,ONES,X) \
  REP##HUNDREDS(REP10(REP10(X))) \
  REP##TENS(REP10(X)) \
  REP##ONES(X)

int main(void)

  printf(REP(9,0,7, "*")); // "*" repeated 907 times
  printf(REP(0,9,2, "#")); // "#" repeated 92 times
  printf(REP(0,0,1, "@")); // "@" repeated 1 times
  return 0;

【讨论】:

好主意!我会保密的。谢谢。 *吐得满屏*,不过用起来还是很简单的+1【参考方案2】:

我的建议是使用 boost。

例如

#include <stdio.h>
#include <boost/preprocessor/repetition/repeat.hpp>

#define Fold(z, n, text)  text

#define STRREP(str, n) BOOST_PP_REPEAT(n, Fold, str)

int main()
    printf("%s\n", STRREP("%s ", 3));//STRREP("%s ", 3) -> "%s %s %s "
    return 0;

【讨论】:

@Alex ,此代码在 C 中工作。使用模板(喜欢利用 C++)不使用 C。它可以使用这样的预处理器。 BOOST_PP_REPEATBOOST_PP_LIMIT_REPEAT 限制设置为 256(对于 1.48)。无论如何,为解决方案 +1。 感谢补充。我觉得普通的25​​6就够了。 谢谢。原则上似乎没有比我所做的更清洁的方法,我会坚持使用 Boost-header,因为它看起来非常优雅。 @Marcus S ,是的,但是即使你在 boost 里面看也是不正常的。但是,总比从头开始好。【参考方案3】:

我最近在自动处理的 __INCLUDE_LEVEL__ 预处理器文字上发现了一个使用 CPP c 预处理器文件包含机制的递归方案 - 所以也许这个算法只适用于 gcc ?!?

该算法在概念上是无限的,它可以通过额外的文件间接进行扩展。 herin 提供的代码处理从 0 到 39202 的 ITERATION_COUNT 使用 ITERATION_SEPARATOR 的注释/取消注释,您可以 生成 N 个元素,或 1 个具有 N 个连接的元素,适用于字符串重复。 ITERATION_ELEMENT 宏用作“重复元素”

您可以定期编译代码,无需任何额外定义。代码里面的宏调用是幂等的。

示例输出:

> gcc iterate.c -o iterate -Wall -s -O3 && ./iterate.exe

0-1591 计数器

1592 个元素


迭代.c:

#include <stdio.h>
#include <inttypes.h>

int main(void) 

const char * preproc_array[] = 
#define ITERATION_COUNT         1592                //0-(199*197-1)39202 (maximum counter)
#define ITERATION_SEPARATOR ,                       //this macro, if active, determines wheather there exits N separate elements otherwise, if outcommented, just 1 element with N concatenations
#define ITERATION_ELEMENT   0-__COUNTER__ Counter\n //the expanded macro as an arbitrary element
#include "iterate.h"
;

return !printf("%s%"PRIu32" Elements",preproc_array[
#ifndef NO_ITERATION_SEPARATOR
__COUNTER__-1
#else
0
#endif
], sizeof(preproc_array)/sizeof(const char *));


迭代.h:

#define     ITERATION_START 1   //start index of first inclusion
#define     ITERATION_LIMIT     199 //conforming to CPP preprocessor manual pg. 54 chapter 11.5, a limit of 200 is set arbitrary
#define     ITERATION(...)      _ITERATION(__VA_ARGS__)
#define     _ITERATION(...)     #__VA_ARGS__ ITERATION_SEPARATOR

#ifndef ITERATION_SEPARATOR
#define ITERATION_SEPARATOR
#define NO_ITERATION_SEPARATOR
#endif   

//here begins the recursive algorithm via preprocessor file inclusion, enable the warnings if you want to see how it loops through

#if __INCLUDE_LEVEL__   <=  ITERATION_COUNT/ITERATION_LIMIT
//~ #warning DIV
#define ITERATION_END   ITERATION_COUNT/ITERATION_LIMIT+3 // + offset
#include "loop.h"
#define ITERATION_END   ITERATION_LIMIT
#include "loop.h"
#include "iterate.h"
#endif

#if __INCLUDE_LEVEL__   ==  ITERATION_START
//~ #warning MOD
#define ITERATION_END   ITERATION_COUNT%ITERATION_LIMIT+ITERATION_START
#include "loop.h"
#if ITERATION_COUNT     %   ITERATION_LIMIT
#define ITERATION_END   3   // + offset
#include "loop.h"
#endif
#endif

//end of alogrithm

循环.h:

#if __INCLUDE_LEVEL__   <   ITERATION_END
#include "loop.h"
ITERATION(ITERATION_ELEMENT)
#undef ITERATION_END
#endif

【讨论】:

【参考方案4】:

不确定是否可以使用宏来完成,但您可以使用以下函数来完成:

char *strrep(const char *str, int nrep)

    if (nrep <= 0 || !str) return NULL;
    char *buf = malloc(strlen(str) * nrep + 1);
    if (!buf) return NULL;
    for (int i = 0; i < nrep; ++i) 
        strcat(buf, str);
    
    return buf;

现在你可以使用它了:

char *r = strrep("%s", 3);
if (r) 
    ...
    free(r);

UPD:如果你想避免malloc/free,这是第一个代码的变体:

/* .h */
#define STRREP_MAX_CHARS 1024
#define STRREP_INIT static char __strrep_buffer[STRREP_MAX_CHARS]
#define STRREP(str, nrep) strrep(str, nrep) ? __strrep_buffer : ""

char *strrep(const char *str, int nrep);

/* .c */
STRREP_INIT;

char *strrep(const char *str, int nrep)

    if (nrep <= 0 || !str) return 0;
    if (strlen(str) * nrep >= STRREP_MAX_CHARS) return 0;
    memset(__strrep_buffer, 0, STRREP_MAX_CHARS);
    for (int i = 0; i < nrep; ++i) 
        strcat(__strrep_buffer, str);
    
    return __strrep_buffer;

现在:

printf("%s\n", STRREP("%s", 3));

OTOH,这看起来比第一个更难看。

【讨论】:

当然可以使用函数来完成,但我希望在编译时知道字符串。

以上是关于用于返回重复一定次数的字符串的 C 预处理器宏的主要内容,如果未能解决你的问题,请参考以下文章

用引号定义C ++预处理器宏[重复]

C 预处理器宏参数在末尾带有空格以进行连接?

带有 2 个参数的 C 预处理器宏问题

Doxygen C预处理器宏文档样式

如何在 Rust 的 FFI 中使用 C 预处理器宏?

xctest - 预处理器宏