So, what is a “Wrapper Layer”? Typically, a wrapper is a thin set of functions or methods (a “shim”) that hides underlying libraries or services and presents a compatible API or set of APIs, the wrapper, to higher level code. The shim code is typically just calling underlying functions translating any data structures used in the higher layer to whatever is compatible in the lower layer, so typically it adds a small bit of overhead but is typically just a one or two lines of code.
Why would you want to do this, sounds like a lot of extra work? The amount of work depends on how close the underlying code is to the desired API, but the reasons include:
- The desired API has already been defined so you are porting the upper layer code. This might happen when a product upgrades its processor and needs a different kernel, if you had a wrapper on the first generation product and a viable kernel for the new product it’s a simple matter to rewrite the wrapper for the new kernel and the port should take very little time (using a different compiler might add some pain).
- The API is used on multiple products with a variety of hardware and firmware. Wrapper standardizes the code across all platforms.
- Having a wrapper makes the code easier to debug, as you now have direct control of all inputs and outputs of the layer you can collect stats and intercept events at critical points making debugging much easier especially if the wrapped code only exists in binary form.
- The wrapper also allows developers to translate code from one language to another. For embedded code that might mean some assembler code can be wrapped for the C code or you could have some code in entirely different languages the wrapper handles the translation interface.
- The wrapper also allows you to use foreign code without modifying it, which might be required for legal reasons.
In my own experience writing a wrapper also enhances your understanding of the underlying library that would have been made by someone else and enforce restrictions on what functions or modes of operation are supported. This simplifies the API the rest of the development team will have to deal with and reduce the “lost sheep” syndrome.
Reasons to NOT use a wrapper include:
- The target is a one off and there’s no chance it will be used for anything else, which is harder than it might seem. I’ve had many projects that were quick prototypes turn into products and ported far and wide while code intended for wide use didn’t go anywhere.
- The expected code is small and simple and only a limited set of functions from the underlying library is needed.
The term wrapper is a bit archaic but still in use for C/C++, Java and assembler, but I haven’t seen it used in reference to interpretive languages like Python but there’s no reason why you couldn’t write a wrapper in Python.
So, the next time you have a new project do an assessment of whether you’ll be better with or without a wrapper. In general, if I’m unsure I error on the side of a wrapper as it doesn’t add much time and builds a nice solid base you can build on top off because each time you think you see a service you need at some level the wrapper changes have to be justified. This regulates the API to only what’s needed and ultimately makes for better tighter code.
Some might suggest an alternative method that eliminates the function call shim by using macros. This can be effective and have some benefits, but this only works if the only differences in the calls are the name and order of parameters. Using macros like this seems to encourage “clever” programming that has its own pitfalls but the big issue with using lots of macros it makes tracing more difficult compared to using a real function call.
Hope that helps with your understanding of when to use a wrapper, please add suggestions as I’m probably missed some pros and cons.
Cheers!