4

我想知道如果我想将在运行时确定大小的数组传递给 DLL 函数,我有哪些选择。目前我在 DLL 中的函数签名看起来像

#ifdef BUILDING_DLL
#define DBUILDING __declspec(dllexport)
#else
#define DBUILDING __declspec(dllimport)
#endif

void __cdecl DBUILDING myFunc(const double t, const double x[], double *xdot);

因此,我传递了在某个类中动态分配的普通旧数据类型,然后必须手动删除这些数据类型。我宁愿不必调用 delete 并担心内存管理,所以我想看看其他选项。显而易见的选择是向量,但我知道您不应该将 STL 容器传递给 dll,所以我需要其他东西。我需要一些可以处理内存管理但我也可以传递给 dll 的东西。

我目前的想法可能是使用 boost::scoped_array 及其 get() 函数以指针形式返回原始数据。这似乎是最好的主意还是我忽略了什么?

4

2 回答 2

5

你是对的,跨 DLL 边界共享 C++ 对象,尤其是标准中定义的类是一个坏主意。只是其中一些原因是违反单一定义规则和混合不同的分配器。

但是,没有什么能阻止您通过vectorDLL 边界传递 a 的内容:

vector<double> x;
// ... fill it in, change the size
myFunc(t, &x[0], x.size(), xdot);

请注意,您几乎总是必须将大小作为额外参数传递。

于 2013-04-19T01:26:51.437 回答
2

[...] 我知道您不应该将 STL 容器传递给 DLL [...] - 这是错误的假设。

先决条件


事实上,您可以跨 DLL 边界传递 STL 对象。此外,您可以以跨平台的方式进行。像往常一样,巧妙的预处理器操作来拯救。以下配置标头对于任何跨平台 DLL 都是必须的:

Config.hpp

#ifndef MY_DLL_CONFIG_HPP
#define MY_DLL_CONFIG_HPP

#ifdef __cplusplus
  #define MY_DLL_CPP
#else
  #define MY_DLL_C
#endif

#if                       \
  defined (__CYGWIN__) || \
  defined (__CYGWIN32__)
  #define MY_DLL_OS
  #define MY_DLL_OS_UNIX
  #define MY_DLL_OS_WINDOWS
  #define MY_DLL_OS_CYGWIN
#elif                      \
  defined (_WIN16)      || \
  defined (_WIN32)      || \
  defined (_WIN64)      || \
  defined (__WIN32__)   || \
  defined (__TOS_WIN__) || \
  defined (__WINDOWS__)
  #define MY_DLL_OS
  #define MY_DLL_OS_WINDOWS
#elif                        \
  defined (macintosh)     || \
  defined (Macintosh)     || \
  defined (__TOS_MACOS__) || \
  (defined (__APPLE__) && defined (__MACH__))
  #define MY_DLL_OS
  #define MY_DLL_OS_UNIX
  #define MY_DLL_OS_MAC
#elif                    \
  defined (linux)     || \
  defined (__linux)   || \
  defined (__linux__) || \
  defined (__TOS_LINUX__)
  #define MY_DLL_OS
  #define MY_DLL_OS_UNIX
  #define MY_DLL_OS_LINUX
#elif                      \
  defined (__FreeBSD__) || \
  defined (__OpenBSD__) || \
  defined (__NetBSD__)  || \
  defined (__bsdi__)    || \
  defined (__DragonFly__)
  #define MY_DLL_OS
  #define MY_DLL_OS_UNIX
  #define MY_DLL_OS_BSD
#elif              \
  defined (sun) || \
  defined (__sun)
  #define MY_DLL_OS
  #define MY_DLL_OS_UNIX
  #define MY_DLL_OS_SOLARIS
#elif               \
  defined (_AIX) || \
  defined (__TOS_AIX__)
  #define MY_DLL_OS
  #define MY_DLL_OS_UNIX
  #define MY_DLL_OS_AIX
#elif                \
  defined (hpux)  || \
  defined (_hpux) || \
  defined (__hpux)
  #define MY_DLL_OS
  #define MY_DLL_OS_UNIX
  #define MY_DLL_OS_HPUX
