Previous section   Next section

Imperfect C++ Practical Solutions for Real-Life Programming
By Matthew Wilson
Table of Contents
Chapter 20.  Shims


20.9. Structural Conformance

Shims represent a distillation of what is known as structural conformance. Basically, this says that things that look the same (are expected to) act the same.

Classically (by which I mean pretemplate generics), C++ focused on what is sometimes called named conformance, which essentially refers to the situation whereby an overridden method in a derived class must conform to the method in the parent class. Naturally, this is based around vtable inheritance-based polymorphism.

Structural conformance is both more useful, and more fraught with problems. It usually pertains to template functions. As we saw in section 20.6.3, the readdir_sequence and glob_sequence classes exhibit structural conformance with respect to the sub_dir_count algorithm.

Simplistically speaking, structural conformance guarantees that types are compile-time compatible, whereas named conformance guarantees that they are run time compatible. However, there is a lot of gray in those distinctions. The weakness of structural conformance is that it is not difficult to have structurally conformant types that do semantically nonconformant things. What is often overlooked, however, is that it is not particularly hard to have semantically nonconformant classes that exhibit putative named conformance. The only difference is that the set of types to which one might apply generic code is potentially unbounded, whereas the set of types that will exhibit named conformance is bound to those that are related by inheritance. The second set is smaller, and more subject to the keen-eyed instincts of experienced C++ programmers,[17] and non-conformance is much more rare but it is still possible.

[17] It's conceivable that as the next generation of C++ programmers grow up seeing dynamic (vtable) and static (template) polymorphism as equally natural mechanisms, this differential will lessen. But it's hard for the old dogs to call this one, since we've already been through our learning process.

The question as to whether structural conformance (with the expectation of semantic conformance) is a good thing is another of those debates that rage endlessly: just do a quick search on the comp.* newsgroups, and give yourself a few days. Some hold the opinion that reliance on structural conformance is at once restrictive, prescriptive, and fragile. It locks us into a correspondence between syntax and semantics that often, in hindsight, would be better discarded or modified. Furthermore, the expectation that all parties will respect expressed, but not mandated, conventions in the unbounded set of types expressible to our generic components could be deemed impossibly optimistic.

I can certainly see the logic in that. When you look at the nastiness that is the naming of several standard library function methods, such as empty(), we see the conflict between conformance and freedom. It's rare to see a class these days with a method empty() that does not mean is empty? but that is only as a result of the standard library forcing its structural conformance on the whole development community. Previously, you would be able to find libraries where the naming was unambiguous.

Detractors argue that structural conformance represents brittleness at best and subversion at worst. They are largely correct. But what is the alternative? Pretty much every aspect of naming in C++ (and most other languages, for that matter) is reliant on convention, and it is absolutely necessary for comprehensibility. What are we to do about empty()? Should we maintain that it is ill named, stipulate that all our own libraries will use it to mean make empty! and supply a separate method is_empty() for the current semantic? All that will do is confine us to a backwater of our own making.

20.9.1 Semantic Conformance

Shims represent a convention whereby structural conformance is extended to include an explicit semantic for each shim. Naturally, this is only achieved by voluntary adherence to the semantics of a given shim. We must ensure that shims are named appropriately; hence the distinct naming conventions for the different shim concepts.

Where we compromise is in readability against potential ambiguity. Thus it can be argued that c_str_ptr() is mildly ambiguous and should have been called access_c_str(). However, I would struggle to see how is_null(), get_ptr(), or make_empty() are ambiguous in any reasonable estimation.

Overall, I acknowledge the problems of structural conformance, but don't see a better alternative. Using shims provides a significant boost toward avoiding the problems of structural conformance, but they are not a complete solution, since such cannot exist.


      Previous section   Next section