tags:

views:

190

answers:

8

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 };
+4  A: 

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 );
sbi
Strictly speaking, you are determining the length of the array...
larsmans
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".
Andrew Cecil
@Andrew: Either that or make `func()` capable of dealing with `NULL` arguments.
sbi
@larsmans: I have no idea what you're referring to.
sbi
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.
larsmans
@larsmans You are correct - I would prefer a solution with having to find the length of the list first.
Andrew Cecil
@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
@larsmans: Ah, I see. I read this as "i don't want to have to iterate over all the items twice". My fault.
sbi
@sbi - I meant to say "without having to find the length first" - oops.
Andrew Cecil
A: 

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.

DeadMG
A: 

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
+6  A: 

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 pass-the-end, always pass-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).

eq-
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.
larsmans
Still, +1 as I recommend this solution.
larsmans
@eq-: 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
(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-
@eq-: Looks great to me. I'll definitely use something like this if I ever run across a case for it.
Jon Purdy
+1  A: 

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
See my edited answer.
sbi
@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
Yes, he can. See my reply to eq-'s answer.
larsmans
@larsmans: you mean like create an iterator and iter1 == iter2 if and only if *iter1 == null?
Armen Tsirunyan
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
@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
@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
@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.
Andrew Cecil
@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
@jalf: I did. :) See my answer.
Andrew Cecil
A: 

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
+1  A: 
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
@Basilevs - 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?
Andrew Cecil
+3  A: 

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.

Andrew Cecil
@Basilevs - Are you referring to the static function or the static const variable nullNTI? This code runs correctly in VS2010.
Andrew Cecil
@Basilevs: Thanks, i've corrected it in the answer.
Andrew Cecil
@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).
Andrew Cecil
@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!
Andrew Cecil
Damn, I've just noticed you are an OP :)
Basilevs