【问题标题】:Passing/recieving strings through a C interface on a C++ software通过 C++ 软件上的 C 接口传递/接收字符串
【发布时间】:2018-11-07 23:50:02
【问题描述】:

我不熟悉 C,但我必须为我的 C++ 软件编写一个 C 接口才能将其作为动态库提供。关于经典实践,我有两个问题:

  1. 传递和接收字符串的经典方法是什么?谁负责记忆?
  2. 始终保留错误代码的返回值,然后在参数中接收带有指针的值是个好主意吗?如果不是,为什么?

这是一个示例,用我最初想象的代码说明我的问题:

int define_new_population(int* id)
{
  // all_pop is a std::map<int,std::vector<boost::shared_ptr<Element>>>
  *id = generate_id();
  all_pop[*id] = std::vector<boost::shared_ptr<Element>>();
  return 0;
}

int add_element_type1_in_pop(int pop_id, const char* name, double lower_bound, double upper_bound)
{
  if(all_pop.find(pop_id) == all_pop.end())
    return ERROR_CODE_WRONG_ID;
  const std::string str_name(name);
  try { all_pop[pop_id].push_back(boost::shared_ptr<Element>(new ElementType1(str_name, lower_bound, upper_bound))); }
  catch(...) { return ERROR_CODE_FOO; }
  return 0;
}

int add_element_type2_in_pop(int pop_id, const char* name, double reference)
{
  if(all_pop.find(pop_id) == all_pop.end())
    return ERROR_CODE_WRONG_ID;
  const std::string str_name(name);
  try { all_pop[pop_id].push_back(boost::shared_ptr<Element>(new ElementType2(str_name, reference))); }
  catch(...) { return ERROR_CODE_BAR; }
  return 0;
}

int get_population_size(int pop_id, int* pop_size)
{
  if(all_pop.find(pop_id) == all_pop.end())
    return ERROR_CODE_WRONG_ID;
  *pop_size = (int)all_pop[pop_id].size();
  return 0;
}

int get_element_name(int pop_id, int elem_index, char** name)
{
  if(all_pop.find(pop_id) == all_pop.end())
    return ERROR_CODE_WRONG_ID;
  if(elem_index >= (int)all_pop[pop_id].size())
    return ERROR_CODE_OUT_OF_RANGE;
  const std::string& str_name = all_pop[pop_id][elem_index].getName();
  *name = malloc(sizeof(char) * (str_name.length() + 1));
  strcpy(*name, str_name.c_str());
  return 0;
}

【问题讨论】:

  • *name = malloc(sizeof(char) * (str_name.length() + 1)); 您的动态库分配了内存,由您的库来释放它。您将需要提供一个函数来执行此操作。应用程序和共享库不一定共享相同的运行时支持和堆。所以他们不能分配和释放彼此的内存。
  • 补充(讨论):大约一半的 MS-Windows API 要求调用者预先分配一个缓冲区并将指针和大小传递给 API(可以返回缓冲区不够大的错误和所需的大小)。在这种情况下,调用者负责释放内存。其他时候,MS-Windows API 为 API 调用分配缓冲区,并为调用者提供一个空闲内存调用,以便在内存使用完毕时使用。
  • 我认为调用者应该负责内存管理。
  • 正如 Richard Critten 所说,您的动态库分配了内存,由您的库来释放它。但大多数时候这是不可能的。你应该做的是提供一个函数来释放内存并记录应用程序必须调用这个函数来释放内存。
  • define_new_population 这样总是返回 0 的函数通常没有意义。它不会失败,所以不要返回错误代码。 -- 在这种情况下,尽管您未能捕获 std::bad_alloc 并返回适当的错误代码。

标签: c++ c dll interface


【解决方案1】:

我至少可以给一些建议。

  • 应该在 API 中明确定义谁释放内存(调用方或 DLL)。
  • 请注意,如果调用者和被调用者分别使用不同版本的 Windows std C 库编译,则不同版本的 C 库/运行时的 malloc 和 free 通常将不起作用。如果您可以通过为 API 用户提供可与 RAII 一起使用的“无内存”回调来自动执行此操作,那么这是一个好方法。为 API 用户提供简单的示例以供参考。
  • 如果您可以完全控制 DLL 库和调用方,您甚至可以通过 DLL 边界传递 C++ 异常,这比错误代码更清晰,但如果您不知道自己在做什么,那就更脆弱了。
  • 您甚至可以拥有一个 C++(不是纯 C)api,使用窗口/Visual Studio 的“它只是工作”机制。它可以正常工作,甚至可以扩展到 C++/CLI 和 C# 混合代码库。这里还有一些文档:implicit PInvoke

关于为错误代码保留返回值和发送指针(或者如果需要,发送指向指针的指针)的问题,这通常是一个很好的并且非常通用的技术。当然,如果你制作一个 C++ API,现代的方法是使用完美的转发和右值引用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-05-31
    • 1970-01-01
    • 1970-01-01
    • 2014-08-11
    • 2017-07-25
    • 2016-07-05
    • 1970-01-01
    相关资源
    最近更新 更多