How to wrap std::vector to use in pure C

Viewed 898

In my project I would like to use C++ and STL containers but have a problem that I have to include hw vendor headers (and link vendor lib and use some vendor C sources that also include the vendor headers) written in ANSI C, that contain C++ reserved keywords, for example: vendor.h:

// Vendor header (read only)
    struct Vendor_Export_Struct {
    unsigned char* data;
};

struct Vendor_Export_Struct export; // <<<  compilation error under C++
union Vendor_Union {
    struct Vendor_Export_Struct export; // <<<  compilation error under C++
};

What included into C++ will cause errors during compile: expected unqualified id before ‘export’. So I'm forced to use pure C and thinking if it would be possible simply wrap STL vector to kind of C API like this (with C++ implementation behind):

cvect.h :

typedef void* Vect_Type;
typedef void** Vect_Iterator_Type;
typedef void* Vect_Data_Type;

Vect_Type Vect_New();
void Vect_PushBack(Vect_Type v, Vect_Data_Type d);
Vect_Iterator_Type Vect_Begin(Vect_Type v);
Vect_Iterator_Type Vect_End(Vect_Type v);
Vect_Iterator_Type Vect_Next(Vect_Type v, Vect_Iterator_Type it);

But problem is how to pass on the vector and iterator. I think I would be forced to use reinterpret_cast when casting from std::vector<> -> void* -> std::vector<> on the C++ code side and still thinking how to cast/pass std::vector<>::iterator.

c_vect.cpp :

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include "c_vect.h"

typedef std::vector<void*> Vect_Container_Type;

Vect_Type Vect_New()
{
   return static_cast<Vect_Type>(new Vect_Container_Type);
}

void Vect_PushBack(Vect_Type v, Vect_Data_Type d)
{
   Vect_Container_Type& vref = *reinterpret_cast<Vect_Container_Type*>(v);
   vref.push_back(d);
}

Vect_Iterator_Type Vect_Begin(Vect_Type v)
{
   Vect_Container_Type& vref = *reinterpret_cast<Vect_Container_Type*>(v);
   return &*vref.begin();
}

Vect_Iterator_Type Vect_End(Vect_Type v)
{
   Vect_Container_Type& vref = *reinterpret_cast<Vect_Container_Type*>(v);
   return &*vref.end();
}

Vect_Iterator_Type Vect_Next(Vect_Type v, Vect_Iterator_Type it)
{
   Vect_Container_Type& vref = *reinterpret_cast<Vect_Container_Type*>(v);
   Vect_Container_Type::iterator it_ = static_cast<Vect_Container_Type::iterator>(it); //<<<< ugly and not portable

   if (it_ != vref.end())
   {
      ++it_;
      return &*it_;
   }

   return NULL;
}

main.c :

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "c_vect.h"
#include "vendor.h"

    typedef struct S_Connection {
    size_t id;
    char* name;
    union Vendor_Union u;
    Vect_Type sessions; // Another vector
} T_Connection;
typedef T_Connection* T_ConnectionPtr;

static void* makeConn(size_t id, const char* name)
{
    T_ConnectionPtr ptr = (T_ConnectionPtr)malloc(sizeof(T_Connection));
    ptr->id = id;
    ptr->name = (char*)malloc(strlen(name) + 1);
    strcpy(ptr->name, name);
    return ptr;
}

int main(int argc, char* argv[])
{
    Vect_Type conns = Vect_New();
    Vect_Iterator_Type it;

    unsigned i;
    for (i = 0; i < 10; ++i) {
        char name[20];
        sprintf(name, "conn_%03d", i);
        Vect_PushBack(conns, makeConn(i + 1, name));
    }

    // Iterate vector and access stored data
    for (it = Vect_Begin(conns);
         it != Vect_End(conns);
         it = Vect_Next(conns, it)) {
        T_ConnectionPtr cd = (T_ConnectionPtr)*it;
    }

    return 0;
}

So I'm not sure if all this is a good idea, probably not from several reasons. I just would like to avoid another redundant C vector implementation, take profit from STL iterators. The final code should be portable. Has someone been solving a similar problem like this? Or do you have a better idea how to cope with this?

2 Answers

I am by no means an expert in this topic, but I've run into this problem before, and I've found that there are two options that seem to work:

  1. You can write a vector library in C yourself. I've often found that this isn't really as hard as it may seem, and then you don't have code that is reliant upon the C++ STL, which won't be supported in a lot of the places one may want to use C, like in embedded systems, for example.
  2. You will have to make a header in the following form:
#ifdef __cplusplus
extern "C" {
#endif

/* Define all your C wrapper functions here */

/* For example: */
void VectorWrapper_Add(struct VectorWrapper *vector_wrapper, const struct VectorWrapper_DataType *data_to_add);

#ifdef __cplusplus
}
#endif

And then in the implementation file, you can put something like this:

#include "my_c_wrapper.h"

#include <vector>

/* Do something similar for all your functions... */

void VectorWrapper_Add(struct VectorWrapper *vector_wrapper, const struct VectorWrapper_DataType *data_to_add)
{
    /* Your implementation might look something like this for such a function: */
    vector_wrapper->_stl_vector->push_back(*data_to_add);
}

Most the time, what I end up doing is just creating my own libraries to manage these sorts of data structures; but again, the choice is yours.

I hope my answer helps you.

There are methods now to access the raw data array of the Vector:

  value_type* data() noexcept;
const value_type* data() const noexcept;

These extensions were added in C++11 exactly for your case, to interface third party libraries that only take the raw data array. Pass the returned pointer to C as an array that is also a pointer in C. You will likely need add vector.size() as another parameter.

Do not manipulate the vector while using the returned pointer.

Related