Iterate over a C++ container in C

245 Views Asked by At

I am attempting to write a C wrapper for some C++ data structures. Right now I've got the following in foo.cpp

typedef std::map<unsigned int, void *> _Map;

extern "C"{
    void* map_create()
    {
        return reinterpret_cast<void*> (new _Map);
    }

    void map_put(void *map, unsigned int k, void *v)
    {
        Map *m = reinterpret_cast<_Map *> (map);
        m->insert(std::pair<unsigned int, void *>(k, v));
    }
}

In foo.h I've got

#ifdef __cplusplus
#define EXTERNC extern "C"
#else
#define EXTERNC
#endif
typedef void *Map;
EXTERNC void *map_create();
EXTERNC void map_put(void* map, unsigned int k, int v);

I include foo.h and I'm good to go.

Now, I wanted to iterate over a map and noticed C++ does that through iterators. I have no experience with C++ and don't know how iterators are implemented.

Can I iterate over a std::map using a C wrapper? How would these functions definitions look like and how would I use them in a for-loop in my C code?

2

There are 2 best solutions below

2
On BEST ANSWER

You won't be able to use iterators directly. You can, of course, do something along the lines of creating/releasing objects and obtain the values somehow. It isn't going to be efficient, though. It would look something like this:

typedef std::map<unsigned int, void*> map_type;
typedef map_type::iterator            map_iterator;

void* map_iterator_create(void* map) {
    return new map_iterator(map.begin());
}
void  map_iterator_destroy(void* it) {
    delete static_cast<map_iterator*>(it);
}
int map_iterator_next(void* map, void* it, unsigned int* key, void** value) {
    map_iterator* mit = static_cast<map_iterator*>(it);
    if (static_cast<map_type*>(map)->end() == *mit) {
        return 0; // no more elements
    }

    *key   = mit->first;
    *value = mit->second;
    ++mit;
    return 1;
}

This particular approach does reduce the flexibility coming with iterators a bit (e.g., it doesn't support subranges) but these could be supported at different costs. You'd use this iterator approach like this:

void*        it = map_iterator_create(map);
unsigned int key;
void*        value;
while (map_iterator_next(map, it, &key, &value)) {
    printf("key=%d value=%p\n", key, value);
}
map_iterator_destory(it);

It is probably more reasonable to not expose the iterator interface but rather an interface iterating over the sequence, e.g.:

extern "C" { typedef void (*map_function)(unsigned int, void*, void*); }
void map_iterate(void* map, map_function fun, void* userdata) {
    map_type* m = static_cast<map_type*>(m);
    std::for_each(m->begin(), m->end(),
                  [=](map_type::value_type const& v) {
                      fun(v.first, v.second, userdata);
                  });
}

... which would be used as

void my_iterating_fun(unsigned int key, void* value, void* userdata) {
     printf("key=%d value=%p\n", key, value);
}
// ...
map_iterator(map, my_iterating_fun, 0);

I haven't tried to compile the code (i.e., it is probably riddled with small typos) but something along those lines should work.

6
On

Here's something that won't compile, but gives you an idea on how I'd do it

class MyIterator {
  public:
    MyIterator(Map map) {
      it = map.begin();
    }

    void advance() {
      ++it;
    }

    bool isValid() {
      return it != map.end();
    }
  private:
    Map::const_iterator it;
    const Map map;
};

extern "C" {
void* newIterator(void* map) {
  return new MyIterator(static_cast<Map>(map));
}
void freeIterator(void* it) {
  delete static_cast<Map>(map);
}
void advanceIterator(void* it) {
  static_cast<MyIterator>(it)->advance();
}
void isValid(void* it) {
  static_cast<MyIterator>(it)->isValid();
}

int getKey(void* iterator) {...}
void* getValue(void* iterator) {...}
}