Question

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.

Attempted Solutions

1. Use void *, break type safety:

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.

2. Create a Managed wrapper for NativeClassB, pass a managed container

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.

Question:

Are there better work arounds?

Related Question:

C++ CLI error C3767: candidate function(s) not accessible

Was it helpful?

Solution

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.

OTHER TIPS

I received a solution from Mike Danes on another forum:

http://social.msdn.microsoft.com/Forums/en-US/vclanguage/thread/b43cca63-b0bf-451e-b8fe-74e9c618b8c4/

Basically, the solution is to create a native wrapper (call it VectorOfNativeB) in assembly B that holds on to a pointer or reference to the std::vector. Export VectorOfNativeB and make it publicly visible. Change method GetMgdClassB to take a pointer or reference VectorOfNativeB.

[posted this here for future reference and to see if anyone here has any comments about this solution].

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top