Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.Sign up
Inline Code in Headers Justification
If you're an experienced C++ programmer you may have noticed that all of the libpropeller objects have the function definitions inline. Since this is non-standard, even frowned upon, you may be asking "why?"
First, let's start with why not and address each point directly. Implicit inline code:
- increases the compile time.
- increases final binary size.
- makes the header harder to read (as a documentation source).
- we can just use precompiled static libraries
It is true that inline code increases the compile time. While this may be a problem for the Linux kernel, it's not a problem for any PropellerGCC code. All code that can ever be written for the Propeller is bounded in size to 32KB (or maybe slightly larger for XMM or Propeller 2 code). Even if everything is in a single file the compilation time is much less than the loading time, and will always be less than a few seconds. Therefore, for Propeller GCC code, compilation time is negligible.
Inline code increases the final binary size iff the compiler actually inlines it. PropellerGCC has some advanced internal metrics that examine the cost and value of inilining, and decides whether or not to inline the code. This applies even if the programmer uses the inline keyword or implicitly inlines the code. With functions defined in the headers, the compiler has the option to do more advanced optimization. In practice, this means that the binaries are smaller, not larger.
Let's show some numbers to back up that statement. Here are some compiled binary sizes (actual size loaded). Most of these are from the unit tests of those classes. The last one (Beta2) is an actual application built using all of the above classes.
#! Class Before After Bytes Saved Stopwatch 6132 6084 48 Serial 18040 17956 84 SD 21752 21532 220 Scheduler 7040 7036 4 PCF8523 10004 9876 128 Numbers 21428 21448 -20 MS5611 10220 10240 -20 MAX17048 7456 7420 36 LSM303DLHC 7820 7820 0 L3GD20 7128 7128 0 I2C 8280 8192 88 GPSParser 9492 9420 72 Elum 6356 6244 112 EEPROM 15600 15580 20 ConcurrentBuffer 12676 12586 90 Beta2 22224 21720 504
From the data it's clear that inline code does not increase the final binary size, except in a few minor and rare cases.
But what about in an actual project. Surely it can't work there, right? Well, it does. The following numbers are based on a project with 4 high level classes that make use of the classes above. Each class was inlined in turn, and the code size recorded.
#! Class Code Size (start) 20148 bytes Class 1 19788 Class 2 19756 Class 3 19460 Class 4 19368 --------------------- (savings) 780
In this actual project we were able to save almost 800 bytes just by inlining 4 classes. This, plus whatever we have already saved by inlining all the lower level classes.
The third reason for inlining is personal: inline code makes the header harder to read, which reduces it's value as documentation. Java programmers get by just fine with inline code in their headers, don't they? And the classes shouldn't be that large anyway, otherwise they should be refactored. But if you're looking for documentation, look no further than the Doxygen generated API webpages.
Finally, opponents present the precompiled libraries. The problem with those is that they hide the source code, and they have to be compiled. This is an issue for the Propeller where you might have several incompatible compile time options that you want to use. The library provided would have to provide a library for each combination of options such as -cmm, -lmm, etc. This is a hassle, so we don't do it.
Let's look at why you would want to inline code, distinct from the above:
- It makes it easier to include it in a project
- It reduces the number of dependent files floating around
- Did I mention that code size is smaller?
The standard method of including a C++ file in another file is via the #include directive. This is handy because the compiler will automatically find the .h file by searching various paths and include it. It's not so good for .cpp files: you have to define those explicitly in your makefile. This makes it a hassle to create new projects. It's much easier just to provide a link to the libpropeller path and let the compiler find the header files.
Secondly, the separate .cpp and .h files means that you have to keep both in sync. Do you want to change the function name? Gotta change it in both. Want to make the function return something? Change it in both. This is a hassle that inline functions don't have to deal with.
Finally, code size is smaller. Yeah, defining your functions in the header file reduces code size, not increases it.