5

Iterating over a NULL terminated string using for_each is possible:

const char *name = "Bob";

void func(const char &arg)
{
   cout << arg;
}

int main()
{
    for_each(name, name + strlen(name), func);
}

Is something similar possible for a NULL terminated list of strings (without having to determine the total length of the list first) such as:

const char *names[] = { "Bob", "Adam", "Simon", NULL };
  • 3
    In C++ a list of strings should be represented using `std::list` (or `std::vector` if that suits better). This would solve all your problems at once. – Björn Pollex Oct 27 '10 at 12:25
  • 3
    In what way similar? You _are_ determining the length of your string in your example. – eq- Oct 27 '10 at 12:26
  • @eq- I realise I am determining the length of the string in my example, but I would prefer not to. Also when there is a single string determining the length is trivial, but with a large array of strings it becomes more tedious. I really want something I can pass in as the end iterator that will cause the ending NULL in the array to be detected as the end of the list by for_each automatically. –  Oct 27 '10 at 12:31

10 Answers10

9

std::for_each "iterates" over a range, so to use it with an array of indeterminate length, you need to use custom iterators to signal the end of the array (on NULL member). If you insist on using NULL-terminated char* array, you could of course create your own for_each function for it, for example like this:

template <typename Function>
void for_each_in_null_terminated_cstring_array(const char** array, Function f)
{
    while (*array) {
        f(*array);
        array++;
    }
}

const char *names[] = { "Bob", "Adam", "Simon", NULL };
for_each_in_null_terminated_cstring_array(names, func);

I'm not really recommending this solution, though.

edit: Yes, more general is always more better, isn't it?

template <typename T, typename Function>
void for_each_in_null_terminated_array(T* array, Function f)
{
    while (*array) {
        f(*array);
        array++;
    }
}

(Here's the implementation of a null terminated ("false"-terminated) iterator I mentioned earlier - with a change or two based on suggestions below. It should be a real InputIterator)

template <class T>
class nt_iterator: public std::iterator<std::input_iterator_tag, T>
{
public:
    typedef typename nt_iterator<T>::pointer pointer;
    typedef typename nt_iterator<T>::value_type value_type;

    nt_iterator(): p(), pte(true) {}
    nt_iterator(pointer p_): p(p_), pte(!p_) {}
    nt_iterator(const nt_iterator<T>& rhs): p(rhs.p), pte(rhs.pte) {}
    nt_iterator<T>& operator++() {
        ++p;
        if (!*p) pte = true; // once past-the-end, always past-the-end
        return *this;
    }
    nt_iterator<T> operator++(int) {
        nt_iterator n(*this);
        operator++();
        return n;
    }
    bool operator==(const nt_iterator<T>& rhs) {
        return pte && rhs.pte || p == rhs.p;
    }
    bool operator!=(const nt_iterator<T>& rhs) {
        return !(operator==(rhs));
    }
    value_type operator*() { return *p; }

private:
    pointer p;
    bool pte; // past-the-end flag
};

And how it's used:

void print(const char* str);

int main()
{
    const char* array[] = {"One", "Two", "Three", NULL, "Will you see this?"};
    std::for_each(nt_iterator<const char*>(array),
                  nt_iterator<const char*>(),
                  print);
}

It's probably a bit slower than the loop version, because of the increased amount of equivalence checks - the speed difference is of course insignificant compared to, for example, printing text - but one should note that std::for_each does not magically make looping faster (in fact, you might be surprised to see how your compiler vendor defines the function - that is, if you are expecting too much).

Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175
eq-
  • 9,986
  • 36
  • 38
  • 3
    No, you don't need to determine the length *by definition*. This is possible with custom iterators. It'd be ugly, but it can be done. – Fred Foo Oct 27 '10 at 12:46
  • Still, +1 as I recommend this solution. – Fred Foo Oct 27 '10 at 13:13
  • This is quite handy! Small critiques: I would say that you oughtn't to use `== NULL` and should rely on either `operator!` or `== T()`, e.g., `pte = !*p;`. Also, the usual way of specifying a generic past-the-end iterator is with the default constructor, as for example `std::istream_iterator`. – Jon Purdy Oct 29 '10 at 19:33
  • (I re-added the code I pulled because it seemed unnecessary) I agree - I've kind of mixed NULL's and negations here and there. I actually first used `pte = !*p` but I was thinking all pointers and thought it seemed too tricky - it'd be the version I'd use in my own code, tho ;) – eq- Oct 29 '10 at 20:31
  • Looks great to me. I'll definitely use something like this if I ever run across a case for it. – Jon Purdy Oct 29 '10 at 20:44
  • Did you lose a `*` in the initialization of `pte`? Shouldn't it be `pte(!*p_)`? In the example, `T` is a `const char *`, so `pointer` would be `const char * *`, right? – Adrian McCarthy Jul 23 '13 at 18:00
