- Each room would have its own HVAC system.
- All the fixtures would have secondary, unrelated purposes, like the toilet that has a lava lamp / nightlight function.
- When you put food it in the fridge, it may be something quite different when you take it back out.
- To make a small change during construction, you'd have to demolish a large chunk of the house.
Let me link you to an example of the problem: sample code. I'm not an anti-Microsoft type, but I do find their dedication to procedural C discomforting. There's never a C++ example of using the Windows API on this site, so you never see a simple illustration of the relevant concepts. Large parts of the code are dedicated to managing memory blocks, HANDLEs, etc. So if you take a bewildering API like CryptoApi, then create samples in C that mix in alot of boilerplate code, you end up with something that you can't scale or change without red flags coming out. By the way, did Microsoft miss the memo about goto 20+ years ago? I was told not to use that keyword on the very first day I learned C.
The solution is simple. C++ lets you put all your boilerplate tasks into classes that take care of cleanup tasks at destruction. The simplest example of this a smartpointer class, where you free a block of memory or close a "handle" upon destruction. If you use smartpointers, you have no need for goto statements or awkward Boolean logic to handle boilerplate tasks. Simply return from the function, and your class destructors will clean up allocated resources. We'll come back to this shortly.
Another awkward aspect of C and C++ is return types. We teach the kids at an early age never to return anything "big" from a C function. So what do you do? You can pass a structure in by reference and fill it up. You can allocate the structure or class instance, returning a pointer to it that needs to be cleaned up later. You can break the rules and return something huge. There are lots of problems with all these approaches, and I won't dive into that here.
Unless you're a low-level API developer, you generally write programs to address business-related use cases. You should not have to focus on memory management and other primitive concepts. C++ developers deserve to return objects from functions, just the way you do in Java and C#. Fortunately, you can accomplish this in C++ with just a template class and a few lines of boilerplate code. You end up with an aggregated class model, with an outer class (your template specialization) that takes care of reference counting, and an inner class that addresses the business use case.
Enough talk. Here's some sample code:
Stackable.h
AfxTesting.cpp
Project (zipped)
Note that we've implemented smartpointers and returnable objects with one template class. The reference counting takes care of cleaning up inner pointers when the reference count hits zero. The aggregation (using one inner pointer to your business class) keeps the outer class small with regard to sizeof(...), so it's fine to return these classes from a function.
This framework does not come for free. Compilers have switches related to exception handling that may allow your destructors to be "skipped over". Also, compilers generate instructions for your destructors. They often require a stacked (rather than inline) function call. The haters out there would scream about the performance cost. Fair enough, but the alternative is hard to read and hard to scale code. If your code is cleaner, you greatly reduce the probability of creating bugs, and you're free to optimize for performance in other ways.
Threading is another caveat with the sample classes. You would need to allocate a MemoryBuffer on the heap to pass it between threads. A stacked instance gets cleaned off the stack in one thread when the function exits, so the other thread is using a pointer that is either bad or will be bad soon. It is worth pointing out that you'll probably crash your app if you pass a stack-based instance to another thread.
So what did we accomplish here? We minimized the code footprint of boilerplate tasks so that our code can be more readable, scalable, and focused on business use cases. We created a framework allowing us to return objects from functions rather than using pointers or other less manageable (or intuitive) alternatives. Now, instead of making refrigerators for the house we're building, we're just buying and installing them. This allows us to focus on the structure of the house and ignore the details of appliances.
Cheers,
Chris

No comments:
Post a Comment
Please keep it short, respectful, and clean!