C++/CLI: Best practices for designing library  
Author Message
Tomas Petricek





PostPosted: Visual C++ Language, C++/CLI: Best practices for designing library Top

Hi,

I'm working on library written in C++/CLI that will be used only from C++/CLI (which means that I don't need to produce pure .NET assembly), so I'm looking for some tips and recomendations how to design C++/CLI library (especially what are the possible ways for building and redistributing it, how it could be used in other projects, etc..).

I have the following needs:

  • It must be distributed with some header files (because I need to use templates in several classes)
  • I need to separate declarations (.h files) and definitions (.cpp files)
  • I'd prefer to distribute it as a dll with some header files, but distributing it with the complete source wouldn't be a big problem...

Probabbly the only possibility is to split classes that could be built into .NET assembly (as base classes) and distribute the rest as header files (as inherited classes), but I'd like to read some recommendations before I start splitting it :-).

Do you know about any documents or articles on this topic I tried searching in MSDN and on the internet, but I didn't find anything useful :-(

Thanks!




Visual C++16  
 
 
Gordon Hogenson - MSFT





PostPosted: Visual C++ Language, C++/CLI: Best practices for designing library Top

I would suggest that you consider designing it so that it could be used by other managed languages, since you never know when that requirement may appear in the future, and also because even accessing your library from two different C++/CLI assemblies is a lot like using your C++/CLI code from another managed language like C#.

That doesn't mean it has to be a pure assembly, but it may mean that you provide a mixed assembly that has managed interfaces (and probably generic interfaces) that is consumable from other managed languages. Mixed assemblies can be invoked via #using from managed languages. Any native types or templates aren't visible to the C# (or VB.NET) code, but the managed types and generics can be accessed from other managed languages. This would imply that you would include both the base classes and the derived classes in the dll rather than just in header files.

With the derived classes in header files, you may run into problems when you have multiple assemblies using your library. The same class #included in more than one built assembly will look like a different type with a duplicate, colliding name as far as the compiler or CLR is concerned, so that when a third assembly uses two assemblies that both #include your type, a compilation error will result saying that the common type was already imported from one of the assemblies, and therefore can't be imported by the other. This would only be a problem for public types that are exported to other assemblies, so as long as #includes declare only private types, you will be OK.

A good rule may be to ship include files only for private types but ship the DLL for public types... just be very clear up front in the design as to which is which and understand the client scenarios for when your library's clients will be importing via include and when they will be importing via #using. You cannot do both in the same client assembly. Essentially your clients will have to be clear that they can use the private types inside one assembly at a time and there won't be any communication with the private types in another assembly, so the clients have to understand that from the beginning in order to sensibly plan their use of your library code.

You say that you're going to have some include files with managed templates. If these are collection types, maybe you should consider creating a generic interface for each managed template collection class that will allow other managed languages or other assemblies to work with those collections. Then you could follow the rule suggested above and make the templates all private and the generic interfaces public.

I'm sure there are a lot of other considerations, but I hope that helps get you started thinking on this...



 
 
Tomas Petricek





PostPosted: Visual C++ Language, C++/CLI: Best practices for designing library Top

Thanks for the answer Gordon!

I'm pretty sure that it will be used only in C++/CLI, but exposing base classes to other CLR languages (which you mentioned) is very interesting approach! I found my old Whidbey "beta 2" DVD and looked at STL/CLR which has quite similar requirements as my library (exposing C++ managed templates). If I understand it correctly, STL/CLR is implemented as private types and it is included in project using headers.

Since I'm beginner in C++/CLI I'll have one (probabbly) simple question - you mentioned that I can create private and public types. I know how to create DLL with public types (that can be used as types from any .NET assembly), but how can I create library with private types (imported using header file)

I tried building the project as a static library (LIB) and using the library in simple application (I added the lib file to the linker settings and included header file), but when building the application I get following error:

>> Linking...
>> Demo.obj : error LNK2020: unresolved token (060001E5) TestLib.ClassBar::Foo

The header looks like this:

// TestLib.h
#pragma
once
namespace
TestLib
{
public ref class ClassBar
{
public
:
void Foo
();
};
}


Thanks!
Tomas



 
 
Gordon Hogenson - MSFT





PostPosted: Visual C++ Language, C++/CLI: Best practices for designing library Top

You can create a private library with header files as you would expect in C++, you create your .LIB and link it in. From the error message, it seems that the definition of Foo isn't being found Double check that it is defined in a .cpp file that is compiled into your .LIB. You can verify whether it made it into the .LIB file by running dumpbin /exports on the library file.



 
 
