创建C ++ DLL没有静态方法(Creating c++ DLL without static m

2019-06-27 23:00发布

我创建C ++中的DLL。 下面是一个例子:

namespace MathFuncs
{
  class MyMathFuncs
  {
    public:
        // Returns a + b
        static __declspec(dllexport) double Add(double a, double b);

        // Returns a - b
        static __declspec(dllexport) double Subtract(double a, double b);

        // Returns a * b
        static __declspec(dllexport) double Multiply(double a, double b);

        // Returns a / b
        // Throws DivideByZeroException if b is 0
        static __declspec(dllexport) double Divide(double a, double b);
    };
}

所有方法都是静态的,也有很多与静态方法的局限性,所以我的问题是如何能够实现相同的不带静电的方法呢? 我总是必须DLL静态方法? 我要导入C#和iOS应用此DLL。

Answer 1:

至于侧面说明我做了1实验前几天的MinGW / C ++至极可以澄清你。

我有发现在我的程序内存泄漏全局引用计数器,

class ReferenceCounter /** other implementations details are omitted.*/
{
public:

static int GlobalReferenceCounter;

//version 1
static int getReferenceCount1() { return GlobalReferenceCounter;}

//verison 2
static int getReferenceCount2(); //same code of version 1 but moved into .cpp file
};

当使用引用计数为DLL编译我的图书馆,则变量被复制,版本1编译成DLL,并且一个版本是在客户端代码编译。

当我问的参考计数的东西istances来自DLL的工厂方法,仅DLL内的参考计数器增加/减少。 当客户端代码,使用从参考计数器继承了它自己的类,则客户端参考计数器增加/减少。

因此,对于检查内存泄漏我必须在程序结束做

assert(ReferenceCounter.getReferenceCount1() == 0);
assert(ReferenceCoutner.getReferenceCount2() == 0);

这是因为在存储器的情况下泄漏的那些值中的一个将是大于0。如果第一个值大于1,则该内存泄漏是由未分配用户类引起的,如果第二值大于0,则内存泄漏是由库引起类。

请注意,如果泄漏是由未分配库的类造成的,这不一定是图书馆的一个错误,因为用户仍然能够泄漏的类,即使应该意味着缺乏在库设计的,因为理想的一切都很应退还在安全适当的智能指针。)

当然,你应该指定一个“GlobalReferenceCoutner”的文档中复制,否则一些不知情的用户可以只觉得2个干将是多余的,并会认为你做了一些错误。 (如果可能避免做这样的事情,是晦涩不清)

这也应该提醒大家,通过DLL的访问静态方法是非常不安全的。 例如,如果在我的课,我想只有1引用计数,而不是2我不得不这样做,对于授予安全:

class ReferenceCounter
{
public:

static int GlobalReferenceCounter;

static void duplicate() { increaseGlobalCounter(); }

static void release() { decreaseGlobalCounter(); }

static int getGlobalCounter() { return privateGetGlobalCounter(); }

private:

static increaseGlobalCounter(); // implementation into Cpp file

static decreaseGlobalCounter(); // implementation into Cpp file

static privateGetGlobalCounter(); // implementation into Cpp file

};

这样做,将授予你,相同的值跨DLL bounduaries,并在用户应用程序中使用。 所以不是有2个不同的变量,这里我们使用1个变量(可能是GlobalCounter仍然编译成可执行的用户,但没有人使用它删除“克隆效应”)



Answer 2:

您必须使用全局,C风格的方法。 这样做的原因是这里描述 。

基本上它归结为:C函数很好地翻译成DLL导出,因为C是在语言功能方面“贴近地面”。 Ç转换更直接成机器代码。 C ++做了很多在编译器级别,给你很多的不能在C ++环境之外使用的功能。 出于这个原因,您的导出函数应遵循一个C风格,以跨越边界的DLL正常工作。 这意味着没有模板,没有内嵌代码,没有非POD类或结构。

考虑以下代码:

extern "C"
{
    __declspec(dllexport) int GlobalFunc(int n)
    {
        return n;
    }

    namespace SomeNamespace
    {
        __declspec(dllexport) int NamespaceFunction(int n)
        {
            return n;
        }
    }

    class MyClass
    {
        __declspec(dllexport) int ClassNonStatic(int n)
        {
            return n;
        }
        __declspec(dllexport) static int ClassStatic(int n)
        {
            return n;
        }
    };

}

这将导致以下DLL导出函数名:

?ClassNonStatic @ MyClass的@@ AAEHH @ Z

?ClassStatic @ MyClass的@@ CAHH @ Z

GlobalFunc

NamespaceFunction

与滑稽命名的是被比Visual Studio中内置C ++项目之外的任何本质上是不相容的。 这就是所谓的名字改编 ,嵌入某种类型的信息到这个名字本身作为一种解决方法,以导出函数我谈论的限制。 您可以在技术上外部使用这些功能,但它是脆弱的,依赖于具体的编译器行为的细微差别。

根据经验,在一个DLL导出函数的规则是:你可以在C这样做吗? 如果不能,那么几乎可以肯定你会造成问题。

这里注意,即使静态类的方法(本质上属于全球)仍然有名字改编,甚至extern "C" 。 但独立的功能,在没有名称重整命名空间的出口(尽管他们失去了命名空间范围)。

你可以开始明白为什么这个经验法则是有道理的。


如果要导出一个类,让我们遵循的经验法则,并设计了DLL接口,你会在做C.下面是一个例子。 让我们这个C ++类:

    class Employee
    {
    private:
        std::string firstName;
        std::string lastName;

    public:
        void SetFirstName(std::string& s)
        {
            this->firstName = s;
        }
        void SetLastName(std::string& s)
        {
            this->lastName = s;
        }
        std::string GetFullName()
        {
            return this->firstName + " " + this->lastName;
        }
    };

你不能只拘泥于__declspec(dllexport)这一点。 你必须为它提供一个C接口,和导出。 像这样:

    extern "C"
    {
        __declspec(dllexport) Employee* employee_Construct()
        {
            return new Employee();
        }

        __declspec(dllexport) void employee_Free(Employee* e)
        {
            delete e;
        }

        __declspec(dllexport) void employee_SetFirstName(Employee* e, char* s)
        {
            e->SetFirstName(std::string(s));
        }

        __declspec(dllexport) void employee_SetLastName(Employee* e, char* s)
        {
            e->SetLastName(std::string(s));
        }

        __declspec(dllexport) int employee_GetFullName(Employee* e, char* buffer, int bufferLen)
        {
            std::string fullName = e->GetFullName();
            if(buffer != 0)
                strncpy(buffer, fullName.c_str(), bufferLen);
            return fullName.length();
        }
    }

然后写在C#侧另一个小包装,并已成功地封这个类。

特别是用于编组的C#,另一种选择是提供一个COM接口,你的类,而不是C接口。 本质上,它是同样的事情,但也有很多辅助类和直接添加COM支持C ++类,而无需编写单独包装特殊的编译器的支持。 COM对象可直接由C#引用。

这不会帮助你与iOS虽然...



文章来源: Creating c++ DLL without static methods
标签: c# c++ ios dll