4

I am learning about macros and I want to know that whether it is possible to create a macro that repeats a given character literal a given number of times. For example:

Input:

repeatMacro('a', 5)

should produce(expand to) the output:

Expected Output:

aaaaa

Similarly, repeatMacro('r', 2) should produce:

rr

If possible, repeatMacro('r') should produce(expand to):

r

I have not worked with macros before so I don't know if this is a very basic thing to do.

I also don't know if there is any other way(other than macros) to do the same. If there is any other way(other than macro) to do this, I would prefer that way instead of using macros.

For example, I know that we can do things like the following with macros:

#define x *
int x func()
{
    int*ptr = new int(4);
    return ptr;
}

So I want to do the same with repeatMacro. Something like shown below:

#define repeatMacro(str, n)  // what should come here for the below example to work
int repeatMacro('*',2) func()
{
    int** ptr = new int*[5]();
    return ptr;
}
Jason
  • 36,170
  • 5
  • 26
  • 60
  • Why macro? You can write a constexpr function. – lurker Apr 10 '22 at 12:15
  • @lurker I didn't know that it was possible using `constexpr` function. – Jason Apr 10 '22 at 12:16
  • Try it out ... :) – lurker Apr 10 '22 at 12:18
  • Sorry, Anya, you have me stumped there now that I've played with it a bit. The `std::string` lets you easily perform this function `std::string(char, number)` but that cannot be in a constexpr. It could be inlined. I have not stumbled upon a macro technique to replicate a character or a string a given number of times at compile time. – lurker Apr 10 '22 at 12:37
  • Can repeatMacro return a `std::array` eg `std::array{'a', 'a', 'a'}`? This allows the result to be `constexpr`. – 康桓瑋 Apr 10 '22 at 13:49
  • @康桓瑋 No. The main requirement of my question is that it must behave like a macro. That is, it should be pure substitution. – Jason Apr 10 '22 at 13:52
  • Does this answer your question? [C preprocessor macro for returning a string repeated a certain number of times](https://stackoverflow.com/questions/8551418/c-preprocessor-macro-for-returning-a-string-repeated-a-certain-number-of-times) – 康桓瑋 Apr 10 '22 at 13:57
  • What if it would be called as `repeatMacro(a, 5)` without the single quotes? – aschepler Apr 10 '22 at 14:01
  • @aschepler Yes that could work i suppose(also i think it would be the next best thing). But the outcome should be the same as i gave in the original question – Jason Apr 10 '22 at 14:09
  • And the result will be used as a variable/function name, not as a string, right? – aschepler Apr 10 '22 at 14:13
  • Could you add an example of how you would use this function/macro? I assumed (in my answer) that you want to create a string like thing with the repeated characters, but based on some of the comments, it's unclear if you actually want that, or instead want to just have text substitution in the source code. An example usage might help clarify that. – cigien Apr 10 '22 at 14:29
  • @aschepler I've added an example usage at the end of my question. Check out the edited question. – Jason Apr 10 '22 at 17:05
  • Thanks for the edit. Note that for the specific case shown in the example, i.e. adding some number of `*` to `int`, it can be done without macros, but that won't work for the general case of repeating characters. – cigien Apr 10 '22 at 17:17
  • @cigien Is there a reason for why what you're suggesting won't work for the general case which is for any arbitrary character literal and for any arbitrary integer as the 2nd argument? I think i would be happy to see how it will work for the specific case of `int` given in my example. Although i wouldn't be able to accept that as the correct answer since the original question requires a general case, but i will still be able to appreciate(by upvoting) it. Thanks – Jason Apr 10 '22 at 17:23
  • 2
    To add pointers to a type you could do something like [this](https://godbolt.org/z/sbd85zvK8). It's specific to type manipulation which is why it won't work for the general case. – cigien Apr 10 '22 at 17:32
  • @cigien Wow, this looks nice. Thanks again. At least [this](https://godbolt.org/z/sbd85zvK8) answers my question partially. – Jason Apr 10 '22 at 17:43

1 Answers1

1

The preprocessor doesn't have any way to do anything with character literals like 'a' other than output them as is, so I don't think that form is possible. Instead, we'll need to work with identifier preprocessor tokens like a.

Working with numbers in the preprocessor is always tricky, but Boost.Preprocessor has tools to help.

#include <boost/preprocessor/seq/cat.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/facilities/overload.hpp>

#define REPEAT_MACRO_SEQ_ELEM(z, n, data) (data)
#define REPEAT_MACRO_2(c, n) \
    BOOST_PP_SEQ_CAT(BOOST_PP_REPEAT(n, REPEAT_MACRO_SEQ_ELEM, c))
#define REPEAT_MACRO_1(c) c

#define REPEAT_MACRO(...) \
    BOOST_PP_OVERLOAD(REPEAT_MACRO_, __VA_ARGS__)(__VA_ARGS__)

I've renamed your macro REPEAT_MACRO, because it's common practice to use only uppercase for a preprocessor macro name, giving a hint to the code reader that it's a macro.

REPEAT_MACRO_2 uses BOOST_PP_SEQ_CAT to paste together a sequence of preprocessor tokens. It expects input in the form of a "sequence" like (a)(a)(a)(a)(a), so we pass it the result of BOOST_PP_REPEAT, using REPEAT_MACRO_SEQ_ELEM as the operator to add the parentheses around the input token.

To take care of allowing REPEAT_MACRO(r) producing just r, there's BOOST_PP_OVERLOAD. It will paste the number of arguments onto the end of the given macro prefix REPEAT_MACRO_, so that REPEAT_MACRO(c) calls REPEAT_MACRO_1(c) and REPEAT_MACRO(c,n) calls REPEAT_MACRO_2(c,n).

This solution doesn't really require that the input identifier is just one character, and I'm not sure there's any way to require that. So you also get that REPEAT_MACRO(xyz, 3) gives xyzxyzxyz.

For the case from comments of repeating a * as in function type int repeatMacro(*, 2) func(), you would not want to concatenate the preprocessor tokens, since there's no such token as **. To get that behavior instead of the identifier-pasting would be instead:

#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/facilities/overload.hpp>

#define REPEAT_MACRO_ID_OP
#define REPEAT_MACRO_2(c, n) BOOST_PP_REPEAT(n, REPEAT_MACRO_ID_OP, c)
#define REPEAT_MACRO_1(c) c

#define REPEAT_MACRO(...) \
    BOOST_PP_OVERLOAD(REPEAT_MACRO_, __VA_ARGS__)(__VA_ARGS__)

I doubt there's any way to get a single macro doing the paste for identifiers and just repetition for punctuation.

aschepler
  • 70,891
  • 9
  • 107
  • 161