Exporting Classes The Right Way, Tricks and Tips?
So, after a long time of asking you for advises about dlls, I've reached to a conclusion on how to do stuff and I would like to show it to you and tell me if it's correct.
First of all let's start with the easy stuff. C++ interfaces, doesn't even need to be exported as dlls since all the implementation is getting handle by the client. Your library just need to define that interface inside a header file, and the client the only thing he need's to do is to include that header file, inherit that interface and implement the abstract methods.
Now let's talk about classes which are not getting inherited by the client. People here told me that the best way is to create a C factory function that generates an instance of that class. I understand how this works but what about the Methods? Do I have to create a C function wrapper for each method of that class and export these functions into the dll? Consider the code below:
MyClass.h
Code:
class MyClass
{
public:
MyClass();
~MyClass();
void PrintHi();
};
MyClass.cpp
Code:
#include "MyClass.h"
#include <iostream>
MyClass::MyClass(){}
MyClass::~MyClass(){}
void MyClass::PrintHi(){ std::cout << "Hi!" << std::endl; }
extern "C"
{
__declspec(dllexport) MyClass *CreateMyClass()
{
return new MyClass();
}
__declspec(dllexport) void DeleteMyClass(MyClass *obj)
{
delete obj;
}
__declspec(dllexport) void PrintHi(MyClass *obj)
{
obj->PrintHi();
}
}
So, do I need to create a C wrapper for each Method? Because I tried to only create a factory function (CreateMyClass) and on the client side every time I was using myObj->PrintHi() the compiler was saying "Undefined reference to PrintHi()" and this make sense since I did not export that Method into the dll. This C wrappers will probably work but I'm loosing the C++ way of using a class. And what if I want to inherit that class? How is the constructor of the base class is going to get called since i haven't exported it? As you can see I'm losing all the good object oriented stuff C++ give's me when using C wrappers.
I thought this carefully and I noticed that every C++ library I used until know (And because I'm a graphics programmer I use a lot of libraries) all of them where only header libraries. Is this a coincidence? Maybe dlls are the reason? You can't export classes into dlls which are going to be compatible with all the compilers out there but what you can do is actually write the whole implementation of you library into header files and let the client include these headers and compile them easily by himself. Is this a good practice to do when creating libraries in c++, since exporting classes into dlls should be a NO for compatibility reasons?
And then I also thought about precompiled libraries. Why not creating my whole library into header files only and then create a pch.h file that includes all of the necessary files and let the client include that pch.h file, compile it the first time only and use it as a precompiled library. Fast, easy to compile and has compatibility.
So what do you thing? Have I understand everything correctly? Do you have any better ideas?
Thank you!
Re: Exporting Classes The Right Way, Tricks and Tips?
Quote:
Originally Posted by
babaliaris
Why not creating my whole library into header files only
Yes why not?
I'd say it's a trend today to distribute libraries by making the library source code available as a set of include files. The users aren't even required to build a compiled version of the library. They just start including the include files of interest into their source code. This is for example how most libraries in the Boost library collection are distributed. These kind of libraries are sometimes called header libraries. In practical terms it means the whole library is on .h files. It's possible because the traditional separation of C++ source code into .h and .cpp files is a convention only and not something C++ demands.
These are your options to distribute a library:
1. Either by way of source code, or compiled code (with include files holding declarations).
2. If it's source code the user either uses it as a pure header library, or is required to build a compiled version of the library before use.
3. A library in compiled form (regardless of who compiled it) must be linked. There are three ways (of which at least one must be supported by the library provider): static linking, dynamic linking (dll) and dynamic loading (also dll but application controlled).
Re: Exporting Classes The Right Way, Tricks and Tips?
Quote:
I thought this carefully and I noticed that every C++ library I used until know (And because I'm a graphics programmer I use a lot of libraries) all of them where only header libraries. Is this a coincidence?
No! You're just starting to feel the pain of c++ and .dll :eek: Unless there is a very good reason, it's much better to have your c++ library code in a header file(s).
Re: Exporting Classes The Right Way, Tricks and Tips?
Then I will stick with this way and of course I'll follow 2klaud's advice to treat c++ as a completely different language ( I'm coming from c)
Re: Exporting Classes The Right Way, Tricks and Tips?
If you plan to distribute your library as a pure header library then there are three basic rules to follow:
1. Each file must have an include guard.
2. Free functions must be declared inline.
3. Use a library specific namespace.
Re: Exporting Classes The Right Way, Tricks and Tips?
Quote:
Originally Posted by
wolle
If you plan to distribute your library as a pure header library then there are three basic rules to follow:
1. Each file must have an include guard.
2. Free functions must be declared inline.
3. Use a library specific namespace.
I always use 1,3 but number 2 by free you mean destructor-like functions (which they free objects?). If yes, why I should declare them as inline?
Re: Exporting Classes The Right Way, Tricks and Tips?
2. Functions defined outside of a class definition in a header file need to be declared as inline so that if the header is included in multiple compilation units you don't get an error referring to already defined functions (I've forgotten the exact error message). See https://en.cppreference.com/w/cpp/language/inline (see description 1) ).
Re: Exporting Classes The Right Way, Tricks and Tips?
Quote:
Originally Posted by
2kaud
2. Functions defined outside of a class definition in a header file need to be declared as inline so that if the header is included in multiple compilation units you don't get an error referring to already defined functions (I've forgotten the exact error message). See
https://en.cppreference.com/w/cpp/language/inline (see description 1) ).
Oh, I understand what you mean. I didn't know that.
Re: Exporting Classes The Right Way, Tricks and Tips?
If I remember correctly from earlier, you are allowing users to create plug-ins to run with your already compiled program. If this is true, you will still have the issues associated with passing c++ objects in dlls regardless of whether you define the library classes entirely in headers.
Re: Exporting Classes The Right Way, Tricks and Tips?
Like as been said before - c++ isn't c! :)
What books/resources are you using to learn c++? See http://forums.codeguru.com/showthrea...560001-C-Books. Professional c++ is recommended.
Re: Exporting Classes The Right Way, Tricks and Tips?
Quote:
Originally Posted by
Arjay
If I remember correctly from earlier, you are allowing users to create plug-ins to run with your already compiled program. If this is true, you will still have the issues associated with passing c++ objects in dlls regardless of whether you define the library classes entirely in headers.
Well one option would be to force the client to code in pure c :D of course this is not a good choice, I want him to be able to use the goodies of c++ oriented programming. Well, maybe somehow I can combine hpp and dlls together to achieve that. Haven't think a lot about it yet.
Re: Exporting Classes The Right Way, Tricks and Tips?
It just came to me, what if I let the client to implement his plugin however he like and then define an entry point function which will be the only which he's going to export on the dll and then my program is going to call that function. Is this going to work?
Re: Exporting Classes The Right Way, Tricks and Tips?
I tried the code below and it works! Isn't that solving my problem of the plugins (Dynamic Loading)?
I'm not exporting any classes, just an entry point c function which i call StartPlugin()
Basically, the Client can do whatever he wants with his code and he just need to provide only one C function
which is going to be exported into the dll and used like an entry point of his code into my extensible application.
Plugin DLL Code
Code:
#include <iostream>
#include <string>
class Base
{
private:
std::string message;
public:
Base(std::string msg):message(msg)
{
}
~Base() {}
protected:
void printMessage()
{
std::cout << message << std::endl;
}
};
class Plugin : public Base
{
public:
Plugin() : Base("Initializing the message...")
{
}
~Plugin()
{
}
void print()
{
printMessage();
}
};
extern "C" __declspec(dllexport) void StartPlugin()
{
Plugin *obj = new Plugin();
obj->print();
}
My Awesome extensible app
Code:
#include <iostream>
#include <Windows.h>
typedef void(*StartPluginFunc)();
int main()
{
//Load The DLL.
HINSTANCE handle = LoadLibrary(TEXT("C:\\Users\\babaliaris\\source\\repos\\test_solution\\Debug\\Plugin.dll"));
if (!handle)
{
std::cout << "Dll specified could not be found!" << std::endl;
std::cin.get();
return -1;
}
//Load The StartPlugin Symbol.
StartPluginFunc StartPlugin = (StartPluginFunc)GetProcAddress(handle, "StartPlugin");
if (!StartPlugin)
{
std::cout << "Could not find symbol!" << std::endl;
std::cin.get();
return -1;
}
//Call the plugin.
StartPlugin();
std::cout << "Press ENTER to continue..." << std::endl;
std::cin.get();
//Unload the library.
FreeLibrary(handle);
return 0;
}
Re: Exporting Classes The Right Way, Tricks and Tips?
Yep, that's getting on the right track. You should supply a StopPlugin export so the caller can clean up.
Re: Exporting Classes The Right Way, Tricks and Tips?
Code:
typedef void(*StartPluginFunc)();
rather than using typedef (which is c), the c++ way would be to use using
Code:
using StartPlugInFunc = void(*)();
See https://en.cppreference.com/w/cpp/language/type_alias
Also
Code:
//Load The StartPlugin Symbol.
StartPluginFunc StartPlugin = (StartPluginFunc)GetProcAddress(handle, "StartPlugin");
in c++ would be coded as
Code:
const auto StartPlugin = reinterpret_cast<StartPlugInFunc>(GetProcAddress(handle, "StartPlugin"));
See https://en.cppreference.com/w/cpp/la...interpret_cast and the links to the other c++ casts - const_cast, static_cast and dynamic_cast.