tags:

views:

99

answers:

3

I am mixing some C and C++ libraries and have only a single pointer available to do some work in a callback function. All I need to do is iterate through a vector. Here's a simplified, untested example:


bool call_back(void* data){
  done=...
  if (!done) cout << *data++ << endl;
  return done;
}

Note that this function is in an extern "C" block in C++. call_back will be called until true is returned. I want it to cout the next element each time it's called. data is a pointer to something that I can pass from elsewhere in the code (an iterator in the above example, but can be anything). Something from data will likely be used to calculate done. I see two obvious options to give to data:

  1. Have data point to my vector.
  2. Have data point to an iterator of my vector.

I can't use an iterator without having the .end() method available, right? I can't use a vector alone (unless maybe I start removing its data). I could make a struct with both vector and iterator, but is there a better way? What would you do?

A: 

How about dereferencing the iterator and passing it's value to call_back? Then increment it after the function returns?

Duracell
+1  A: 

I can't use an iterator without having the .end() method available, right?

No. You can use the iterator with the result of invoking the .end() function. You don't need to keep calling the .end() function... so if you just store both iterators, then you're golden.

I can't use a vector alone (unless maybe I start removing its data).

Not alone, but with a std::size_t index then that's all you would need.

I could make a struct with both vector and iterator, but is there a better way? What would you do?

If you don't have to worry about supporting other container types, then I would use:

 template<typename T> struct CALLBACK_DATA
 {
      std::vector<T>* array;
      std::size_t index;
 };

If you might have to support multiple container types, then I would use:

 template<typename T> struct CALLBACK_DATA
 {
     typedef std::vector<T> container_type;
     typedef typename std::vector<T>::const_iterator const_iterator;
     const_iterator current;
     const_iterator end;
 };

So, yeah, I would either pass the vector and an index or a pair of iterators, and I would construct a struct to hold the data. If you want to avoid creating a struct, then you could use std::pair, however I personally think it is more readable to simply create a custom struct to hold this information.

Michael Aaron Safyan
+3  A: 

Why not have data point to a structure with all the information you need.

The point about the old "C" style callbacks is that a void* can point to any object. Your callback function knows what the type is, but it can be anything.

typedef struct Plop
{
    std::vector<int>::iterator begin;
    std::vector<int>::iterator end;
} Plop;

bool call_back(void* data)
{
    // Or static_cast<> for the pedantic.
    // I like reinterpret_cast<> because it is a clue to humans that this is dangerious
    // and as long as the object was originally a Plop* pointer it is guaranteed to work.
    Plop*   info = reinterpret_cast<Plop*>(data);

    bool    done= info.begin == info.end;

    if (!done) cout << *data++ << endl;
    return done;
}
Martin York
I really wanted to avoid a new struct just for this function. Interesting comments about casting.
User1