April 21, 2003
Metacode Progress

On April 4th, I presented the Metacode extension at the ACCU Spring Conference in Oxford. The room was packed (which was very encouraging) and many attendees appeared excited about the ideas being presented (which was even more encouraging). A lot of ideas were raised at that presentation.

Originally, I planned to bring up the Metacode extension at the October 2003 meeting of the C++ standardization committee. However, the ACCU presentation generated enough “buzz” to justify repeating the presentation for the standardization committee at the April meeting (which conventiently happened in Oxford April 6-11). That presentation too was well received.

Here is a short summary of the extension...

Overview

There are four main components to the Metacode extension:

  • Compile-time executable functions (called metacode functions)
  • A library of predefined metacode functions and types
  • Constructs to inject new code
  • A metacode block construct to execute metacode functions in declarative contexts

Metacode Functions

A new keyword metacode introduces functions that are to be executed at compile time.
Such functions can contain any C++98 construct, plus the injection constructs explained later on.
Since they are evaluated at compile time, they can appear in constant-expressions.
Here is a simple example (which works with my current implementation, btw):

template<typename T> metacode
T power(T b, unsigned n) {
        T r = 1;
        for (int k = 0; k<n; ++k) r *= b;
        return r;
}

float a[power(2, 3)];

One of the more surprising rules for metacode function calls is that no implicit conversions are performed (by default) on their arguments.
That ensures that the original properties of the arguments (such as lvalueness) are retained and available for inspection.

Another interesting feature of metacode functions is that any kind of function can be a metacode function.
For example, you could imagine a metacode constructor.

The Standard Metacode Library

I envision a standard metacode library that implements a bunch of “magical” metacode functions and types.
This library lives in a reserved namespace stdmeta.
So far, I have implemented is_lvalue, is_accessible (which returns true if the given string literal contains a name that exists and it accessible), and error (which issues an error diagnostic on the first active metacode function call).
However, I plan to add quite a few facilities, including types to manipulate string literals, identifiers, and C++ types.

Code Injection Primitives

I'm planning two code-injection primitives: One to inject declaration in a surrounding class or namespace scope, and one to inject an expression as a substitute for a metacode call.
I've started work on the latter. It involves the token sequence return->. The trailing -> is probably not needed, but it helps reminding the reader that what is being returned is not a plain value.
Here is an example that builds on the previously developed metacode function template.

metacode 
double mypow(double b, int n) {
  using ::stdmeta::is_constant;
  if (is_constant(b) &&
      is_constant(n) &&
      n >= 0) {
    return power<>(b, (unsigned)n);
  } else {
    return-> ::std::pow(b, n);
  }
}

I'm hoping to get this example working in the relatively near future.

The injection of declarations uses the metacode-> token sequence.
The following example illustrates some additional concepts.

metacode define_fields(array<type>  types) {
  for (int k = 0; k<types.length(); ++k) {
    type FieldT = types[k];
    id FieldName = id("field" +
                      string_literal(k));
    metacode-> {
      FieldT FieldName;  // Metacode identifiers
    }                    // translated according
  }                      // to their types.
}

The types type, array<type>, and id are to be imported from stdmeta.
The tricky bit is that FieldT in the injected block finds a metacode variable of type stdmeta::type and is therefore transformed into a type token.
Similarly, FieldName finds a metacode variable of type stdmeta::id and is therefore transformed into an identifier token.
There is some more to this, but it is the basic mechanism that parameterizes injected code.
I expect this will take a while to implement.

Metacode Blocks

Metacode blocks were actually the original idea that lead to all this.
They are a construct that allows metacode statements to be specified in a declarative context.
Here is a small example:

template<typename T> struct S {
  metacode {  // Start metacode block
    if (stdmeta::typevar<T>().is_reference()) {
      stdmeta::error("No reference, please.");
    }
  }
  // ...
};

I'm not quite sure yet how hard this will be to implement.

Posted by Daveed at 09:24 AM