Would exporting BookStore
will also export BookCollection
and Book
indirectly or they also need the macro for export?
They also need the macro.
The compiler only exports what is marked for export. It doesn't automatically export arguments nor return types for methods and functions that are being exported.
As follows;
class BOOKSTORE_API Book
{
std::string title;
std::string publisher;
char * getTitle();
char * getPublisher();
}
class BOOKSTORE_API BookCollection
{
std::vector<Book> books;
int getBooksCount();
Book getBook(int location);
}
class BOOKSTORE_API BookStore {
// ...
};
You will get additional warnings about the members not being exported. Provided you use the same compiler and settings for the dll and the exe, these are largely noise and can be silenced (or disabled).
A more comprehensive alternative is the pimpl pattern and remove the std::vector
et. al. from the class definition(s) and the standard library members would not need to be exported from the dll. MSDN has a nice article on this.
class BOOKSTORE_API BookCollection {
protected:
struct Pimpl;
Pimpl* pimpl_;
}
// in the cpp compiled into the dll
struct BookCollection::Pimpl {
// ...
std::vector<Book> books;
// ...
}
On the "rule of three" and the "rule of five" and the unresolved symbol(s)...
When exporting classes from a dll, it is best to export all the special members as well to avoid unresolved symbol errors. This is especially applicable if using the pimpl idiom.
[S]uppose all these classes are in different files, should the macro remain the same or [does it] need to be changed per file?
Keep the macro and the #define
that contains it the same per dll. So, if for the single dll, they are in three files, then they all use the same #define
block. Essentially you are controlling the import/export on a per dll basis. I would also put the define block into a header of its own and include it in the header for each class (but that is not needed).
[F]rom this simple example, would [a] different msvc compiler version or CRT of the client code raise undefined behaviour as I am aware that returning [an] STL object would cause this. Since in this code, the getter only return primitive C datatype, would it be a problem as well?
Yes, different compiler versions and standard libraries could/would cause problems. E.g. even if all the constructors, assignment operators and destructors were exported from the dll, the client still needs to allocate the correct amount of space (via. new
or on the stack) for objects of this class. Different standard libraries could have different sizes for std::string
etc. and mixing them will create memory corruption etc. In this respect, the pimpl or NVI (non-virtual interface or template patten) is better.