Fundamental Theorem of Software Engineering

The fundamental theorem of software engineering (FTSE) is a term originated by Andrew Koenig to describe a remark by Butler Lampson[1] attributed to the late David J. Wheeler FRS:[2]

“We can solve any problem by introducing an extra level of indirection.”

 

It is a basic guidance in C++ Template Metaprogramming’s problem solving when we encounter any difficulties in supporting more generic types or special partial specifications of a template. This sentence is quoted often in the book <<C++ Template Metaprogramming: Concepts, Tools, and Techniques from Boost and Beyond>>

Let us see an example from this book on how this FTSE can be applied.

Below is the initial sample which will normally come out of our mind:

 

#include <iostream>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/for_each.hpp>
struct A{};
struct print_type
{
template <class T>
void operator() (T t) const //I added t here for your easier reading
{
std::cout << typeid(T).name() << std::endl;
}
};
typedef boost::mpl::vector<int, long, char*, A*> s;
int main ()
{
boost::mpl::for_each<s>(print_type());
}

The output is:

i
l
Pc
P1A

 

If you want to add references into the type list, for example, typedef boost::mpl::vector<int, long, char*, A&> s;you will get compilation error. The underlying reason is that a reference must refer to an object already created. For other types than reference, t will be created as temporary object with its default constructor.

 

We have a dilemma here, in function operator() (T t), we have to use a type T which will utilise its default constructor to create a temporary object, however we cannot use T directly here if T is a reference type.

 

The idea here is that since we cannot create T& in function operator()(T t), maybe we can follow  FTSE to use one extra layer of indirection to create a wrapper like CWrap<T>? then no matter T is a reference or not, we can create temporary object of  CWrap<T>. Meanwhile we can still retrieve type T from the type CWrap<T>. It sounds great.

 

Below is the sample codes provided in the book <<C++ Template Metaprogramming: Concepts, Tools, and Techniques from Boost and Beyond>>. I have made some modifications to make sure it compiles with my GCC compiler in RHEL. Code difference has been highlighted.

#include <iostream>#include <boost/mpl/lambda.hpp>#include <boost/mpl/transform.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/for_each.hpp>

struct A{};
template <class T>
struct CWrap
{
};

struct print_type
{
template <class T>
void operator() ( CWrap< T > c ) const
{
std::cout << typeid(T).name() << std::endl;
}
};
typedef boost::mpl::vector<int, long, char*, A&> s;
int main ()
{
boost::mpl::for_each<
boost::mpl::transform< s, CWrap<boost::mpl::_1> >::type
>(print_type());
}

The output is::

i
l
Pc
1A

 

 

Now all types in s have been transformed into CWrap<T> before it is passed into operator()( CWrap<T> ) function. In this way that we use T only to specialize types of CWrap<T> that T can be used for typeid(T) and CWrap<T> is used to contruct the temporary object.

 

What a brilliant way to solve the dilemma encountered before by introducing one indirection – template class CWrap with the guidance of FTSE!

 

About xiongzou
a passionate C/C++ programmer

Leave a comment