我正在用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);
};
}
Run Code Online (Sandbox Code Playgroud)
所有方法都是静态的,静态方法有很多限制,所以我的问题是如何在没有静态方法的情况下实现相同的方法?我总是需要在DLL中使用静态方法吗?我想在C#和IOS应用程序中导入此DLL.
您必须使用全局C风格的方法.这里描述了其原因.
基本上它归结为:C函数很好地转换为DLL导出,因为C在语言特征方面"更接近地面".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;
}
};
}
Run Code Online (Sandbox Code Playgroud)
这导致以下DLL导出的函数名称:
?ClassNonStatic @ MyClass的@@ AAEHH @ Z
?ClassStatic @ MyClass的@@ CAHH @ Z
GlobalFunc
NamespaceFunction
具有有趣命名的那些与Visual Studio构建的C++项目之外的任何内容基本上都不兼容.这称为名称修改,将一些类型信息嵌入到名称本身中,作为我正在谈论的导出函数限制的解决方法.从技术上讲,您可以在外部使用这些函数,但它很脆弱,并且依赖于编译器特定行为的细微差别.
导出DLL中函数的经验法则是:你能用C做这个吗?如果你做不到,那几乎可以肯定你会导致问题.
请注意,即使是静态类方法(基本上是全局的)仍然具有名称修改,即使使用extern "C".但是名称空间导出中的独立函数没有名称错误(尽管它们失去了命名空间范围).
你可以开始明白为什么这个经验法则是有道理的.
如果你想导出一个类,让我们按照经验法则,像在C中那样设计DLL接口.这是一个例子.我们来看看这个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;
}
};
Run Code Online (Sandbox Code Playgroud)
你不能坚持__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();
}
}
Run Code Online (Sandbox Code Playgroud)
然后在C#端编写另一个小包装器,并且您已成功封送此类.
特别是对于C#的编组,另一个选择是为您的类而不是C接口提供COM接口.本质上它是一样的,但有很多辅助类和特殊的编译器支持直接向C++类添加COM支持而无需编写单独的包装器.COM对象可以由C#直接引用.
虽然这对ios没有帮助...