CSC/ECE 517 Spring 2013/ch1b 1k hf

From PG_Wiki
Jump to: navigation, search


Contents

A Basic Definition of Metaprogramming

The most basic definition of metaprogramming is: writing code that writes or manipulates code. [1] [2]

A metaprogram can either manipulate itself, or it can manipulate some other program.

Below are three important terms associated with a metaprogramming[2]:
  • metalanguage - the language in which a metaprogram is written
  • object language - the language of the program that is being manipulated by a metaprogram
  • reflection - the ability of a programming language to be its own metalanguage (also known as reflexivity)


Uses of Metaprogramming

Metaprogramming can allow programmers to write code that is more concise and more flexible. It can be more concise because a program that can write code is able to generate more code than it is initially given. It can be more flexible, because it can allow the program to change the way it behaves without having to recompile.

One of the best examples of metaprogramming is that of the compiler[3]. A compiler uses code written in one language in order to generate code that can be executed by a computer.
Metaprogramming is also used for building frameworks such as Ruby on Rails. In this case, the use of convention over configuration allows developers to write more concise and consistent code. Following specific conventions, developers write a relatively small amount of code. The Ruby on Rails scaffold uses the code the code written by the developer to generate code that is expansive, interconnected, and standardized.
One of the most powerful uses for metaprogramming, is the ability to change a program during runtime. In languages such as Ruby and Groovy, programmers can[4]:
  • Create a new class at runtime
  • Create new methods at runtime
  • Modify existing classes and/or methods at runtime
  • Respond to calls made on non-existent methods


Features in Ruby that Enable Metaprogramming

In dynamically typed languages, the majority of its type checking is performed at run-time, as opposed to compile time.[5] Because variables are not type-checked until they are used in program execution, it is possible to use types that are created during program execution.
Interpreted languages avoid explicit compilation[6]. Because Ruby is being interpreted as it is being executed, it is possible to create new classes and methods during program execution.
Most object-oriented programming languages adhere to the open/closed principle[7]. This means that the core components that are part of the language cannot be modified. In Ruby, the classes are open, meaning that all of the classes and methods be changed.


Examples of Metaprogramming in Ruby

Creating a New Class at Runtime

Below is an example of creating a new class at runtime:[8]

  3.times do
     class MyClass
        puts "MyClass defined"
     end
  end

In example shown above, the times method is invoked on the 3 object. Inside of the times method call, the class MyClass is defined. MyClass is actually defined three times, once for each of the three iterations. Even though the class is defined three times, it has the same effect as only defining it once. The important thing to note here, is that a new class is defined at runtime, inside of a method call.


Modifying a Class at Runtime

Below is an example of modifying a class, and and example of creating a new method at runtime:[8]

  3.times do
     class MyClass
        def one
           puts "one"
     end
  end

In the example shown above, the class MyClass is modified again to include a new method. This method, the one method is defined three times, just like the class is defined three times. Even though it has been defined three times, it has the same effect as only defining it once. The important thing to note here is that a new method is defined at runtime.


Creating a Method at Runtime

Below is an additional example of modifying a class at runtime:[8]

   class MyClass
     def two
        puts "two"
  end

In the example shown above, the classMyClass is reopened and the two method is added to it.


Below is an example of what happens when the code written above is executed:[8]

  
  mc = MyClass.new
  mc.one
  mc.two
  

The output of the code will be:

  one
  two


Modifying a Method at Runtime

Below is an additional example of modifying a method at runtime:[8]

   class MyClass
     def two
        puts "one two"
  end

In the example shown above, the two method is reopened and modified.


Below is an example of what happens when the modified code written above is executed:[8]

  
  mc = MyClass.new
  mc.one
  mc.two
  

The output of the code will be:

  one
  one two

Responding to a Non-Existent Method Call

Whenever a call to an undefined method is made on an object, Ruby provides an option to intercept the call. This is done by implementing the method method_missing within the class definition. Ruby passes, as parameters, the name of the method called and the arguments passed to it.

Define a module (or class) Roman. This class contains a method_missing method that intercepts calls to “class methods” that are undefined. It then tries to interpret the method name as a Roman numeral. For example,

 class Roman 
 DIGITS = {
   'I' => 1,
   'V' => 5,
   'X' => 10,
   'L' => 50,
   'C' => 100,
   'D' => 500,
   'M' => 1000,
 }
 def roman_to_integer(roman_string)
   last = nil
   
 roman_string.to_s.upcase.split(//).reverse.inject(0) do
 |memo, digit|
     if digit_value = DIGITS[digit]
       if last && last > digit_value
         memo -= digit_value
       else
         memo += digit_value
       end
 last = digit_value
     end
 memo
   end
 end
 def method_missing(method)        
   str = method.id2name 
   roman_to_integer(str)      
 end


Metaprogramming in Other Languages

The C preprocessor (CPP)[9]

First let's look at metaprogramming that involves textual macro languages. Textual macros are macros that directly affect the text of the programming language without knowing about or dealing with the meaning of the language.

The SWAP macro:
  #define SWAP(a, b, type) { 
      type __tmp_c; c = b; b = a; a = c; 
  }

This macro allows you to swap the two values of the given type. A macro is effective in this situation because:


 #define SWAP(a, b, type) { type __tmp_c; c = b; b = a; a = c; }
 int main()
 {
     int a = 3;
     int b = 5;
     printf("a is %d and b is %d\n", a, b);
     SWAP(a, b, int);
     printf("a is now %d and b is now %d\n", a, b);
     return 0;
 }

Meta Programming in Java[10]

The generics can be used in classes, interfaces, methods and constructors. Two new types:

A type variable is an unqualified identifier. Class and interface declarations can have type arguments (type variables)

Method and constructors definitions can have type arguments (type variables)

 public interface List<E> { 
   void add(E x);
   Iterator<E> iterator();
 }
 public interface Iterator<E> { 
   E next();
   boolean hasNext();
 }
 List<String> anExample;
 anExample.add(”sdfdfss”);
 anExample.add(new Object()); // compile time error
 String aTest = anExample.iterator().next();

References

  1. Video
  2. 2.0 2.1 Wikipedia Article on Metaprogramming
  3. Overview of Metaprogramming
  4. Coding Insights Blog
  5. Wikipedia Article on the Type System
  6. Wikipeida Article on Interpreted Language
  7. Wikipedia Article on the Open/Closed Principle
  8. 8.0 8.1 8.2 8.3 8.4 8.5 Dr. Gehrenger's notes on Metaprogramming
  9. The Art of Metaprogramming
  10. Metaprogramming in Java
Personal tools
Namespaces
Variants
Actions
Navigation
Toolbox