如果我想将运行时确定大小的数组传递给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()函数以指针形式返回原始数据。这看起来像是最好的主意还是有些东西我会忽略?
答案 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导出的二进制代码,而不是实例化他们自己的代码。
你可能不知道有一些陷阱,所以我觉得我应该提到上述所有内容的重要限制才能正常运作。
您必须确保相同编译器的相同版本 用于构建您的DLL及其使用者。
构建可能影响STL对象布局的设置 对于你的DLL及其使用者来说,它们完全相同。
您的DLL及其使用者都应该链接到相同的编译器运行时。
也要注意this。
MSVC可能会生成警告:C4231 "nonstandard extension used : 'extern' before
template explicit instantiation.
,可以安全地忽略它。
某些STL类使用其他STL类。还必须导出这些其他类。 必须导出的类列在编译器警告中。
某些STL类包含嵌套类。无法导出这些类。对于
实例,deque
包含嵌套类deque::iterator
。如果您导出deque
,则为您
将收到警告,您必须导出deque::iterator
。如果您导出
deque::iterator
,您会收到警告,必须导出deque
。我可以保证
可以导出vector
和string
。其他容器都包含
嵌套类,无法导出。 我不确定这是否仍然相关,你会的
必须进行实验。
导出使用用户定义类型(UDT)参数化的STL容器时,必须使用
为您的UDT定义运算符<
和==
。例如,如果您导出
vector<MyClass>
,您必须定义MyClass::operator <
和MyClass operator ==
。
这是因为所有STL容器类都有成员比较运算符
要求所包含的类型存在运算符<
和==
。
注意:其中一些可能已经无关紧要,因此必须在实践中进行检查。