I have two c++/cli dlls (i.e. compiled with /clr) where A.dll references B.dll. In assembly B, I have a method, GetMgdClassB, I'd like to call from assembly A. Here is the code in assembly B (B.cpp):
namespace B
{
public class NativeClassB
{
public:
NativeClassB();
// ...
};
public ref class MgdClassB
{
public:
static MgdClassB ^ GetMgdClassB(const std::vector<NativeClassB *> & vecNativeBs)
{
// ...
vecNativeBs;
return gcnew MgdClassB();
}
};
}
Notice that the method GetMgdClassB takes a std::vector. In assembly A, I attempt to call this method with the following code (A.cpp):
namespace B
{
class NativeClassB;
}
#pragma make_public(std::vector<B::NativeClassB *>)
namespace A
{
void Foo()
{
std::vector<B::NativeClassB *> vecNativeBs;
B::MgdClassB::GetMgdClassB(vecNativeBs);
}
}
When I compile A.cpp, I get the following error:
error C2158: 'std::vector<_Ty>' : #pragma make_public directive is currently supported for native non-template types only
the reason I wanted to add this pragma is because native types are private to the assembly by default. If I remove the pragma I get the following error (as expected):
error C3767: 'B::MgdClassB::GetMgdClassB': candidate function(s) not accessible
since the template instantiation type std::vector<B::NativeClassB *>
is private to the assembly.
Change the method, GetMgdClassB
to take a void *
and pass the address of the std::vector<NativeClassB *>
to the method. In GetMgdClassB
. I can then static_cast
the passed in void *
to std::vector<NativeClassB *> *
. This, of course, works, but breaks type safety.
Create a managed class, say ref class NativeClassBWrapper
who's sole purpose is to hang on to a reference to the native NativeClassB. Change GetMgdClassB to take a managed container of NativeClassBWrappers (e.g. List<NativeClassBWrapper ^> ^
). This has the downside of having to create and populate a new managed container prior to calling GetMgdClassB, and then within managed class B, I have to repackage it into the the native container std::vector<NativeClassB *>
(since the code in B deals with this type.
Currently, I'm leaning toward going with Solution #1, since (a) it doesn't introduce any performance concerns and (b) I'll only be doing this in a few cases. I don't like losing the type safety, but it seems justifiable given the current deficiency in the compiler's ability to make native template instantiation types visible.
Are there better work arounds?
C++ CLI error C3767: candidate function(s) not accessible
I'm not aware of any way to export that type. If you have to have that function signature, I would lean in the direction of using a mix of managed and native exports (managed functions using native types can't be consumed by other languages anyway), and maybe use delay loading when calling the native exports so you have a chance to trap errors finding the assembly in the usual .NET way.
But your particular function may be problematic since it uses both managed types and complex native types in the signature.
In general, the best practice is to not pass native C++ classes across DLL boundaries at all, since this sets you up for One Definition Rule violations.
For this particular situation, my suggestion is to make an wrapper that implements ICollection
. That cures the problem just like your solution #2, without ever having to actually copy all the elements into a new data structure.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With