#elif \
  defined (__QNX__)
  #define MY_DLL_OS
  #define MY_DLL_OS_UNIX
  #define MY_DLL_OS_QNX
#elif                 \
  defined (unix)   || \
  defined (__unix) || \
  defined (__unix__)
  #define MY_DLL_OS
  #define MY_DLL_OS_UNIX
#endif

#ifndef MY_DLL_OS
  #error "Sorry, but current OS is not supported by MyDLL."
#endif

#if                        \
  defined (__MINGW32__) || \
  defined (__MINGW64__)
  #define MY_DLL_COMPILER
  #define MY_DLL_COMPILER_MINGW
#elif \
  defined (__GNUC__)
  #define MY_DLL_COMPILER
  #define MY_DLL_COMPILER_GNU
  #define MY_DLL_COMPILER_GNU_VERSION_MAJOR __GNUC__
  #define MY_DLL_COMPILER_GNU_VERSION_MINOR __GNUC_MINOR__
  #define MY_DLL_COMPILER_GNU_VERSION_PATCH __GNUC_PATCHLEVEL__
#elif \
  defined (__clang__)
  #define MY_DLL_COMPILER
  #define MY_DLL_COMPILER_CLANG
#elif \
  defined (_MSC_VER)
  #define MY_DLL_COMPILER
  #define MY_DLL_COMPILER_MICROSOFT
#elif \
  defined (__BORLANDC__)
  #define MY_DLL_COMPILER
  #define MY_DLL_COMPILER_BORLAND
#elif \
  defined (__CODEGEARC__)
  #define MY_DLL_COMPILER
  #define MY_DLL_COMPILER_CODEGEAR
#elif                           \
  defined (__INTEL_COMPILER) || \
  defined (__ICL)
  #define MY_DLL_COMPILER
  #define MY_DLL_COMPILER_INTEL
#elif                   \
  defined (__xlc__)  || \
  defined (__xlC__)  || \
  defined (__IBMC__) || \
  defined (__IBMCPP__)
  #define MY_DLL_COMPILER
  #define MY_DLL_COMPILER_IBM
#elif \
  defined (__HP_aCC)
  #define MY_DLL_COMPILER
  #define MY_DLL_COMPILER_HP
#elif \
  defined (__WATCOMC__)
  #define MY_DLL_COMPILER
  #define MY_DLL_COMPILER_WATCOM
#endif

#ifndef MY_DLL_COMPILER
  #error "Sorry, but current compiler is not supported by MyDLL."
#endif

#if                              \
  defined (MY_DLL_OS_WINDOWS) && \
  !defined (BOOST_DISABLE_WIN32)
  #define MY_DLL_EXPORT_DECLARATION __declspec(dllexport)
  #define MY_DLL_IMPORT_DECLARATION __declspec(dllimport)
#elif \
  MY_DLL_COMPILER_GNU_VERSION_MAJOR >= 4
  #define MY_DLL_EXPORT_DECLARATION __attribute__((visibility("default")))
  #define MY_DLL_IMPORT_DECLARATION __attribute__((visibility("default")))
#else
  #define MY_DLL_EXPORT_DECLARATION 
  #define MY_DLL_IMPORT_DECLARATION 
#endif

#ifdef MY_DLL_EXPORT
  #define MY_DLL_API        MY_DLL_EXPORT_DECLARATION
  #define MY_DLL_FUNCTION
  #define MY_DLL_TYPE
#else
  #define MY_DLL_API        MY_DLL_IMPORT_DECLARATION

  #ifdef MY_DLL_CPP
    #define MY_DLL_FUNCTION extern "C"
  #else
    #define MY_DLL_FUNCTION extern
  #endif

  #define MY_DLL_TYPE       extern
#endif

#endif

注意:在 OP 的问题中,有些平台检测可能被认为是矫枉过正。但是,它们在创建生产 DLL 方面非常重要,所以我不想剥离它们。

例子


MyDLL.hpp

#include "Config.hpp"

#include <vector>

