代码之家  ›  专栏  ›  技术社区  ›  SigTerm

从dll返回std::string/std::list

  •  12
  • SigTerm  · 技术社区  · 16 年前

    简短的问题。

    我刚得到一个我应该连接的dll。 Dll使用msvcr90D.Dll中的crt(注意D),并返回std::strings、std::lists和boost::shared\u ptr。运算符new/delete不会在任何地方重载。

    我假设crt混合(发布版本中的msvcr90.dll,或者如果其中一个组件是用较新的crt重建的,等等)最终一定会导致问题,应该重写dll,以避免返回任何可能调用new/delete的内容(即在dll中分配的内存块(可能使用不同的crt)上的代码中可能调用delete的内容)。

    4 回复  |  直到 16 年前
        1
  •  13
  •   Doug T.    16 年前

    要记住的主要一点是DLL包含 代码 而不是

    这就是为什么在西蒙的回答中他说:

    除非你能做到,否则坏事就会发生 始终保证您的整套 所有的二进制文件都是用相同的 工具链。

    因为如果由于某种原因,字符串s的副本在a.dll和b.dll之间不同,就会发生奇怪的事情。更糟糕的是,如果字符串本身在a.dll和b.dll之间是不同的,并且其中一个的析构函数知道清除另一个忽略的额外内存。。。你可能很难找到内存泄漏。也许更糟。。。a、 dll可能是根据完全不同的STL版本(即STLPort)构建的,而b.dll则是使用Microsoft的STL实现构建的。

    对于向第三方公开DLL,这完全是另一回事。除非您希望严格要求客户机提供特定的生成设置,否则您将希望避免导出STL模板。我不建议你的客户严格执行特定的构建设置。。。他们可能有另一个第三方工具,希望您使用完全相反的构建设置。

    (1) 是的,我知道在加载/卸载dll时静态和局部变量被实例化/删除。

        2
  •  11
  •   AshleysBrain    16 年前

    sizeof(std::vector<T>) (发布版本)!= sizeof(标准::向量<T>)

    pod<T> (POD代表普通的旧数据,比如chars和int,它们通常在dll之间进行精细传输)。此类的工作是将其模板参数打包为一致的二进制格式,然后在另一端解包。例如,而不是返回 std::vector<int> ,则返回 pod<std::vector<int>> . 有一个模板专门化 pod<std::vector<T>> ,它malloc一个内存缓冲区并复制元素。它还提供 operator std::vector<T>() ,这样通过构造一个新的向量,将其存储的元素复制到该向量中,并返回它,返回值就可以透明地存储回std::vector。因为它总是使用相同的二进制格式,所以可以安全地将其编译为独立的二进制文件,并保持二进制兼容。另一个名字 pod 可以是 make_binary_compatible .

    以下是pod类定义:

    // 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>> 如果向量包含另一个STL类型,比如std::string,那么我们也希望它是二进制兼容的!

    // 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\u malloc和pod\u free—它们只是malloc和free,但在所有DLL之间使用相同的函数。在我的例子中,所有dll都使用malloc和free from the host EXE,因此它们都使用相同的堆,这就解决了堆内存问题。(你到底是怎么弄明白的,这取决于你自己。)

    pod<T*> , pod<const T*> ,以及所有基本类型的pod( 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;
    };
    

    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类不存在一样使用它。

        3
  •  2
  •   Community Mohan Dere    9 年前

    我不确定“任何可以调用new/delete的东西”——这可以通过小心地使用具有适当分配器/deleter函数的共享指针等价物来管理。

    当我需要这类功能时,我经常跨越边界使用虚拟接口类。然后,您可以为 std::string , list shared_ptr .

    ,因为它太有用了。我还没有遇到任何问题,但是所有的东西都是用相同的工具链构建的。我在等着它咬我,毫无疑问它会咬我的。请参见上一个问题: Using shared_ptr in dll-interfaces

        4
  •  0
  •   user877329    10 年前

    为了 std::string c_str . 在更复杂的情况下,选项可以是

    class ContainerValueProcessor
        {
        public:
             virtual void operator()(const trivial_type& value)=0;
        };
    

    然后(假设您想使用std::list),您可以使用一个接口

    class List
        {
        public:
            virtual void processItems(ContainerValueProcessor&& proc)=0;
        };