Library Implementation of Automatic Variables

Pablo Halpern, 23 September 2012

 

There is a commonly-stated principle within the C++ standards community that, rather than extend the C++ language, anything that can be implemented as a library feature should be implemented as a library feature.  I do not agree, and offer this example to show what happens when this principle is taken to the extreme.

I decided to see what would happen if we had all of the modern template features of the language but did not have the ability to declare block-scoped object variables. Could we implement a library template that produced the effect block-scoped objects?  The template would need to allocate and construct local objects upon entering the scope and destroy and deallocate them on exiting the scope, including when an exception is thrown.  Below is such an implementation.  It was remarkably easy to specify and code, but the results were less than pleasing, in my opinion.

This example is deliberately extreme.  It does not give guidance as to when a feature should be implemented as a language feature vs. a library feature, except to show that it is sometimes desirable to implement a language extension even when a library implementation of the same feature is theoretically possible.  I will further point out that, while some of us have become extremely comfortable with lambda expressions, this example shows that reaching for lamdas as the solution to almost all programming problems leads to code that looks remarkably unlike C++.

Library interface for local variables:

// This implementation assumes that local pointer types and reference types

// exist in the language, but other local variable types do not.

template <typename Type, typename F, typename... CtorArgs>

void local_var(F&& body, CtorArgs&&... ctor_args)

{

    Type *var = new Type(std::forward<CtorArgs>(ctor_args)...);

 

    try {

        body(*var);

    }

    catch (...) {

        delete var;

        throw;

    }

 

    delete var;

}

 

Usage:

 

int main()

{

    // If local variables were not part of C++, then instead of writing this:

    std::string s(5, 'a');

    std::cout << s << std::endl;

 

    // , you would write this:

    local_var<std::string>([](std::string& s){

            std::cout << s << std::endl;

        }, 5, 'a');

}