将可变大小数组传递给DLL有什么选择?

时间:2013-04-18 22:02:57

标签: c++ arrays memory-management boost dll

如果我想将运行时确定大小的数组传递给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);

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

我目前的想法可能是使用boost :: scoped_array及其get()函数以指针形式返回原始数据。这看起来像是最好的主意还是有些东西我会忽略?

2 个答案:

答案 0 :(得分:5)

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

然而,没有什么可以阻止你将vector的内容传递到DLL边界:

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

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

答案 1 :(得分: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. 构建可能影响STL对象布局的设置 对于你的DLL及其使用者来说,它们完全相同。

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

  4. 也要注意this

  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容器类都有成员比较运算符 要求所包含的类型存在运算符<==

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