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:
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.
CodePudding user response:
This can be done during constant evaluation (compile-time) without using macros at all, using the technique described here. (See the couple of episodes before as well, to get a better context.)
While std::string
can be used at compile time, in this particular case a std::array
will suffice to do most of the heavy lifting, i.e. the creation of the repeated characters
template<int Size, char Char>
constexpr auto create_array()
{
std::array<char, Size> a;
std::fill(std::begin(a), std::end(a), Char);
return a;
}
Now, we can make a std::string_view
from this std::array
quite easily
template<int Size, char Char>
constexpr auto repeat()
{
constexpr auto a = create_array<Size, Char>();
auto const & static_a = make_static<a>();
return std::string_view(std::begin(static_a), std::end(static_a));
}
Note the make_static
helper function. The reason this function is needed is that a
is a local variable, and returning a string_view
to that data will lead to a dangling reference. The data needs to have static storage duration in order to create a string_view
from it.
The implementation of make_static
is really straightforward, and relies on the fact that non-type template parameters of class type have static storage duration, so a reference to this parameter can be passed around safely.
template<auto Data>
constexpr auto const & make_static()
{
return Data;
}
and now you get a string that can be used at compile time
static_assert(repeat<5, 'a'>() == "aaaaa"sv);
Here's a demo.
Note that in this example, the size and char are passed as template parameters to repeat
, but the general technique doesn't rely on that, as arguments can be passed to functions and used as constant expressions, by simply wrapping it in a constexpr lambda.
CodePudding user response:
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
.