How Can I Use Boost preprocessor to Expand a String

962 views Asked by At

I want to expand a string of unknown length with Boost's preprocessor library.

For example I want this:

const string foo{"bar"};

To be expanded by my macro to this:

foo[0], foo[1], foo[2], '\0'

Here is my code, which I have mostly copied from here:

#include <boost/preprocessor/arithmetic/add.hpp>
#include <boost/preprocessor/arithmetic/inc.hpp>
#include <boost/preprocessor/arithmetic/sub.hpp>
#include <boost/preprocessor/control/deduce_d.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/tuple/elem.hpp>

#define EXPAND(first, last) BOOST_PP_REPEAT( BOOST_PP_INC( BOOST_PP_SUB((last), (first)), EXPAND_M, ((first), BOOST_DEDUCE_D())), '\0'

#define EXPAND_M(z, n, data) EXPAND_M_2((n), BOOST_PP_TUPLE_ELEM(2, 0, (data)), BOOST_PP_TUPLE_ELEM(2, 1, (data)))

#define EXPAND_M_2(n, first, d) foo[BOOST_PP_ADD_D((d), (n), (first))],

Can I just use it like this?

const string foo{"bar"};

cout << string{ EXPAND(0, foo.size()) } << endl;
2

There are 2 answers

0
Columbo On BEST ANSWER

As the preprocessor only works with tokens you will have to provide the length of the string passed to foo as a hard-coded magic constant, i.e. an integer literal. There's no way around this.
And as this integer literal would be independent from the string literal, the whole approach is error-prone and should be avoided.

Try to use variadic templates instead, if flexible expansions are what you are looking for. (It's hard to tell what you should use as you didn't provide the use-case you need this for!)

7
sehe On

Are you simply looking for a way to include the (trailing) NUL character?

#include <algorithm>
#include <iterator>
#include <iostream>
#include <sstream>

template <typename R = std::string, size_t N>
R stringlit(char const(&lit)[N]) {
    return R(lit+0, lit+N);
}

static std::string const foo(stringlit("bar"));
static auto        const bar(stringlit<std::vector<int>>("bar"));

int main() { 
    std::cout << "foo: ";
    for(char ch : bar)
        std::cout << std::showbase << std::hex << static_cast<int>(ch) << " ";

    std::cout << "\nbar: ";
    for(int i : bar)
        std::cout << std::showbase << std::hex << i << " ";
}

Prints

foo: 0x62 0x61 0x72 0 
bar: 0x62 0x61 0x72 0 

Perhaps related variation:

You can also employ template aliases to be able to specify the array literal's size explicitly with a temporary:

template <typename T> using Alias = T;

static std::string const foo(stringlit(Alias<char[7]>{"bar"})); // 4 trailing '\0's