2

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;
Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • 2
    The length must be availabe as a literal. – Columbo Dec 22 '14 at 13:45
  • @Columbo Welp, that kinda shoots down this whole plan. I guess it makes sense though. Since this is preprocessor, how would the preprocessor know when to stop? My code would be cleaner if I could somehow tie the value back to `foo` rather than using a hardcoded `3`. Do you know of any way to do that, as long as foo is defined at compile time of course? – Jonathan Mee Dec 22 '14 at 13:59
  • 1
    The preprocessor only works with tokens. The token 3 must be included somewhere, you can even not write a function that gives you the length of a string literal as an integer literal. – Columbo Dec 22 '14 at 14:02
  • @Columbo Could you write this as an answer for me to accept? Not that I'm happy about it since it means that I've just wasted a lot of time :( – Jonathan Mee Dec 22 '14 at 14:03
  • I am somehow unhappy with my answer (and the fact I got upvotes for it). Can you provide more context? Are you simply opting to expand a string into a sequence of characters? That is indeed possible by using an upper limit, say, 256. Boost.PP's repeat is possible, but an own, binary recursive approach will do it as well. ' – Columbo Jan 01 '15 at 23:48
  • @Columbo I can provide a more precise question, however I feel that your answer is the one I needed to hear. I'm trying to use [placement new](http://stackoverflow.com/a/19793639/2642059) to store a `const char[]` in my managed memory (using an `initializer_list`). The string that I'm storing is from a define, but I don't want to have to go change the code each time the defined string changes. Basically what your answer told me is that I must use `string` and get over my plans collapse. – Jonathan Mee Jan 05 '15 at 03:13

2 Answers2

3

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!)

Columbo
  • 60,038
  • 8
  • 155
  • 203
3

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
sehe
  • 374,641
  • 47
  • 450
  • 633
  • What is this witchcraft? `__PRETTY_FUNCTION__`? This is some fun code, but sadly, I was in fact trying to create an `initializer_list`. Which [Columbo](http://stackoverflow.com/users/3647361/columbo) has helped me realize cannot be accomplished without explicitly specifying the variable size :( – Jonathan Mee Dec 23 '14 at 00:49
  • That pretty function name was purely for debugging. My code has the same effect add the initializer list in your sample. – sehe Dec 23 '14 at 06:09
  • Well I understood what you were doing until you added that `Alias` code. Now I'm not sure I understand. What's going on there? – Jonathan Mee Dec 23 '14 at 11:27
  • @JonathanMee That was just bonus (because indeed the question wasn't exactly clear on why you want what you describe). It just generalizes the notion of a string literal initializing a char-array-reference in such a way that you can override the lenght of the char-array. Feel free to just ignore that. – sehe Dec 23 '14 at 11:29
  • So the code I have here was supposed to solve this question: http://stackoverflow.com/questions/27504692/how-do-i-use-a-variable-in-news-value-initialization I think the problem is that what I want is the intermediate step, with the `initializer_list`. You are giving me a better way to get to the end step, unless I have a gross misunderstanding somewhere. You would concur that what I was trying to do with the `initializer_list` is impossible, right? – Jonathan Mee Dec 23 '14 at 11:33
  • @JonathanMee Yes on all accounts [check](http://en.cppreference.com/w/cpp/utility/initializer_list). I've just generalized the answer somewhat to show that it also applies to other target types than just std::string. – sehe Dec 23 '14 at 11:40
  • I'm sorry I can only give you one upvote here, I learned a lot from your post, but I think for my original question [Columbo](http://stackoverflow.com/users/3647361/columbo) has the right answer. – Jonathan Mee Dec 24 '14 at 14:40