我在我正在處理的項目中有這個確切的問題 - STL類傳遞給DLL和很多。問題不僅僅是不同的內存堆 - 實際上,STL類沒有二進制標準(ABI)。例如,在調試版本中,一些STL實現會向STL類添加額外的調試信息,例如sizeof(std::vector<T>)
(發佈版本)!= sizeof(std::vector<T>)
(調試版本)。哎喲!沒有希望你可以依賴這些類的二進制兼容性。此外,如果您的DLL是在其他一些使用其他算法的其他STL實現的編譯器中編譯的,那麼您也可以在發佈版本中使用不同的二進制格式。
我解決這個問題的方法是使用一個名爲pod<T>
(POD代表簡單的舊數據,如chars和ints,它們通常在DLL之間轉換)的模板類。此類的工作是將其模板參數打包爲一致的二進制格式,然後在另一端將其解包。例如,代替返回std::vector<int>
的DLL中的函數,您將返回pod<std::vector<int>>
。 pod<std::vector<T>>
有一個模板專門化,它將malloc一個內存緩衝區並複製這些元素。它還提供了operator std::vector<T>()
,以便返回值可以透明地存回std :: vector中,方法是構造一個新的向量,將其存儲的元素複製到它中並返回。因爲它總是使用相同的二進制格式,所以它可以安全地編譯爲單獨的二進制文件並保持二進制兼容。 pod
的替代名稱可能爲make_binary_compatible
。
這裏的豆莢類定義:
// All members are protected, because the class *must* be specialization
// for each type
template<typename T>
class pod {
protected:
pod();
pod(const T& value);
pod(const pod& copy); // no copy ctor in any pod
pod& operator=(const pod& assign);
T get() const;
operator T() const;
~pod();
};
下面是pod<vector<T>>
部分特 - 注意,部分專門用來使這個類適用於任何類型T的另外要注意,它實際上是存儲內存緩衝區pod<T>
而不僅僅是T - 如果vector包含另一個類似std :: string的STL類型,我們希望它也是二進制兼容的!
// Transmit vector as POD buffer
template<typename T>
class pod<std::vector<T> > {
protected:
pod(const pod<std::vector<T> >& copy); // no copy ctor
// For storing vector as plain old data buffer
typename std::vector<T>::size_type size;
pod<T>* elements;
void release()
{
if (elements) {
// Destruct every element, in case contained other cr::pod<T>s
pod<T>* ptr = elements;
pod<T>* end = elements + size;
for (; ptr != end; ++ptr)
ptr->~pod<T>();
// Deallocate memory
pod_free(elements);
elements = NULL;
}
}
void set_from(const std::vector<T>& value)
{
// Allocate buffer with room for pods of T
size = value.size();
if (size > 0) {
elements = reinterpret_cast<pod<T>*>(pod_malloc(sizeof(pod<T>) * size));
if (elements == NULL)
throw std::bad_alloc("out of memory");
}
else
elements = NULL;
// Placement new pods in to the buffer
pod<T>* ptr = elements;
pod<T>* end = elements + size;
std::vector<T>::const_iterator iter = value.begin();
for (; ptr != end;)
new (ptr++) pod<T>(*iter++);
}
public:
pod() : size(0), elements(NULL) {}
// Construct from vector<T>
pod(const std::vector<T>& value)
{
set_from(value);
}
pod<std::vector<T> >& operator=(const std::vector<T>& value)
{
release();
set_from(value);
return *this;
}
std::vector<T> get() const
{
std::vector<T> result;
result.reserve(size);
// Copy out the pods, using their operator T() to call get()
std::copy(elements, elements + size, std::back_inserter(result));
return result;
}
operator std::vector<T>() const
{
return get();
}
~pod()
{
release();
}
};
注意所使用的內存分配函數是pod_malloc和pod_free - 這些都是簡單的malloc和free,但使用的所有DLL之間相同的功能。在我的情況下,所有DLL使用malloc並從主機EXE中釋放,所以它們都使用相同的堆,這解決了堆內存問題。 (究竟如何算出這個是到你。)
另外請注意,你需要專業化的pod<T*>
,pod<const T*>
,莢所有基本類型(pod<int>
,pod<short>
等),使它們可以被存儲在一個「莢載體「和其他莢容器。如果你瞭解上面的例子,這些應該足夠簡單。
此方法的意思是複製整個對象。但是,您可以傳遞對pod類型的引用,因爲在二進制文件之間有一個安全的operator=
。然而,沒有真正的傳遞引用,因爲更改pod類型的唯一方法是將其複製回原始類型,然後將其更改,然後重新打包爲pod。此外,它創建的副本意味着它不一定是最快的方式,但它作品。
但是,你也可以莢專注自己的類型,這意味着你可以有效地返回複雜的類型,比如std::map<MyClass, std::vector<std::string>>
提供有用於pod<MyClass>
專業化和std::map<K, V>
,std::vector<T>
和std::basic_string<T>
偏特(你只需要編寫一次) 。
最終結果用法如下所示。一個常見的接口定義:
class ICommonInterface {
public:
virtual pod<std::vector<std::string>> GetListOfStrings() const = 0;
};
一個DLL可能實現它是這樣:
pod<std::vector<std::string>> MyDllImplementation::GetListOfStrings() const
{
std::vector<std::string> ret;
// ...
// pod can construct itself from its template parameter
// so this works without any mention of pod
return ret;
}
,主叫方,一個單獨的二進制文件,可以把它作爲這樣的:
ICommonInterface* pCommonInterface = ...
// pod has an operator T(), so this works again without any mention of pod
std::vector<std::string> list_of_strings = pCommonInterface->GetListOfStrings();
所以一旦建立起來,你就可以像使用pod課程一樣使用它。
「任何可以調用新的/刪除的東西」我的意思是說「任何可以調用在我的代碼中分配給dll的內存塊中的刪除」的東西。 – SigTerm 2010-08-25 13:47:22