如何包装 std::vector 以在纯 C 中使用

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

在我的项目中,我想使用 C++ 和 STL 容器,但有一个问题,我必须包括 hw vendor headers(和 link vendor lib 并使用一些供应商 C 源,它们也包括用 ANSI C 编写的供应商 headers),其中包含 C++ 保留关键字,例如: 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++
};

C++ 中包含的内容会在编译期间导致错误:'export' 之前需要不合格的 id。所以我被迫使用纯 C 并思考是否有可能将 STL 向量简单地包装成 C API 这样的类型(后面有 C++ 实现):

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);

但问题是如何传递向量和迭代器。我想在 C++ 代码端从 std::vector<> -> void* -> std::vector<> 转换时我会被迫使用 reinterpret_cast 并且仍在思考如何 cast/pass std::vector<>::迭代器。

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;
}

所以我不确定这一切是否是个好主意,可能不是出于几个原因。我只是想避免另一个冗余的 C 向量实现,从 STL 迭代器中获利。最终代码应该是可移植的。有人一直在解决类似的问题吗?或者你有更好的办法来处理这个问题吗?

我绝不是这个主题的专家,但我以前 运行 解决过这个问题,我发现有两个选项似乎有效:

  1. 你可以自己用C写一个矢量库。我经常发现这并不像看起来那么难,而且您没有依赖于 C++ STL 的代码,它在很多人们可能想要的地方都不受支持例如在嵌入式系统中使用 C。
  2. 您必须按以下形式制作 header:
#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

然后在实现文件中,你可以这样写:

#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);
}

大多数时候,我最终做的只是创建我自己的库来管理这些类型的数据结构;但同样,选择权在你。

希望我的回答对您有所帮助

现在有访问 Vector 的原始数据数组的方法:

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

这些扩展是在 C++11 中完全针对您的情况添加的,以连接仅采用原始数据数组的第三方库。将返回的指针作为数组传递给 C,该数组也是 C 中的指针。您可能需要添加 vector.size() 作为另一个参数。

在使用返回的指针时不要操作向量。