You must use global, C-style methods. The reason for this is described here.
Basically it comes down to this: C functions translate well into DLL exports because C is "closer to the ground" in terms of language features. C translates more directly into machine code. C++ does a lot at the compiler level, giving you a lot of features that can't be used outside of a C++ environment. For this reason, your exported functions should follow a C style in order to function properly across DLL boundaries. That means no templates, no inline code, no non-POD classes or structs.
Consider this code:
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;
}
};
}
This results in the following DLL exported function names:
?ClassNonStatic@MyClass@@AAEHH@Z
?ClassStatic@MyClass@@CAHH@Z
GlobalFunc
NamespaceFunction
The ones with the funny naming are essentially incompatible with anything other than Visual Studio-built C++ projects. This is called name mangling, embedding some type info into the name itself as a workaround to the limitations of exported functions I'm talking about. You can technically use these functions externally, but it's fragile and relies on nuances of compiler-specific behavior.
The rule of thumb for exporting functions in a DLL is: Can you do this in C? If you can't, then it's almost certain you're going to cause problems.
Note here that even static class methods (which are essentially global) still have name mangling, even with extern "C"
. But free-standing functions in a namespace export without name mangling (though they lose the namespace scope).
You can begin to see why this rule of thumb makes sense.
If you want to export a class, let's follow the rule of thumb, and design the DLL interface as you would do in C. Here's an example. Let's take this C++ class:
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;
}
};
You cannot just stick __declspec(dllexport)
on this. You must provide a C interface for it, and export that. Like this:
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();
}
}
Then write another small wrapper on the C# side, and you have successfully marshaled this class.
Specifically for marshaling to C#, another option is to provide a COM interface to your class instead of a C interface. Essentially it's the same thing, but there are a lot of helper classes and special compiler support for adding COM support directly to C++ classes without writing separate wrappers. COM objects can be referenced directly by C#.
That won't help you with ios though...