18

Does someone know of any C99 preprocessor magic that allows for creating a string consisting of another string repeated N times?

E.g.

STRREP( "%s ", 3 )

becomes

"%s %s %s "

after preprocessing.

The only thing I could think of myself was something like this

#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
...

which works well, but is ugly as I have to define a macro for each repetition length manually. I want to use it together with variadic macros and the macro returning the number of macro arguments shown here.

Community
  • 1
  • 1
sonntam
  • 358
  • 1
  • 2
  • 14
  • 2
    I'm pretty sure it's not possible. See another question here which is similar - http://stackoverflow.com/questions/319328/writing-a-while-loop-in-the-c-preprocessor – mattjgalloway Dec 18 '11 at 11:34
  • Thank you, @mattjgalloway. You seem to be right. There is no way of variable recursion length in pure C99 using the preprocessor. So my idea seems to be the only (ugly!) way. – sonntam Dec 20 '11 at 10:08

5 Answers5

52

Since it's a macro and N is a numeric constant anyway, how about this?

#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;
}
Lucas Gabriel Sánchez
  • 40,116
  • 20
  • 56
  • 83
Alexey Frunze
  • 61,140
  • 12
  • 83
  • 180
7

My suggestion is to use the boost.

E.g.

#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;
}
BLUEPIXY
  • 39,699
  • 7
  • 33
  • 70
  • @Alex , This code work in C. using the template(like to take advantage of C++) does not use C. it can use such a preprocessor. – BLUEPIXY Dec 19 '11 at 01:20
  • `BOOST_PP_REPEAT` has `BOOST_PP_LIMIT_REPEAT` limit set to `256`(as for 1.48). Anyway, +1 for solution. – maverik Dec 19 '11 at 09:24
  • Thank supplement. I think enough if ordinary 256. – BLUEPIXY Dec 19 '11 at 09:49
  • Thank you. As in principle there seems to be no cleaner way than what I did, I'll stick to the Boost-header as it looks quite elegant. – sonntam Dec 20 '11 at 10:14
  • @Marcus S , Yes, but it is abnormal even if you look inside boost. However, better than making from scratch. – BLUEPIXY Dec 20 '11 at 14:05
4

I recently discovered a recursion scheme with the CPP c-preprocessor file inclusion mechanism over the __INCLUDE_LEVEL__ preprocessor literal which is treated automatically - so maybe this algorithm only works for gcc ?!?

  • The algorithm is conceptually unlimited, it can be extended with additional file indirection.
  • The herin presented code handles an ITERATION_COUNT from 0-39202
  • With the comment/uncomment of the ITERATION_SEPARATOR you can generate N elements, or 1 element with N concatenations, suitable for string repetitions.
  • The ITERATION_ELEMENT macro is used as the "repetition element"

You can compile the code regulary, without any additional defines. The macro invocation inside the code is idempotent.

An exemplary output:

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

0-1591 Counter

1592 Elements


iterate.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 *));

}

iterate.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

loop.h:

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

How about something like this?

#define DUP2(str) str str
#define DUP4(str) DUP2(str) DUP2(str)
#define DUP8(str) DUP4(str) DUP4(str)
#define DUP16(str) DUP8(str) DUP8(str)
#define DUP32(str) DUP16(str) DUP16(str)
#define DUP64(str) DUP32(str) DUP32(str)
#define DUP128(str) DUP64(str) DUP64(str)
#define DUP256(str) DUP128(str) DUP128(str)
#define DUP512(str) DUP256(str) DUP256(str)
#define DUP1024(str) DUP512(str) DUP512(str)
PROgram52bc
  • 98
  • 1
  • 1
  • 8
  • And how would one use this to duplicate a string, e.g., 1023 times? (With 1023 being a numerical input.) – Wrzlprmft Apr 26 '22 at 16:53
0

Not sure whether it can be done with the macro but you can do it with the function like:

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;
}

Now you can use it:

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

UPD: If you want to avoid malloc/free this is a variant of the first code:

/* .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;
}

Now:

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

OTOH, this looks even uglier than the first one.

maverik
  • 5,508
  • 3
  • 35
  • 55
  • 5
    Of course, it can be done using functions, but I'd like to have the string known at compile time. – sonntam Dec 20 '11 at 10:13