2

In a complex solution I have a mix of native C++ library, C++/CLI wrappers and C# assemblies. In the wrappers I often have to use native types so I have to make them public using #pragma make_public. The problem with those pragmas however is that you often get linker error LNK2022 as discussed here and here (and many other places).

The usual solution is to collect all pragmas in one place to avoid the duplicate type error. So far the only reliable place I found was to put the list in my stdafx.h header file. This is however annoying as it causes my entire project to recompile as soon as I have to add a new native type there (it's my precompiled header).

I'd like to extract that list to a separate header or, even better, cpp file. But my attempts so far have not worked. The types where not made public. It also seems I have to put the #pragma make_public calls in a header file. I'd prefer a cpp however.

So, what other possibilties exist? Where do others place their #pragma make_public calls?

Community
  • 1
  • 1
Mike Lischke
  • 48,925
  • 16
  • 119
  • 181
  • This doesn't sound good. The point of a *wrapper* is to wrap and *not* make native types visible. – Hans Passant Feb 07 '14 at 18:23
  • Well, the wrapper is not the last link in the chain. There are consumer libraries that use the wrapped classes in higher level structures. They get native objects and construct the wrappers for them which requires to have the native classes be visible to them. – Mike Lischke Feb 08 '14 at 08:58

1 Answers1

4

I put the pragma directly after including the .h file that brought in the type:

#include "native_type.h"
#pragma make_public(Native_Struct_1)

I only use the pragma in the case where the type is not one I control. If the type is one that I control, then I just mark the type as public by using the following preprocessor helper (to allow it to be compiled by either cli enabled code or non cli enabled code):

#ifdef __cplusplus_cli
#define CLR_ASSEMBLY_ACCESS_SPECIFIER__Public public
#else
#define CLR_ASSEMBLY_ACCESS_SPECIFIER__Public 
#endif

Usage:

CLR_ASSEMBLY_ACCESS_SPECIFIER__Public 
class NativeClass
{
}; 

Edit:

Example 1:

Native only library A.dll has header a.h which includes the following class declaration:

CLR_ASSEMBLY_ACCESS_SPECIFIER__Public 
class TypeA
{
};

When compiled in library A.dll, the CLR_ASSEMBLY_ACCESS_SPECIFIER__Public compiles to nothing. And there is no concept of managed visibility from the native dll.

When that class is used within a c++/cli assembly B.dll, and the header a.h within a /clr compiled source file, the CLR_ASSEMBLY_ACCESS_SPECIFIER__Public compiles to public and TypeA becomes public from B.dll. The equivalent would occur by using make_public within B.dll's source instead of using CLR_ASSEMBLY_ACCESS_SPECIFIER__Public.

Example 2: Now, let's say that A.h did not use CLR_ASSEMBLY_ACCESS_SPECIFIER__Public. And, let's say that in B.dll, we include A.h but we do not use make_public. Also, let's say we expose a managed method that includes TypeA:

public ref class ManagedTypeB
{
     public: void Foo(TypeA * pA);
};

Now, consider a managed assembly C.dll that references B.dll. It cannot call ManagedTypeB::Foo (even though it is public) because TypeA is private in B.dll. Furthermore, there is no way to fix this from the source code of C.dll. I.e., C.dll's source cannot used make_public to make TypeA public from B.dll. In this situation, the source of B.dll must be changed in order to make Foo callable.

Matt Smith
  • 17,026
  • 7
  • 53
  • 103
  • However, using the public keyword only works in a managed library, right? If the native type is in native DLL you always have to use make_public. That fact is barely mentioned when you read about make_public. – Mike Lischke Feb 07 '14 at 17:08
  • Using make_public (also) only works in a managed library. make_public changes the visibility of a native type for the assembly. It cannot be used to change the visibility of a type in *another* referenced assembly (see http://stackoverflow.com/questions/9198363/adding-pragma-make-publictype-not-removing-c3767-error). – Matt Smith Feb 07 '14 at 17:34
  • @MikeLischke Using make_public is really only supposed to be for the case where you are using a header that you cannot/should not modify (i.e. standard library headers, windows headers, etc.) – Matt Smith Feb 07 '14 at 17:36
  • @MikeLischke However, if the native type is in a native dll, and you are using the headers in your project, then you can make that native type public in *your* assembly (you just can't make it public in the native dll). Even in this case, if you have control over the source of the native dll, I would use the 2nd approach rather than the make_public. Using that approach, if you compile with /clr, you don't need to use make_public, if you compile without /clr it is as if the public keyword is not there. – Matt Smith Feb 07 '14 at 17:40
  • So you say, I can make a native type from a native DLL (native, not an assembly) public in another, this time managed, DLL, without using make_public? An interesting point with make_public is that you even need it when you have control over the source code of the native type. That because you cannot make it public in the native type (in the managed sense, it is public/exported from a pure C++ standpoint). You *have* to make it public in the managed library in any case. My problem is that doing this, especially the way you suggested in your answer, often leads to that LNK2022 error. – Mike Lischke Feb 08 '14 at 08:54
  • @MikeLischke, I've made some edits with examples to try to make my point clear. Can you give a small, but complete example of a situation where you run into the LNK2022 error? – Matt Smith Feb 10 '14 at 14:27
  • I appreciate your attempt to make this clear, but my problem is not to understand how to make the native type public, but looking for a better way to avoid LNK2022 other than putting all make_public pragmas in the stdafx.h header file. It's a complex project (almost 60 projects in the solution, native, managed and C#, 1.5M LOC etc.). Difficult to extract an example to demonstrate the LN2022 error, especially as I have now managed to get rid of it. I've been hit many times by that because the make_public pragmas where in the individual source files. – Mike Lischke Feb 10 '14 at 15:40
  • @MikeLischke, got it. I'll stand by my original suggestion: always put it directly after the header which first introduces the type into your library. I know of no way that that can go wrong (and it will avoid your stdafx.h issue). – Matt Smith Feb 10 '14 at 15:54
  • @MattSmith YOU ARE A GODSEND!! your macro was exactly what I needed – lululoo Aug 17 '21 at 15:57