Comparison of Metaprogramming and Template Programming

Download Comparison of Metaprogramming and Template Programming

Preview text

Comparison of Metaprogramming and
Template Programming Solutions for
Implicit Invocation of Invariant
Jonathan Gdalevich
Large software systems commonly contain multiple independent components. When independent components change, dependent components must change as well in order to establish system invariants. This scheme leads to a variety of approaches for components to communicate with each other to maintain the invariants. One promising way to do so is to automatically generate code for maintaining the invariant between dependent and independent components through implicit invocation. However, since a complex system could have many invariants and performance requirements, the generated code must have a small runtime overhead. This paper explores eight separate approaches for the implementation of implicit invocation invariant maintenance in C++ using compile-time metaprogramming via OpenC++ and generic programming with C++ templates.

1 Introduction
1.1 Motives
An invariant is a relationship between components in which the state of one component, the dependent, is based on the states of other independent components. For instance, if a variable a in component A must always be twice the value of variable b in component B, A is a dependent component that depends on independent component B. The formula a = 2 * b is therefore the invariant. The easiest way to specify the relationship is with explicit invocation in which A and B know about each other in order to communicate the event of change in B. However, explicit invocation results in high coupling between system components complicating maintenance and addition of features. Another method of invariant maintenance is implicit invocation [1] where instead of invoking a direct procedure, the independent component announces the event to any dependent components responsible for maintaining the invariant. In implicit invocation, B would announce that it has change to all system dependent components. A would detect the change and get the value of B in order to change itself. True benefit of implicit invocation can be seen with an addition of component C dependent on B. With explicit invocation, B would have to keep track to both A and C so that both could be notified in case B changes. Conversely, implicit invocation allows B to have no knowledge of A or C but to simply announce changes so that both A and C detect the change and update themselves as necessary.
While beneficial, the use of implicit invocation for invariant maintenance raises two questions. First, there are many approaches to implementing implicit invocation. Some

require extra objects to hold the invariant rules and detect changes while others distribute invariant maintenance between multiple dependent and independent components. Likewise, while certain approaches allow an invariant to be implemented in both directions with ease, others only support a single direction. Second, a complex system can support hundreds or thousands of invariants making it difficult to keep track of dependent and independent components. Therefore, generative programming techniques must be employed to implement implicit invocation from rules written in a declarative language such as OCL [3] or another constraint language. Two appealing C++ implementation techniques are metaprogramming and template programming. When used with different implicit invocation approaches, the advantages and disadvantages of each technique must be analyzed and compared to determine the best one for each particular situation.
One way to implement implicit invocation within invariant maintenance is through the use of metaprogramming as described in [4]. Metaprogramming involves the manipulation of program code, data, or objects either at compile time or runtime by another program. In the a = 2 * b example, metaprogramming can be used to generate the code required for B to broadcast its change event and for A to receive the event and update itself. Moreover, the same metaprogram can generate update code for both A and C if given the description of the invariant, freeing the developer from having to keep track of dependent and independent components of each invariant. Furthermore, the metaprogram can be written to generate different implementations based on the nonfunctional requirements such as reuse or performance. For instance, a change in B

would change A to 2B while a change in A not caused by a change in B would not effect B’s value. On the other hand, a change in B would change C to 3B but a change in C, not cause by a change in B, would also change B through the invariant B = 1/3C. Given the invariant rules and the components, it is perfectly valid to expect the metaprogram to implement all three without errors within the resulting program. However, metaprogramming, aside from template programming, is not built-into C++ and usually requires a separate tool or compiler for executing the metaprogram.
Contrasting metaprogramming, the template programming has been an integral part of ANSI/ISO C++ since early implementations [5]. The main purpose of templates is to allow one component or structure to take in or use different datatypes determined at compile time. This permits templates to be used in generating an implementation of implicit invocation where the dependent component does not know the invariants independent components until compile time. Without templates, the developer would be forced to specify the exact type of the independent component in the code. When combined with generative programming, templates that support implicit invocation can be generated based on invariant constraints before compiling the entire program. Best of all, since most C++ compilers support template programming, no extra tools or compilers are required for compiling the generated code with the system components.
The purpose of this research is to determine which programming technique under which approach would be the most beneficial in implementing implicit invocation for invariant maintenance. The benefits can be divided into two broad categories of quantitative and

qualitative analysis. Quantitative analysis is measured through compile and runtime comparison as well as the analysis of final assembly code. Qualitative analysis, on the other hand, is harder to quantify and will be determined by the amount of code the programmer has to modify and the lines of new code written to take advantage of each technique and approach. Combined, quantitative and qualitative analysis will be used to conclude on the best approach to implementing implicit invocation for invariant maintenance.
1.2 Related Work
Besides implicit invocation, there are a number of techniques to avoid direct references between dependent and independent components. Researchers at the University of Twente in the Netherlands, developed objects known as Abstract Communication Types that contains abstract senders and receivers for handling communication between system components [6]. ACTs also provide mechanisms for synchronization of messages and the ability to reflect upon a message. They are implemented in Sina which is not as widespread or popular as C++. On the other hand, Robert DeLine’s Flexible Packaging [7] allows the user to determine the exact nature of interaction between components at integration time by separating each component into functional and interactional parts. Due to a large runtime overhead required for message passing, Flexible Packaging results in a significant runtime cost and should be avoided for invariant maintenance. Finally, Kevin Sullivan and David Notkin from the University of Washington describe and a case study in the implementation of a mediator to support invariant maintenance between components [8], [9]. A mediator is a separate component that contains implicit or explicit references to all components in the invariant as well as the relationship between

