Freitag, 23. November 2012

[C++11] Reducing compile time using forward declaration and pimpl

Just thought about how to reduce some compile time.
Things like dependencies and therefor forward declaration and pimpl.
Here is a rather long example a came up with:

To hide data that could  be changed from my "class B" I shadowed it putting it in an extra class and having a pointer to it to access it. The good old pimpl.
Instead of normal * I use a std::unique_ptr. For std::shared_ptr there is std::make_shared. The some is missing for std::unique_ptr because they forgot to include it. Here is a simple implementation
I tried returning a normal std::string from getData() but couldn't set up a forward declaration because std::string is just a typedef and those can't be forward declared.
So I've put the std::string in a class. For a little bit more abstraction  that one is capsuled again.

Accessing the data as shown in the main() isn't short and clean. Not awesome.
So I changed some lines and then it looked like this:

Making the method getData() a template with an explicit instance using std::string as T inside the class  definition helps saving a lot of code and strange capsule classes.

Doing template specialization I now have a getData() I can use to get int and std::string. Also the method now returns an object of the requested type instead of a std::shared_ptr like before. Dereferencing inside main() isn't needed anymore.
Here is that code including the code for make_unique():

 In the end I tried making a bit of nested code:

The header files just include what is necessary. In this case just memory for std::unique_ptr. All other classes could be forward declared or template-tized.  This way nice little object files could be created for each header-source-pair.

The good thing:
→ If you change a class declaration no other class declaration will change.
Here is some more about those changes and when a recompile should take place:

The bad thing:
→ Pointer.
Pointer itself aren't bad. But each access through a pointer will take some time. More time then if you could access it directly. So if you have a lot of data in your Data-class and a lot of access to it through the pimpl you will get a performance decrease.
Also when you want to access a method of the real class you need a pointer to that one. (In my D2-Data class this is the member _b.) 
(More details about those problems here:

And not enough: If your real class has some methods that are protected you need to friend your Data-class to to able to use those them from there. But that shouldn't give you any performance problems and it's easy to setup.
A bit annoying is also that you have to include the headers for the class of the object you want to use and also the forward declared classes from inside. Just imagine a nested class structure with members being from different classes and always just being forward declared you will get a really long list of includes when using that object. In that case you might want to consider putting the needed includes for your forward-declare-only-header as comment in its head. That way you can copy-paste it when using and won't forget any includes.

One thing from the last sample that can clearly be seen:
Try to have a complete class declaration early on.

If I change the declaration of class B I have to compile this and all those that include it again. Having B ready set up and just need to fill e.g. the method definitions inside the source file won't change any other code. Same goes for it's data behind the pimpl.

You will get shorter compile times or better to say re-compile times. But you will lose runtime-performance and get lots of includes in your source-files.

Think before you start to code.

The best way should be to combine all possible ways.
If the pointer overhead doesn't matter - use some pimpl.
You may also put some data-intensive methods right into the data-class.
If you don't mind having lots of includes - forward declare classes.
std::unique_ptr and std::shared_ptr are your friends.
iosfwd has some forward declaration for the ios-header (input-output-stream).
Try templates if you have problems with typedefs (but be warned of the compile-time increase for using templates). Also try instantiation and specialization.

But always:

Think before you start to code.

What are your tips and tricks for using forward declaration, pimpl, and how do you try to reduce compile time?