none
Best workaround for compiler error C2158: make_public does not support native template types

    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?

    Monday, November 08, 2010 4:28 AM

Answers

  • 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 7:26 PM

All replies

  • 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:07 AM
  • 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 6:45 PM
  • 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 7:26 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

    Tuesday, November 09, 2010 8:54 PM