dependent and independent components. The components themselves know only about the mediator and announce any changes to it. Inside the mediator, changes from independent components are received and applied to the dependent components. Any change to the invariant is implemented within the mediator and does not require changing components aside from adding or removing mediator references.
Metaprogramming is divided into runtime, also known as reflection, and compile time metaprogramming. Reflection is the most interesting since it allows interaction and modification to objects, components, and structures that do not exist as compile time. Languages like Smalltalk and Lisp facilitate reflection through the use of an interpreter implemented in the same language. Brian Foote and Ralph Johnson demonstrate the ease and usefulness of reflection within Smalltalk-80 [10] through the construction of monitors, distributed objects, and futures, and the experimentation with new inheritance, delegation, and protection schemes. Moreover, Fred Rivard uses Smalltalk reflection to implement an invariant constraint system resulting in a 2% compile time increase in exchange for a 9% reduction at runtime [11].
Unlike Smalltalk and Lisp, Java and C++ do not feature extensive built-in reflection facilities. While there is a Java reflection API [12], it is limited to determining the signature of objects and methods, but not the manipulation of those components. Nevertheless, OpenJava, from the University of Tsukuba, is a class-based macro system that allows metaprogramming by manipulating an abstract syntax tree containing the logical structure of a Java program [13]. Likewise, a variety of tools and techniques have

been developed to provide metaprogramming in C++. For example, [14], [15] and [16] provide directions and libraries for implementing different types of metaprogramming for C++. However, one of the most popular and useful metaprogramming tools for C++ is Shigeru Chiba’s OpenC++ metaobject protocol [17]. Based on the Gregor Kiczales’ suggestion for implementing abstraction via metaprogramming [18], OpenC++ provides an abstract syntax tree to encapsulate C++ code and an API to manipulate it. Although, it does not use true runtime reflection, OpenC++ allows the manipulation of source code without a runtime cost. In fact, Michigan State University’s TRAP/C++ uses OpenC++ to implement facilities for selecting and incorporating new behavior at run time into C++ systems [19].
An alternative to metaprogramming for the implementation of implicit invocation in invariant maintenance is template programming. Also known as “programming at compile time”, templates allow the compiler to determine certain variable and object types at compile time. This can be used to divide architecture into layers containing different parts of an invariant. For example, GenVoca generators construct applications from reusable layers through the use of C++ templates [20]. The generators are used to implement structures called mixin-layers that combine commonly used parts of objects into a single structure for faster access without subtyping [21]. Mixin-layers are in-turn used within the DYNAMO project where the layers are more clearly defined to support invariant maintenance through implicit invocation [22], [23], [24]. To do this, C++ template classes, containing a template parameter that is the class’ supperclass, allow

dependent components to access features of independent components without resorting to the run-time cost of using a pointer.
1.3 Research Questions
This study attempts to answer the following questions about implicit invocation of invariant maintenance. Which technique, metaprogramming or template programming, provides best solution when applied to an invariant maintenance system as evaluated by quantitative and qualitative criteria including performance, ease-of-use, and simplicity of input?
1.4 Paper Map
The next section describes the approach used for the case study. It includes evaluation criteria and an account of why this research is unique. Section 3 contains the case study with the description of each approach. Section 4 contains the results of the case study including the analysis of generated assembly code. Section 5 discusses the results including their implications and suggestions for future work. Finally, section 6 concludes the paper with a reflection on the research study.
2 Approach
2.1 Evaluation Criteria
To evaluate and compare metaprogramming with template programming for the implementation of implicit invocation of invariant maintenance, solutions to the same problem were implemented using different techniques and approaches. Afterwards, each solution was analyzed based on predefined quantitative and qualitative criteria.

Quantitative criteria included time measurements of runtime and compile time, count of generated lines of assembly code, count of lines of C++ code required to implement the method in the main() method, and the size of the generated executable in bytes. Qualitative criteria is made up of personal evaluations of metaprogramming and template programming. Those include how easy each technique was to install and use and how well each technique fits the programmer’s mindset about the problem. While subjective, qualitative conclusions are based on real life experience described in the case study.
2.2 Metaprogramming
Unlike Lisp or Smalltalk, C++ does not support a built-in metaprogramming system aside from template metaprogramming. Therefore, OpenC++, a third party technology, was used to implement metaprogramming solutions. OpenC++ is an independent C++ compiler extension that allows detection of assignment within C++ code in order to generate and insert new code for the implementation of invariant maintenance. First, it translates an input C++ program into an abstract syntax tree containing all program structures, objects, and variables. Afterwards, the tree is manipulated based on a C++ metaprogram written using the OpenC++ API. Using its own compiler, OpenC++ transforms the original C++ program into a new C++ program that includes user manipulations described in the OpenC++ metafile.
For invariant maintenance, all approaches have OpenC++ detect the change in the independent variable and call a function that changes the dependent variable. Furthermore, implicit invocation between the components is generated by OpenC++ based on the specific metaprogram input to the compiler. The only parts requiring user

assistance are the code in the main() method that creates instances of classes and connects them if necessary and the declaration of a class required for OpenC++ manipulation.
Figure 1 OpenC++ Development Process

Preparing to load PDF file. please wait...

0 of 0
Comparison of Metaprogramming and Template Programming