+5  A: 

Some Background

Shared libraries in C++ are quite difficult because the standard says nothing about them. This means that every platform has a different way of doing them. If we restrict ourselves to Windows and some *nix variant (anything ELF), the differences are subtle. The first difference is Shared Object Visibility. It is highly recommended that you read that article so you get a good overview of what visibility attributes are and what they do for you, which will help save you from linker errors.

Anyway, you'll end up with something that looks like this (for compiling with many systems):

#if defined(_MSC_VER)
#   define DLL_EXPORT __declspec(dllexport)
#   define DLL_IMPORT __declspec(dllimport)
#elif defined(__GNUC__)
#   define DLL_EXPORT __attribute__((visibility("default")))
#   define DLL_IMPORT
#   if __GNUC__ > 4
#       define DLL_LOCAL __attribute__((visibility("hidden")))
#   else
#       define DLL_LOCAL
#   endif
#else
#   error("Don't know how to export shared object libraries")
#endif

Next, you'll want to make some shared header (standard.h?) and put a nice little #ifdef thing in it:

#ifdef MY_LIBRARY_COMPILE
#   define MY_LIBRARY_PUBLIC DLL_EXPORT
#else
#   define MY_LIBRARY_PUBLIC DLL_IMPORT
#endif

This lets you mark classes, functions and whatever like this:

class MY_LIBRARY_PUBLIC MyClass
{
    // ...
}

MY_LIBRARY_PUBLIC int32_t MyFunction();

This will tell the build system where to look for the functions when it calls them.

Now: To the actual point!

If you're sharing constants across libraries, then you actually should not care if they are duplicated, since your constants should be small and duplication allows for much optimization (which is good). However, since you appear to be working with non-constants, the situation is a little different. There are a billion patterns to make a cross-library singleton in C++, but I naturally like my way the best.

In some header file, let's assume you want to share an integer, so you would do have in myfuncts.h:

#ifndef MY_FUNCTS_H__
#define MY_FUNCTS_H__
// include the standard header, which has the MY_LIBRARY_PUBLIC definition
#include "standard.h"

// Notice that it is a reference
MY_LIBRARY_PUBLIC int& GetSingleInt();

#endif//MY_FUNCTS_H__

Then, in the myfuncts.cpp file, you would have:

#include "myfuncs.h"

int& GetSingleInt()
{
    // keep the actual value as static to this function
    static int s_value(0);
    // but return a reference so that everybody can use it
    return s_value;
}

Dealing with templates

C++ has super-powerful templates, which is great. However, pushing templates across libraries can be really painful. When a compiler sees a template, it is the message to "fill in whatever you want to make this work," which is perfectly fine if you only have one final target. However, it can become an issue when you're working with mutliple dynamic shared objects, since they could theoretically all be compiled with different versions of different compilers, all of which think that their different template fill-in-the-blanks methods is correct (and who are we to argue -- it's not defined in the standard). This means that templates can be a huge pain, but you do have some options.

Don't allow different compilers.

Pick one compiler (per operating system) and stick to it. Only support that compiler and require that all libraries be compiled with that same compiler. This is actually a really neat solution (that totally works).

Don't use templates in exported functions/classes

Only use template functions and classes when you're working internally. This does save a lot of hassle, but overall is quite restrictive. Personally, I like using templates.

Force exporting of templates and hope for the best

This works surprisingly well (especially when paired with not allowing different compilers).

Add this to standard.h:

#ifdef MY_LIBRARY_COMPILE
#define MY_LIBRARY_EXTERN
#else
#define MY_LIBRARY_EXTERN extern
#endif

And in some consuming class definition (before you declare the class itself):

//    force exporting of templates
MY_LIBRARY_EXTERN template class MY_LIBRARY_PUBLIC std::allocator<int>;
MY_LIBRARY_EXTERN template class MY_LIBRARY_PUBLIC std::vector<int, std::allocator<int> >;

class MY_LIBRARY_PUBLIC MyObject
{
private:
    std::vector<int> m_vector;
};

This is almost completely perfect...the compiler won't yell at you and life will be good, unless your compiler starts changing the way it fills in templates and you recompile one of the libraries and not the other (and even then, it might still work...sometimes).

Keep in mind that if you're using things like partial template specialization (or type traits or any of the more advanced template metaprogramming stuff), all the producer and all its consumers are seeing the same template specializations. As in, if you have a specialized implementation of vector<T> for ints or whatever, if the producer sees the one for int but the consumer does not, the consumer will happily create the wrong type of vector<T>, which will cause all sorts of really screwed up bugs. So be very careful.

Travis Gockel
You are so quick! Thanks!But for windows, I heard that dllimport / dllexport would put some restrictions on code like templates must have been instantiated. Is there any way we can minimize the use of them?
abel
I revised my answer to encompass exporting of templates.
Travis Gockel
So you mean it would be a bad design if my code involved a template<typename T> SmartPtr; because I'd never be able to instantiate it all possible types T?
abel
The idea would be that every place you used a `SmartPtr<T>`, that you force declaration before you use it with the `MY_LIBRARY_EXTERN template class MY_LIBRARY_PUBLIC SmartPtr<int>` or whatever.
Travis Gockel
Also, simple things are typically safer, simply because there is less stuff for the compiler to change when the version changes. But I would look into using smart pointers from Boost or Qt, since they have thought long and hard about things like binary compatibility, so you don't have to worry about it as much.
Travis Gockel
BTW, is that possible for the core to statically link to the app and to the plugin and the static variables work without problem? Because I have little things to export in plugins, that might be perfect for me.
abel
If you statically link, then you no longer have a plug-in architecture. Statically linking just makes your final executable consume the code from the static library into itself.
Travis Gockel
well, i mean plugins are still dynamically linked, but core statically linked to the app.
abel
The location of the items in the static library will be duplicated in every shared object, which means that the different dynamic shared objects which statically compiled the core into them will all have their own copies. Typically, anything that alters the global program state cannot be statically linked.
Travis Gockel
I still have some problem with explicit template instantiation. Should I place your code in a .h file or .cpp file?I tried to put some explicit template instantiation code (like what you suggested) in a .h file that has been guarded but referenced by multiple other .h / .cpp but that resulted in link errors on mac with gcc 4.0.1.Now I declare all instantiation code with "extern" and then in the cpps, I do the explicit instantiation. This compiles without error.Questions: * Was I doing anything wrong? * Will my current approach work as expected?
abel
Besides, what if I have some static variables (well, I would put variables in anonymous namespaces) hidden in cpps, they will be still duplicated across libraries?
abel
Declaring `extern` is the "proper" way to do that in the first place, so it should all work as expected. And about the static variables: if they are ultimately being statically linked, then they will be duplicated. If not, then you're fine.
Travis Gockel
Just some comments. As I have found on the link you suggested: http://gcc.gnu.org/wiki/Visibilitythat DLL_IMPORT should also be defined as __attribute__((visibility("default")))without which, the interaction would not work properly for gcc.
abel