2
votes

I have a public ref class in a C++/CLI assembly (MyAssembly) containing a public static method that accepts native parameters.

#pragma make_public(nativeTypeA)

namespace namespaceA
{ 
    public ref class MyClass : namespaceB::MyClass
    {
    public:
        static managedTypeA ^ MethodA(nativeTypeA param);
        static managedTypeB ^ MethodB(nativeTypeB param);
    }
}

I would like to expose this method to another C++/CLI assembly. The managed assembly compiles fine but the assembly that references it (CallingAssembly) generates the following warning for MethodB:

warning C4679: 'namespaceA::MyClass::MethodB' : could not import member
This diagnostic occurred while importing type 'namespaceA::MyClass ' from assembly 'MyAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. 

I cannot use make_public on nativeTypeB because it is a typedef of a templated class, however, I am using make_public for non-templated native types (e.g. nativeTypeA) and it works (i.e. no C4679 when compiling CallingAssembly). Instead of using make_public, I have declared the native class public and used __declspec(dllexport) in the native header via preprocessor directives as suggested in this post. It was also necessary to conditionally exclude the "public" modifier (via CLR_ACCESS_MODIFIER) as the class is also included in other native projects:

#ifdef MANAGED
#define CLR_ACCESS_MODIFIER public 
#ifdef MYASSEMBLY_DEF
    #define MYASSEMBLY_LINKAGE __declspec(dllexport)
#else
    #define MYASSEMBLY_LINKAGE __declspec(dllimport)
#endif
#else
#define MYASSEMBLY_LINKAGE
#define CLR_ACCESS_MODIFIER
#endif

template<>
CLR_ACCESS_MODIFIER class MYASSEMBLY_LINKAGE nativeTypeB<TT> : public nativeTypeB_base<TT> {
...
}

I have also done this for the base class of nativeTypeB (necessary in order to compile), and its typedef:

typedef public nativeTypeB<TT> MYASSEMBLY_LINKAGE nativeTypeB;

I'm not sure whether the line above is necessary, but C4679 still occurs either way.

I've done the usual checks: the MANAGED preprocessor directive is defined both projects; MYASSEMBLY_DEF is defined in MyAssembly; and I have added a reference to MyAssembly in CallingAssembly as well as MyAssembly.lib to its link line. The project build order is correct and there are no missing dependencies but I am still getting C4679.

I could change the interface to accept non-templated types but I don't really want to do this as it will lead to code bloat and is less elegant. This post mentions that the normal use of "public" in my native class should work.

Can anyone help?

Thanks in advance!

1
That just isn't going to work, templates don't have external linkage. Use a plain old #include. - Hans Passant
Can you elaboarate on how to accomplish this using a #include? If I simply #include MyClass.h in the caller's header, I get exactly the same C4679 warnings plus a slew of other errors, e.g. class type redefinition, due to there being eponymous classes in both assemblies. I tried making MethodB private and invoking it from a public proxy method inside MyAssembly, by this did not remove the warnings, which seems surprising given that the caller shouldn't have visibility of private methods. - greenback
Agreed. I like solution 2 in the linked post above. - greenback
I could probably achieve what Hans suggests with some refactoring. The same topic is discussed here: stackoverflow.com/questions/12800262/… - greenback

1 Answers

0
votes

Passing objects of native classes across DLL boundaries has never been a good idea, because it's so very easy to violate the One Definition Rule.

C++/CLI does nothing to help this, instead it provides the ability to generate managed types which are specifically designed for sharing across assemblies. It also prevents you from courting disaster (ODR violations) by sharing native types. You can use the make_public pragma to override this, but with restrictions (such as no templates).

The better way to share native types is via COM-style interfaces.