In news://msnews.microsoft.com/microsoft.public.dotnet.languages.vc Edward Diener asked an very interesting question: Why can't I call a function having arguments of a native type like std::string from another assembly?
Assume you have some code like this one:
// Conversions.cpp
// compile with "CL /clr /LD conversions.cpp"
// output: mixed code assembly Conversions.dll
#include <string>
public ref class Conversions
{
public:
static void S2S(System::String^ s1, std::string& s2) { /* ... */ }
};
This code should compile as expected, however, it would not give you the expected result!
The code below looks like a suitable client:
// ConversionsClient.cpp
// compile with "CL /clr ConversionsClient.cpp"
#using "Conversions.dll"
#include <string>
int main()
{
std::string s;
Conversions::S2S("asdf", s);
}
If you try to compile this code, you will get a disappointing error message:
error C3767: 'Conversions::S2S': candidate function(s) not accessible
Why is a public static function S2S of a public type Convesions not accessible?
To use the native type std::string in managed code, the compiler generates a managed value type std::string in the assembly where std::string is used. This managed wrapper value type is private, therefore, the Conversions::S2S cannot be called from outside the assembly even though it is a public function of a public type.
At the first view it seems, key to the solution is to make sure the compiler generates a public type for std::string, in theory this is possible, however it would not help to solve the problem. In fact the native wrapper type has been defined as a private type for some good reasons.
Assume the native wrapper type for std::string was public. To call S2S, one would have to pass a tracking handle to a System::String, defined in mscolib.dll, and a value of the type std::string defined in the assembly Conversions.dll. The std::string type that we pass in ConversionsClient.cpp is a different one! It is the native wrapper type defined in ConversionsClient.cpp - not the native wapper type defined in Conversions.dll. Therefore, the parameters would not match.
So how can we solve this problem?
The origin of the problem is the fact, that type identity rules of .NET do not allways mix well with the type identity rules of native C++. To solve this problem, you can switch back to the world of native code sharing without loosing you managed code features. Simply create a mixed code static library:
Create a static mixed code library from the code :
// ConversionsLib.cpp
// compile with "CL /c /clr ConversionsLib.cpp"
// make lib with "LIB ConversionsLib.obj"
// output: mixed code static library ConversionsLib.lib
#include <string>
void S2S(System::String^ s1, std::string& s2) { /* ... */ }
Create a client from the code below.
// ConversionsLibClient.cpp
// compile with "CL /clr ConversionsLibClient.cpp"
#include <string>
#pragma comment (lib, "ConversionsLib.lib")
void S2S(System::String^ s1, std::string& s2);
int main()
{
std::string s;
S2S("asdff", s);
}
Conclusion: Beware the different type identity rules. Native types are identifies by their namespace-qualified typename, managed types are identified by their assembly- and namespace-qualifies typename. If you need native type identity rules, use native code sharing features, if you need managed type identity, use managed code sharing features.