Introduction

In this document I describe details of the synthesis process results of which are presented in Class-type Enumeration. I recommend that you read the introductory discussion from the aforementioned article if you are not familiar with the problem.

The Method

Traditionally, people rely on their intuition and experience when it comes to designing new things. When engineers are asked how they design, the answer is usually, "We don't know, we just do". With such an approach, however, we can't be sure we've got the best solution for the effort. Also mere engineering doesn't scale once we start undertaking more ambitious projects with large sets of conflicting criteria to meet.

While trying to resolve numerous problems of C++ enumerations, I realized that I need a more formal method of solution seeking which will allow me to (a) handle the complexity of multiple objectives (b) formally show that I've got optimal solution(s). To achieve this I used the process of optimal synthesis which is based on the theory of multi-criteria optimization.

The method can be described as a number of steps:

  1. Alternative Set Synthesis. Usually it is done with the help of structural-parametric model. Structural-parametric model is a description of the object or process that allows us to get a complete set of possible alternatives by varying structure and parameters in predefined limits.
  2. Forming Criteria System. Criteria system is a set of formally defined properties that we would like our object or process to have. For any two alternatives from the alternative set, each criterion in the system determines whether one alternative is better than the other according to this criterion.
  3. Multi-criteria Optimization. The process of optimization consists of evaluating criteria and discarding the inferior alternatives. The result of this process is the set of non-improvable, according to the criteria system, alternatives which, in general, contains less elements than the initial alternative set.
  4. Higher-level Considerations or Compromise-based Selection. If the optimized alternative set contains more than one element (and this is usually the case), the higher-level considerations or compromise is used to perform further selection. Sometimes all alternatives from the optimized set has to be implemented.
  5. Traditional analysis. At this stage the results of the synthesis are analyzed using traditional methods such as in an experiment or series of tests. If the result is not satisfactory, the process is repeated with all the corrections found in this stage accounted for.

Application

The first step is to create structural-parametric model. Based on the desired properties of the construct, I came up with the following scheme. Each alternative is described by the vector

a = (t, e, p, s, i, c)

where t, e, p, s, i, c are from the corresponding sets

T = {class(0), enum(1), int(2)}

E = {class(0), enum(1), int(2)}

P = {class(0), name(1)}

S = {class(0), enum(1), int(2)}

I = {none(0), explicit(1), implicit(2)}

C = {none(0), explicit(1), implicit(2)}

t is an enumeration type. For example when t = class the enumeration type is a class-type. e is an enumerator type. p is an enumerator's qualifier name. p = class means that t = class and enumerators are qualified with this name (e.g. Color::red). p = name means that enumerators are qualified with a name which is achieved via placing enumerators inside a namespace or a struct. s is a type of switch labels. The model allows separate constants of type s to be defined for switch support. If e and s have the same value, then enumerators are used for switch labels. i determines the type of integral promotion/conversion supported by t. Similarly c determines the type of conversion to char const*. Jokingly, you can view this model as the following pseudo-C++ declaration.

struct t == class ? p : null
{
  t Color
  {
    e red, green, blue;

    if (e != s)
    {
      s red_l, green_l, blue_l;
    }

    conversion (i, int);
    conversion (c, char const*);
  };
};

Where conversion expands to a conversion operator, conversion function or nothing depending on its arguments. Note also that this model contains a good number of impossible alternatives that we will have to take care of later.

If you read until here you must be wondering how I came up with this model. First of all, this is the third-generation model in the sense that I performed optimal synthesis three times and the results from the first two were used to shape the model. Another source of ideas for the model is criteria system. We can view each criterion as a function from the space of alternatives to the space of positive numbers plus 0. 0 means that the alternative does not meet the criterion. Other numbers indicate how well the alternative meets the criterion (the higher the number the better). Now, if we can build a reciprocal function of our criterion function and collect all the values of this function for all the arguments except 0, we will get a complete set of alternatives which can possibly meet the criterion. Performing this operation for each criterion in criteria system we can get an idea about what the complete alternative set should look like.

The next step is to define the criteria system.

  1. Encapsulation of enumerators. The criterion function is defined as follows:

    C(a) :
         t == 0 -> 2
         p == 1 -> 1
         0
    

    By giving p = 1 case value 1 instead of 2 I accounted for the fact that encapsulation with the enumeration type is preferred to the encapsulation with some other name.

  2. Default or enforced initialization. The only way to get default or enforced initialization in C++ is by using class constructor. Thus, the criterion function looks like this:

    C(a) :
         t == 0 -> 2
         0
    
  3. Labels. The ability to retrieve labels for enumerators depends on the conversion to char const* in our model. Thus, the criterion function is defined as

    C(a) :
         c
    
  4. Banning meaningless usage. In order to make meaningless operations on enumeration instances illegal, the enumeration type should not have implicit conversion to integral or pointer type, plus enumerator's type should not be of integral type:

    C(a) :
         i != 2 && c != 2 && e == 0  -> 2
         0
    
  5. Usable with switch control statement. This criterion can be divided into two sub-criteria: the enumeration type should be convertible to integral type and switch label type should be of integral type. Also we prefer enumerator and switch label to be the same thing (s == e).

    C(a) :
         i == 2 && s != 0 && s == e  -> 4
         i == 2 && s != 0            -> 3
         i == 1 && s != 0 && s == e  -> 3
         i == 1 && s != 0            -> 2
         0
    

As you may have noticed none of our criteria express preference to enum over int or vice versa. Based on this observation we can combine them into one parameter which will make the result analysis more manageable:

T = {class(0), integral(1)}

E = {class(0), integral(1)}

P = {class(0), name(1)}

S = {class(0), integral(1)}

I = {none(0), explicit(1), implicit(2)}

C = {none(0), explicit(1), implicit(2)}

Having structural-parametric model and criteria system ready we can proceed to optimal synthesis. I wrote a small synthesis program that generates the alternative set based on the model, performs optimization of this set and dumps the result:

$ ./synth
132 alternatives before optimization
(0, 0, 0, 1, 1, 1)  (2, 2, 1, 2, 2)
(0, 1, 0, 1, 2, 2)  (2, 2, 2, 0, 4)

As you can see the optimized set contains two alternatives. I invite you to further discuss the meaning of the results in Class-type Enumeration.


Copyright © 2003, 2004 Boris Kolpackov. See license for conditions.

Last update on Jan 26 2004