3

With

const char *names[] = { "Bob", "Adam", "Simon" };

you can just call

std::for_each(names, names + sizeof(names)/sizeof(names[0]), func );

or, nicer, using two helper functions:

std::for_each(begin(names), end(names), func );

Of course, this fails the moment the array decays into a pointer (but at least the compiler won't accept it then). If you must rely on that trailing NULL, you either need to write your own looping function or count before-hand, as with std::strlen():

std::ptr_diff_t num = std::find( names
                               , names + std::numeric_limits<std::size_t>::max()
                               , NULL);
std::for_Each( names, names+num, func );
Community
  • 1
  • 1
sbi
  • 219,715
  • 46
  • 258
  • 445
  • And with the NULL terminated array of strings this technique works if you substract one from the end pointer - "names + sizeof(names)/sizeof(names[0]) - 1". –  Oct 27 '10 at 12:39
  • @Andrew: Either that or make `func()` capable of dealing with `NULL` arguments. – sbi Oct 27 '10 at 12:40
  • 1
    I am referring to the OP's requirement "without having to determine the total length of the list first". You're using `sizeof` to do just that. – Fred Foo Oct 27 '10 at 12:44
  • @larsmans You are correct - I would prefer a solution with having to find the length of the list first. –  Oct 27 '10 at 12:49
  • @Andrew: Then you should pick [eq's answer](http://stackoverflow.com/questions/4033071/iterate-over-null-terminated-array-of-strings-using-for-each/4033248#4033248). It's good and I up-voted it. – sbi Oct 27 '10 at 12:52
  • @larsmans: Ah, I see. I read this as "i don't want to have to iterate over all the items twice". My fault. – sbi Oct 27 '10 at 13:04
  • @sbi - I meant to say "without having to find the length first" - oops. –  Oct 27 '10 at 13:14
3

Expanding on Basilevs answer with a fully working solution.

A custom iterator may be defined as follows:

template <class T>
class NullTerminatedIterator
    :public std::iterator<std::forward_iterator_tag,
    T,ptrdiff_t,const T*,const T&>
{
public:
    typedef NullTerminatedIterator<T> NTI;

    NullTerminatedIterator(T * start): current(start) {}
    NTI & operator++() {current++; return *this;}
    T & operator*() { return *current; } 
    static NTI end() { return NTI(0); }
    bool operator==(const NTI & that) { return *current == *that.current; }
    bool operator!=(const NTI & that) { return *current != *that.current; }
private:
    T * current;
};

And then used like so:

const char *names[] = {"Bob", "Adam", "Simon", NULL};

NullTerminatedIterator<char*> iter((char**)names);

for_each(iter, NullTerminatedIterator<char*>::end(), func);

The base class for the NullTerminatedIterator are taken from this custom iterator question.

This only traverses the list during the for_each call, as requested.

Community
  • 1
  • 1
  • @Basilevs - Are you referring to the static function or the static const variable nullNTI? This code runs correctly in VS2010. –  Oct 28 '10 at 06:22
  • @Basilevs - Maybe it was just the cast of 0 to (T*) that I needed, but it wouldn't compile in VS2010 when trying to initialise with NTI(0). –  Oct 28 '10 at 11:11
  • @Basilevs - Turns out the NTI(0) not working was just a consequence of something else not compiling at the time - fixed in answer now, along with correct use of NullTerminatedIterator in for_each line. Thanks again! –  Oct 28 '10 at 12:03
  • Damn, I've just noticed you are an OP :) – Basilevs Oct 28 '10 at 15:32
1

There have been multiple answers that tell you what you can do instead. However the answer to your particular question is just "no, you can't" :)

Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434
  • @sbi: nah... that still iterates over the sequence two times, I think what the OP wants is that the range be iterated once. But with for_each. I believe it's impossible – Armen Tsirunyan Oct 27 '10 at 12:43
  • Yes, he can. See my reply to eq-'s answer. – Fred Foo Oct 27 '10 at 12:51
  • @larsmans: you mean like create an iterator and iter1 == iter2 if and only if *iter1 == null? – Armen Tsirunyan Oct 27 '10 at 13:10
  • 1
    Yeah, it's possible. You just need to write an iterator type in which the end iterator is "special" and doesn't point to a specific location, but instead just compares equal to any iterator that points to a null value. It's kind of similar to the stream iterators already in the standard library. – jalf Oct 27 '10 at 13:11
  • @larsmans, @jalf: Yeah, I guess. But it's not worth it, is it? Whatever, my answer is wrong, I'll delete it soon – Armen Tsirunyan Oct 27 '10 at 13:13
  • 1
    @Armen: I never said it was worth it. Just that it is possible ;) (It's also one of the stronger arguments in favor of Alexandrescu's "iterators must go" thing. Something like this would be much more natural to express as a range.) – jalf Oct 27 '10 at 14:41
  • @jalf: I would love to see a solution that used some kind of custom iterator to allow the standard for_each to be used efficiently. –  Oct 27 '10 at 14:45
  • @Andrew: Well, write one. ;) If you use Boost's Iterator library to do the heavy lifting, it's not that much work. I've done something similar before. :) – jalf Oct 27 '10 at 18:47
  • @jalf: I did. :) See my answer. –  Oct 27 '10 at 18:48