Tomas Petricek





PostPosted: Visual C++ Language, C++/CLI: Best practices for designing library Top

Again, thanks for the answer! The solution using private library and headers would be great for my project. Unfortunately I'm still having problems with exporting classes from dll (probabbly some stupid mistake caused by my lack of C++ experiences). If I use the following code (compile it as a LIB and include the LIB and header in demo application) it works as expected (however if I look at the exports using "dumpbin /exports TestLib.lib" it doesn't show any exported functions; I also tried adding module definition (DEF) file - in this case I can see the exports, but I don't know how to export class methods using DEF file):

// TestLib.h
namespace TestLib
{
class MyMathFuncs
{
public
:
static double Add(double a, double b
);
};
};

// TestLib.cpp
#include "stdafx.h"
#include
"TestLib.h"

using namespace TestLib
;
double MyMathFuncs::Add(double a, double b
) {
return a+b
;
}

As I said, if I have this library, it works well, but when I change "class" to "ref class" it stops working and I'm getting linker error when compiling the application that uses library (error LNK2020: unresolved token (060001ED) TestLib.MyMathFuncs::Add). Is it possible to export "ref classes" using same technique as native classes

I was looking at the STL/CLR headers again and I couldn't find any method declared in the headers without implementation - does this mean that entire STL/CLR will be compiled into every exe/dll that uses it And if yes, how is the problem that you mentioned earlier solved in STL/CLR (if two libraries include the common classes, it can cause collision when both libraries ara used in third project).

Thanks!



 
 
Holger Grund





PostPosted: Visual C++ Language, C++/CLI: Best practices for designing library Top

In my very limited experience the standard library searching mechanism is broken with managed code. I don't have a VC 2005 compiler here, but I believe the compiler will emit a TypeDef for MyMathFuncs in every translation unit which includes TestLib.h. The linker should then merge several input assembly scopes of the contributing object files into the final assembly.

In your case, the consuming translation unit will have a TypeDef for MyMathFuncs with an incomplete MethodDef for Add. The linker will then recursively select object files from the various libraries to satisfy outstanding references. This is done via the archive symbol table of the libraries. It lists which contained object file exposes which symbol (with external linkage). I have no idea, how that is supposed to work for metadata references (as in your case). One would expect that the same lookup is performed via the mangled name of the function (which is available). But in my experience that doesn't (always ) happen.

Have you tried passing the object file (instead of the .lib file) to the linker If that works, you're probably seeing the phenomenon I described above. Unfortunately, I am not aware of decent solution. Proxy symbols and #pragma linker("/INCLUDE") should work, but are akward for larger libraries - and actually also for smaller ones.

That being said, it's usually a good idea, to factor common things out into a common assembly and only have thin wrappers exposed in your code that gets duplicated into every consuming assembly. Which means, just write your code inline or at least include the function bodies from your headers.

Class templates are only instantiated when used - so it's usually impossible to precompile them (be it in a lib or in an external DLL). Even explicit instantiations might cause problems. Also, it is perfectly possible to have types with the same name in different assemblies. They will simply be different types. This may cause confusion for a programmer, but it isn't a problem for the CLR.

Bottom line, just pack everything, which you want duplicated in your client apps into your headers and the common things you want to have shared into a separate assembly. The headers should use metadata references to the shared assembly only.

But then again, my knowledge of the area is very limited.

-hg


 
 
Gordon Hogenson - MSFT





PostPosted: Visual C++ Language, C++/CLI: Best practices for designing library Top

Holger is right. The method I described works if you link with the object file, but not as a static library (or even as a DLL with an export library). That will teach me to recommend something without testing it out first.

I'll have to concur with Holger that including the code in the header files seems to be the way to go. I believe this is what STL/CLR does. You won't run into the problem I described unless you try to simultaneously #include and #using the same types. As long as the types you are #including are private, your clients won't run into problems since they can't be invoked via #using.




 
 
Tomas Petricek





PostPosted: Visual C++ Language, C++/CLI: Best practices for designing library Top

Thanks Holger and Gordon!

Linking "obj" files produced by library compilation (together with including header files) works great. I also downloaded last "Orcas" CTP to look at the STL/CLR. It has some common interfaces declared in shared managed assembly (so you can pass collections across different assemblies via these interfaces) and C++ template classes are declared (and implemented) in header files that are included in applications (and compiled as part of these assemblies). However it looks that whole STL/CLR implementation is in the headers and (where possible) in the shared assembly, so it dosn't use any "OBJ" files.

I think I understand it much better now! Thanks again!