// Instantiate class std::vector<int>.
// NOTE: This does not create an object. It only enforces the generation
// of all of the members of class std::vector<int>. It exports them from
// the DLL and imports them into any artifact linked against this DLL.
MY_DLL_TYPE template class MY_DLL_API std::vector<int>;

// Instantiate class std::vector<float>.
// NOTE: This does not create an object. It only enforces the generation
// of all of the members of class std::vector<float>. It exports them from
// the DLL and imports them into any artifact linked against this DLL.
MY_DLL_TYPE template class MY_DLL_API std::vector<float>;

class MY_DLL_API MyClass {
public:
  static std::vector<char> floatVector;

  void
  setIntVector(std::vector<int> intVector);

  std::vector<int>&
  intVector();

  bool operator < (MyClass const& c) const
  { return _intVector < c._intVector; }

  bool operator == (MyClass const& c) const
  { return _intVector == c._intVector; }

private:
  std::vector<int> _intVector;
};

// Instantiate class std::vector<MyClass>.
// NOTE: This does not create an object. It only enforces the generation
// of all of the members of class std::vector<MyClass>. It exports them from
// the DLL and imports them into any artifact linked against this DLL.
MY_DLL_TYPE template class MY_DLL_API std::vector<MyClass>;

MyDLL.cpp

#include "MyDLL.hpp"

std::vector<float> MyClass::floatVector;

void
MyClass::
setIntVector(std::vector<int> intVector) 
{ _intVector = intVector; }

std::vector<int>&
MyClass::
intVector()
{ return _intVector; }

注意:使用 MY_DLL_EXPORT定义构建。最佳实践是在编译期间提供此定义。例如,在 MSVC 上它将是/DMY_DLL_EXPORT,而在 GCC 上它将是-DMY_DLL_EXPORT.

Application.cpp

#include "MyDLL.hpp"

#include <iostream>

using std::cout;
using std::endl;
using std::vector;

int main() {
  MyClass x;

  x.setIntVector({-3, -2, -1});

  for (int i = 0; i < 7; ++i)
    x.intVector().push_back(i);

  for (int i = 0; i < 10; ++i)
    x.floatVector.push_back(i);

  for (auto value : x.intVector())
    cout << value << endl;

  cout << endl;

  for (auto value : x.floatVector)
    cout << value << endl;

  cout << endl;

  vector<MyClass> v;
  for (int i = 0; i < 5; ++i)
    v.push_back(MyClass());
}

注意:构建没有 MY_DLL_EXPORT定义。

因此,您的 DLL 的使用者将使用从 DLL 导出的二进制代码,而不是实例化他们自己的。

免责声明


有一些你可能不知道的陷阱,所以我觉得我应该提到以上所有功能正常运行的重要限制。

  1. 您必须确保使用同一编译器的同一版本 来构建您的 DLL 及其使用者。

  2. 对您的 DLL 及其使用者而言,可能影响 STL 对象布局的构建设置应该完全相同。

  3. 您的 DLL 及其使用者都应该链接到相同的编译器运行时。

  4. 也请注意一点。

  5. MSVC 可能会生成警告:C4231 "nonstandard extension used : 'extern' before template explicit instantiation.,可以安全地忽略它。

  6. 一些 STL 类使用其他 STL 类。这些其他类也必须导出。必须导出的类列在编译器警告中。

  7. 一些 STL 类包含嵌套类。这些类无法导出。例如,deque包含一个嵌套类deque::iterator。如果您导出deque,您将收到必须导出的警告deque::iterator。如果您导出 deque::iterator,您会收到必须导出的警告deque。我可以保证两者vectorstring可以导出。其他容器都包含嵌套类,无法导出。我不确定这是否仍然相关,您必须进行实验。

  8. 当您导出使用用户定义类型 (UDT) 参数化的 STL 容器时,您必须为您的 UDT 定义运算符<==。例如,如果您导出 vector<MyClass>,则必须定义MyClass::operator <MyClass operator ==。这是因为所有 STL 容器类都有成员比较运算符,这些运算符需要运算符的存在<==包含的类型。

注意:其中一些可能已经无关紧要,因此必须在实践中进行检查。

于 2013-04-19T01:13:29.003 回答