0
    // C version 
    const char* vars[16]={"$USER","$HOME","$DISPLAY","$PASSWORD",0};

    for(const char** pc = vars; *pc!=0; pc++)
    {
            printf("%s",*pc);
    }
user2195463
  • 321
  • 5
  • 8
0

You can use sizeof() for compile-time sized arrays.

const char *names[] = { "Bob", "Adam", "Simon" };
std::for_each(names, names + sizeof(names)/sizeof(*names), [](const char* arg) {
    std::cout << arg << "\n";
});
std::cin.get();

For dynamically sized arrays, you should be using std::vector<std::string> and iterate over that.

Excuse my use of lambdas, your compiler (probably) doesn't support them.

Puppy
  • 144,682
  • 38
  • 256
  • 465
0

add them to a container instead, and iterate over them using for_each
i use a vector for my example:

void function(string name)
{
    cout << name;
}

int main()
{
    vector<string> nameVector;

    nameVector.push_back("Bob");
    nameVector.push_back("Adam");
    nameVector.push_back("Simon");

    for_each(nameVector.begin(), nameVector.end(), function);

    return 0;
}
Tom
  • 908
  • 1
  • 6
  • 14
0

Can you not replace the argument passed to func with a reference to a pointer to const char, in order to achieve what you want. Kind of like this:

const char *names[] = { "Bob", "Adam", "Simon" };

void func( const char* &arg )
{
   cout << arg << endl;
}

int main()
{
    for_each( names, 
              names + sizeof( names ) / sizeof( names[ 0 ] ), 
              func );
}

And obviously for a NULL-terminated string array, just subtract 1 from the array size...

AndyUK
  • 3,933
  • 7
  • 42
  • 44
0
template <class T>
struct NullTerminatedIterator {
  typedef NullTerminatedIterator<T> NTI;
  T * current;
  NTI & operator++() {current++; return this;}
  T & operator*() {return *current;} 
  NullTerminatedIterator(T * start): current(start) {}
  static NTI end() {return  NTI(0);}
  bool operator==(const NTI & that) {return current==that.current;}

}
Basilevs
  • 22,440
  • 15
  • 57
  • 102
  • I think this is what I want, but I can't quite get it to work in practice. What would the for_each line look like using this iterator? –  Oct 27 '10 at 17:25
0

I know this is not for_each, but I wanted to use old regular for-loop to do the same. This one is from the MSDN blog:

This reinterpretation of a double-null-terminated string as really a list of strings with an empty string as the terminator makes writing code to walk through a double-null-terminated string quite straightforward:

for (LPTSTR pszz = pszzStart; *pszz; pszz += lstrlen(pszz) + 1) {
   // ... do something with pszz ...
}

Looks kinda clean to me!

Ciantic
  • 6,064
  • 4
  • 54
  • 49