Best workaround for compiler error C2158: make_public does not support native template types
-
Monday, November 08, 2010 4:28 AM
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 onlythe 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 accessiblesince the template instantiation type
std::vector<B::NativeClassB *>is private to the assembly.Attempted Solutions
1. Use void *, break type safety:
Change the method,
GetMgdClassBto take avoid *and pass the address of thestd::vector<NativeClassB *>to the method. InGetMgdClassB. I can thenstatic_castthe passed invoid *tostd::vector<NativeClassB *> *. This, of course, works, but breaks type safety.2. Create a Managed wrapper for NativeClassB, pass a managed container
Create a managed class, say
ref class NativeClassBWrapperwho'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 containerstd::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.
Question:
Are there better work arounds?
All Replies
-
Tuesday, November 09, 2010 6:07 AMModerator
Hi Smith,
As far as I can tell, using the managed wrapper class for native class with a managed container is the better workaround (Your Solution 2) and it is not too hard to achieve. We can use a native type pointer insider a managed class in this case. For example:
In assembly B (B.cpp)
#pragma once
using namespace System;
namespace B
{
public class NativeClassB
{
public:
NativeClassB(){};
// ...
};
public ref class WrapperClassB{
public:
NativeClassB *nativeClassB;
WrapperClassB(){
nativeClassB = new NativeClassB();
};
};
public ref class MgdClassB
{
public:
static MgdClassB ^ GetMgdClassB(array<WrapperClassB^> ^vecNativeBs)
{
// ...
vecNativeBs;
return gcnew MgdClassB();
}
};
}
In assembly A (A.cpp)
namespace B
{
class NativeClassB;
}
namespace A
{
void Foo()
{
array<B::WrapperClassB^> ^vecNativeBs;
B::MgdClassB::GetMgdClassB(vecNativeBs);
}
}
Regards,
Yi Feng Li
MSDN Subscriber Support in Forum
If you have any feedback of our support, please contact msdnmg@microsoft.com.
Please remember to mark the replies as answers if they help and unmark them if they provide no help.
Welcome to the All-In-One Code Framework! If you have any feedback, please tell us. -
Tuesday, November 09, 2010 6:45 PM
Yi Feng Li,
Thanks for your response. Yes, Solution #2 is not hard to implement--but it is not performant (for large collections). In both assembly A and Assembly B the code makes use of the type std::vector<B::NativeClassB *>. So when I want to pass this from A to B I have to
1. Within A (prior to calling GetMgdClassB), Create a new managed WrapperClassB for each item in the vector and populate the managed container.
2. Within B (in the implementation of GetMgdClassB), Create a std::vector<B::NativeClassB *> from the managed container (since B's code works with std::vector<B::NativeClass B *>.
I'd like to avoid all this copying.
- Matt
-
Tuesday, November 09, 2010 7:26 PMModerator
So make a native class VectorOfNativeB and store a pointer/ref to a std::vector<NativeClassB *> in it. Put this class in B and make it public by using the make_public pragma. Change GetMgdClassB method to take a pointer/ref to this native class.- Proposed As Answer by Adjutor Thursday, November 11, 2010 4:03 AM
- Marked As Answer by Matt Smith Autodesk Thursday, November 11, 2010 2:53 PM
-
Tuesday, November 09, 2010 8:54 PM
Mike,
Thanks, it looks like it will work. I just do a native C++ export of the VectorOfNativeB (as well as mark it as public visibility) and import it in A. This looks like the best solution!
I originally went down the road of doing the same thing with a *managed* class as the wrapper and that didn't work because the constructor was not accessible (due to having the std::vector<NativeClassB *> as a parameter).
Thanks,
- Matt

