This is not a breaking issue but I like to clean my code from warnings so this is getting on my nerves.
I have been using the c++11 version of pimpl idiom to hide the class implementation for my library the usual way.
// dll header
class FrameworkImpl;
class EXPORT_API Framework
{
Framework(const Framework&) = delete;
Framework& operator=(const Framework&) = delete;
Framework(Framework&&) = delete;
Framework& operator=(Framework&&) = delete;
public:
Framework();
~Framework();
private:
std::unique_ptr<FrameworkImpl> impl_;
};
// application implementation
int main()
{
std::unique_ptr<Framework> test = std::make_unique<Framework>();
}
Everything will be fine, but I will keep getting the warning:
warning C4251: 'Framework::impl_': class 'std::unique_ptr<FrameworkImpl,std::default_delete<_Ty>>' needs to have dll-interface to be used by clients of class 'Framework'
So I tried have to add:
template class EXPORT_API std::unique_ptr<FrameworkImpl>;
Before the forward declaration but the warning would just change to:
warning C4251: 'std::_Unique_ptr_base<_Ty,_Dx>::_Mypair': class 'std::_Compressed_pair<_Dx,FrameworkImpl *,true>' needs to have dll-interface to be used by clients of class 'std::_Unique_ptr_base<_Ty,_Dx>'
I have being seeing this issue since VS2010 and I cannot figure a good way to fix this. No problems on gcc or clang and it would break my heart to use the old raw pointer version..
That is a very common issue with DLL classes, that use templates from std
.
Why does it happen?
Reason is very simple: standard specifies only guarantees, limitations and requirements. So you can be sure, that every C++ 11 compiler will provide std::unique_ptr
, that looks and works as described on this page. But everything else is implementation dependent.
The main problem is, that different implementations may (and usually, will) use a totally different structure for particular types. They use additional helper variables, different layout and so on. This may differ even between two versions of the same compiler. So if client code touches in any way member variables of your class, you need to provide DLL interface for them. That applies recursively to all types used by dllexport
ed class.
You may want to read this article on MSDN, that describes this problem with containers in mind.
This problem can be simplified to the following:
- If client code has no access to your data, disable this warning.
- If you have members, that are intended to use by client code, create wrapper, that is
dllexport
ed or use additional indirection with dllexport
ed methods.
- Usually, you can use PIMPL to hide non-DLL types, but in your case it is not applicable, since you use non-exportable type to actually implement PIMPL.
Further reading:
- MSDN: How to export an instantiation of a STL class
- Microsoft DLL export and C++ templates
- SO: Exporting classes containing std:: objects from a dll
- SO: How to use an exported class in an STL template?
Instead of exporting the entire class, you could export public methods only:
class Framework
{
Framework(const Framework&) = delete;
Framework& operator=(const Framework&) = delete;
Framework(Framework&&) = delete;
Framework& operator=(Framework&&) = delete;
public:
EXPORT_API Framework();
EXPORT_API ~Framework();
private:
std::unique_ptr<FrameworkImpl> impl_;
};
The solution was to declare the constructor/destructor after the impl declaration and a combination of Olga Perederieieva answer
Please refer to this website for a detailed explanation and sample
Header:
#include <memory>
class FridgeImpl;
class Fridge
{
public:
DLL_EXPORT Fridge();
DLL_EXPORT ~Fridge();
DLL_EXPORT void coolDown();
private:
std::unique_ptr<FridgeImpl> impl_;
};
Implementation:
#include "Engine.h"
#include "Fridge.h"
class FridgeImpl
{
public:
void coolDown()
{
/* ... */
}
private:
Engine engine_;
};
Fridge::Fridge() : impl_(new FridgeImpl) {}
Fridge::~Fridge() = default;