メタプログラミングツールとしてのIronPython

リフレクションを使えばメタプログラミングが出来ますが、ちょっと面倒です。たとえば、以前やったネタですが、
http://d.hatena.ne.jp/akiramei/20050612/p1
インタフェースの委譲を自動化するだけなのに結構なコード量になっています。リフレクションをやめてソースコードを生成してコンパイルした方が楽だったかもしれませんね。(^^; さて、ソースコードを生成するのなら、IronPythonを使うとevalも使えるのでもっと楽チンです。

using System;
using System.Collections.Generic;
using System.Text;
using IronPython.Hosting;

namespace IronTest
{
    public interface ICalc
    {
        double Add(double x, double y);
        double Sub(double x, double y);
    }

    public interface ICalcEx
    {
        double Mul(double x, double y);
        double Div(double x, double y);
    }       

    public class Keisan
    {
        public double Tasu(double x, double y)
        {
            return x + y;
        }

        public double Hiku(double x, double y)
        {
            return x - y;
        }

        public double Kakeru(double x, double y)
        {
            return x * y;
        }

        public double Waru(double x, double y)
        {
            return x / y;
        }
    }

    class Program
    {
        static PythonEngine engine = new PythonEngine(new EngineOptions());

        /// <summary>
        /// 既存クラスを使ってインタフェースを実装する
        /// </summary>
        /// <param name="ns">名前空間</param>
        /// <param name="iname">インタフェース名</param>
        /// <param name="cname">実装クラス</param>
        /// <param name="mapping">委譲先のマッピング</param>
        /// <returns>インタフェースを実装したクラス</returns>
        static object Impliments(string ns, List<string> iname, string cname, 
            Dictionary<string, string> mapping)
        {
            string newclass = String.Join("", iname.ToArray()) + cname;
            string baseclass = String.Join(",", iname.ToArray()) + "," + cname;

            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("from {0} import *\n", ns);
            sb.AppendFormat("class {0}({1}):\n", newclass, baseclass);
            sb.Append(" def __init__(self):\n");
            foreach (string key in mapping.Keys)
                sb.AppendFormat("  self.{0} = super({1}, self).{2}\n", 
                    key, newclass, mapping[key]);
            sb.AppendFormat("_ = {0}()\n", newclass);

            System.Diagnostics.Debug.Write(sb.ToString());
            engine.Execute(sb.ToString());

            return engine.Evaluate("_");
        }

        static void Main(string args)
        {
            engine.LoadAssembly(System.Reflection.Assembly.GetEntryAssembly());
            engine.Import("IronTest");

            // メソッドの委譲先
            Dictionary<string, string> mapping = new Dictionary<string, string>();
            mapping["Add"] = "Tasu";
            mapping["Sub"] = "Hiku";
            mapping["Mul"] = "Kakeru";
            mapping["Div"] = "Waru";
            // インタフェースの実装
            Object o = Impliments("IronTest", new List<string>(
                new string { "ICalc", "ICalcEx" }), "Keisan", mapping);
            ICalc ic = o as ICalc;
            ICalcEx ix = o as ICalcEx;
            Console.WriteLine(ic.Add(3, 5));
            Console.WriteLine(ic.Sub(3, 5));
            Console.WriteLine(ix.Mul(3, 5));
            Console.WriteLine(ix.Div(3, 5));
        }
    }
}

/* 生成したPythonコード
from IronTest import *
class ICalcICalcExKeisan(ICalc,ICalcEx,Keisan):
 def __init__(self):
  self.Add = super(ICalcICalcExKeisan, self).Tasu
  self.Sub = super(ICalcICalcExKeisan, self).Hiku
  self.Mul = super(ICalcICalcExKeisan, self).Kakeru
  self.Div = super(ICalcICalcExKeisan, self).Waru
_ = ICalcICalcExKeisan()
 */

/* 結果
 8
 -2
 15
 0.6
 */

Pythonのコードは簡潔なので生成するのも楽ですね。ただ、Pythonメタプログラミングは不慣れなので、もっと良い方法があるかもしれません。(^^;