Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

Mixin-Based Programming in C++

Source Code Accompanies This Article. Download It Now.


Configuration Generator

Obviously, the manual creation of configuration repositories is tedious and error prone:

  • First, there may be a large number of mixins in a library and the application programmer would have to be aware of them.

  • Second, not all possible configurations of mixin classes are semantically correct and the application programmer would have to know the correct ways to configure the mixins.

  • Third, given a larger number of mixin classes, the configuration repositories themselves can reach considerable sizes and the number of possible configurations usually grows exponentially (for example, we developed a matrix library with mixin classes that can be configured into almost 2000 different matrix types (see http://www.prakinf.tu-ilmenau.de/~czarn/gmcl/).

  • Finally, achieving abstract features requires preferring certain constellations of mixin classes over other constellations, and the application programmer would have to know them. Therefore, we would like let application programmers specify the desired properties of a configuration and have the appropriate configuration generated automatically from the specification.

Template metaprogramming (which is a set of programming techniques in C++; http://oonumerics.org/blitz/papers/) and generative programming (which is an analysis, design, and implementation approach) provide the necessary foundation for developing so-called "configuration generators," which solve the problems just outlined. Listing Eight contains such a configuration generator for various customer types. It is implemented as a template metafunction (that is, a class template conceptually representing a function to be executed at compile time) that accepts a specification of the desired customer type in a so-called "domain-specific language" (DSL) and returns the concrete customer type in its type member RET. The DSL in this example is kept relatively simple. Using enumeration constants, client programmers can specify whether a customer type should include information for phone contact or e-mail contact. Furthermore, they can specify the types of the various members of the mixin classes. A default value is provided for each parameter, so that even a default customer type can be ordered using an empty specification. Of course, the DSL needs to be clearly documented, so that clients know what parameters and parameter values are available.

Listing Eight

#include <iostream>
#include <string>
using namespace std;
#include "meta.h"
using namespace meta;
#ifdef _MSC_VER
  #pragma warning (disable:4786)
  #pragma warning (disable:4305)
  #define GeneratorRET RET
#else
#define GeneratorRET Generator::RET
#endif

struct NIL
{};
template <class T,class Next_ = NIL>
struct Param
{
  Param(const T& t_,const Next_& n_ = NIL()):t(t_),n(n_)
  {}  

  const T& t;
  Next_ n;
  typedef Next_ N;
};

// We will pass Generator to Customer rather than Config; the latter will be
// nested in Generator, and Customer has to retrieve it; this modification
// of Customer is necessary to avoid certain circularity problems.
template <class Generator_>
class Customer
{
  public:
    // Exporting config
    typedef typename Generator_::Config Config;
   // ...
   // Rest of Customer is the same as in Listing Seven.

// The remaining mixin classes and the parameter adapter
// are the same as in Listing Seven.

// Bitmask for describing customer options - part of the domain
// specific language (DSL) for describing customers
enum CustomerSpec
{
  BasicCustomer = 0, // values represent bits in a bitmask
  WithPhone = 1,
  WithEmail = 2
};

// Customer generator (the generator parameters represent rest of DSL)
template <
           int spec = BasicCustomer, // spec is a bitmask
           class Firstname = const char*,
           class Lastname = const char*,
           class PhoneNo = const char*,
           class EmailAdd = const char*
         >
struct CUSTOMER_GENERATOR
{
  // Provide a shorthand for CUSTOMER_GENERATOR ...
  typedef CUSTOMER_GENERATOR < spec,
                               Firstname,
                               Lastname,
                               PhoneNo,
                               EmailAdd
                             > Generator;
  // Parse DSL
  // Assume there is always a basic customer ...
  enum
  { hasPhone = spec & WithPhone,
    hasEmail = spec & WithEmail
  };

  // Assemble components
  typedef Customer<Generator> Part1;

  typedef typename
    IF < hasPhone,
         PhoneContact<Part1>,
         Part1
       >::RET Part2;

  typedef typename
    IF < hasEmail,
         EmailContact<Part2>,
         Part2
       >::RET Part3;
  // Result of the generator template metafunction:
  typedef ParameterAdapter<Part3> RET;

  // Compute config
  struct BasicCustomerConfig
  { // Provide some metainformation
    enum
    { specification = spec 
    };
    typedef Firstname FirstnameType;
    typedef Lastname LastnameType;
    typedef GeneratorRET RET;
  };
 
  struct CustomerWithPhoneConfig: BasicCustomerConfig
  {
    typedef PhoneNo PhoneNoType;
  };

  struct CustomerWithEmailConfig: BasicCustomerConfig
  {
    typedef EmailAdd EmailAddressType;
  };

  struct CustomerWithPhoneAndEmailConfig
    : CustomerWithPhoneConfig,CustomerWithEmailConfig
  {};

  typedef typename
    SWITCH < spec,
      CASE<BasicCustomer,BasicCustomerConfig,
      CASE<WithPhone,CustomerWithPhoneConfig,
      CASE<WithEmail,CustomerWithEmailConfig,
      CASE<WithPhone+WithEmail,CustomerWithPhoneAndEmailConfig
    > > > > >::RET Config;
};
int main()
{
  CUSTOMER_GENERATOR<>::RET c1("Teddy","Bear");
  c1.print(); cout << endl;
  CUSTOMER_GENERATOR<WithPhone>::RET c2("Rick","Racoon","050-998877");
  c2.print(); cout << endl;
  CUSTOMER_GENERATOR<WithEmail>::RET c3("Dick","Deer","[email protected]");
  c3.print(); cout << endl;
  CUSTOMER_GENERATOR<WithPhone + WithEmail>::RET
  c4("Eddy","Eagle","049-554433","[email protected]");
  c4.print(); cout << endl;
  return 0;
}

Implementing the generator starts with parsing the DSL. There is no "buildability" check because we assume that no wrong specification is requested by the client programmer (or the client program using the generator). The next step is to assemble the components (that is, the mixin classes) according to the specified features. Metacontrol structures, such as IF and SWITCH (they are part of the include file meta.h, which is not shown here), are used for assembling the components. The final result is made available through the typedef-name RET, which is a convention commonly used in template metaprogramming.

Finally, the configuration repository is computed. This example demonstrates a special technique for assembling configuration repositories. Normally, you could put all the information into a single configuration repository. In most cases, this works perfectly because the involved components (mixin classes) will retrieve only the information they need for themselves from the configuration repository. However, in order to preserve the different structures of the configuration repositories from Listing Seven, we decided to compose these different configuration repository types using inheritance and to select the one that is best suited for the generated component using a SWITCH. To our knowledge, this technique also has not been published elsewhere before. Please note that we use a kind of static overriding technique when redefining selected names in the derived configuration repositories — FinalParamType.

Conclusion

The solution to the constructor problem in mixin-based programming with C++ we've presented here effectively helps to avoid dependencies between components and special assembly orderings imposed by the number of arguments required by mixin class constructors. The solution also allows client programmers to create instances of the composed types in a natural way. Furthermore, it scales very well because adding new mixin classes requires minimal adaptations (that is, only the generator and the DSL need to be extended). Finally, the solution is also efficient. The only overhead results from the necessary creation of instances of heterogeneous value lists.

However, this cost seems to be affordable, especially when compared to the costs introduced by the other approaches. Thanks to configuration generators based on template metaprogramming, the complexity of the solution is completely hidden from the client programmers. Although this complexity has to be mastered by the generator programmer, it needs to be mastered only once for a given set of mixins. This effort then pays back every time generated components are requested by the client programmers. The sources of all program examples are available as a zipped archive at http://home.t-online.de/home/Ulrich.Eisenecker/cpmbp.zip. They were tested with gcc 2.95.2 and Microsoft Visual C++ 6.0.


Ulrich is a professor at the University of Applied Sciences Kaiserslautern at Zweibrücken, Frank a graduate student at the university, and Krzysztof a researcher at DaimlerChrysler Research and Technology. They can be contacted at [email protected], [email protected] student-zw.fh-kl.de, and [email protected], respectively.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.