読者です 読者をやめる 読者になる 読者になる

C++テンプレートとマクロでLisp

C++テンプレートでLispに触発されて作成。


lisp.h

#pragma once

// nil ======================================
struct nil {
    typedef nil eval;
};
#define nil nil::eval

// t ========================================
struct T {
    typedef T eval;
};
#define T T::eval

// cons ======================================
template < class A, class B>
class cons {
    typedef typename A::eval left;
    typedef typename B::eval right;
public:
    typedef cons<A, B> eval;
// friend:
    template <class> friend struct car;
    template <class> friend struct cdr;
};
#define cons(A, B) cons<A, B>::eval

// car ======================================
template <class A>
struct car {
    typedef typename A::left eval;
};
#define car(A) car<A>::eval

// cdr ======================================
template <class A>
struct cdr {
    typedef typename A::right eval;
};
#define cdr(A) cdr<A>::eval

// atom =====================================
template <class>
struct atom {
    typedef T eval;
};

template <class A, class B>
struct atom< cons<A, B> > {
    typedef nil eval;
};
#define atom(A) atom<A>::eval

// eq =======================================
template <class, class>
struct eq {
    typedef nil eval;
};

template <class A>
struct eq<A, A> {
    typedef T eval;
};
#define eq(A, B) eq<A, B>::eval

// lisp =====================================
#define lisp(expr) expr()

main.cpp

#include <iostream>
#include "lisp.h"

// valueとか作ってみる
template <int val>
struct value {
    typedef typename value<val> eval;
};
#define value(val) value<val>::eval

template <class Type>
void disp_typeinfo(Type t) {
    std::cout << typeid(t).name() << std::endl;
}

int main() {
    disp_typeinfo(lisp(atom(value(10))));
    disp_typeinfo(lisp(atom(cons(value(10), value(20)))));
    disp_typeinfo(lisp(atom(cdr(cons(nil, cons(T, T))))));

    disp_typeinfo(lisp(eq(T, T)));
    disp_typeinfo(lisp(eq(nil, nil)));
    disp_typeinfo(lisp(eq(T, nil)));
    disp_typeinfo(lisp(eq(nil, T)));
    disp_typeinfo(lisp(eq(cons(T, nil), cons(T, nil))));
    disp_typeinfo(lisp(eq(T, car(cons(T, T)))));

    disp_typeinfo(lisp(cons(T, cdr(cons(T, nil)))));
    disp_typeinfo(lisp(cons(T, cdr(cons(T, value(10))))));
}

Visual C++ 2005での実行結果はこんな感じ。

struct T
struct nil
struct nil
struct T
struct T
struct nil
struct nil
struct T
struct T
class cons
class cons >

そしたら当然というかなんというか、次の日のよりLispっぽく見せるにほとんど同じものが・・・
まぁ、改良点(改悪点???)はそれだけじゃないですけどね。おびなたさんのプログラムとの違いは大体以下のとおりです。

  1. consのpublicなtypedefを減らして、代わりにフレンドクラス*1を使用
  2. atomのアルゴリズムを変更*2
  3. eqのアルゴリズムを変更*3
  4. lispマクロの作成*4

変更点は改良になってるのかどうか、Lispをほとんど知らない(おぃ)からよくわかんない。

*1:フレンド構造体?

*2:真偽判定を逆にしたことで、簡単にユーザ定義のアトムを作れる。例としてはmain.cppのvalue

*3:重複部分をまとめただけ

*4:サンプル用でしかなく、使いにくい気が・・・