Home

Awesome

CL-PARAMETRIC-TYPES

Build Status

Summary

CL-PARAMETRIC-TYPES adds C++-style template classes, structs and functions to Common Lisp.

Supported systems

CL-PARAMETRIC-TYPES is currently tested on SBCL, ABCL, CCL, CLISP and CMUCL.

It is quite portable, as it only needs the function CLASS-SLOTS, for example from CLOSER-MOP and the function TYPEXPAND, for example from INTROSPECT-ENVIRONMENT so porting it to other Common Lisp implementations should be relatively straightforward.

Installation and loading

Download CL-PARAMETRIC-TYPES into your Quicklisp local-projects folder. Open a shell and run the commands:

$ cd ~/quicklisp/local-projects
$ git clone git://github.com/cosmos72/cl-parametric-types.git

then load a REPL and run:

CL-USER> (ql:quickload "cl-parametric-types")
CL-USER> (use-package :cl-parametric-types)
 

If all goes well, it will load CL-PARAMETRIC-TYPES and its dependencies.

A somewhat annoying issue is that CL-PARAMETRIC-TYPES must redefine the standard macro DEFSTRUCT to work around a technical limitation imposed by the standard (for the curious, :INCLUDE disallows derived types, so template superclasses would be forbidden). The effect is that (use-package :cl-parametric-types) signals a symbol conflict warning: you will need to either take the new symbol or resolve the conflict manually.

In real code, one would write instead:

(defpackage #:my-package
  (:use #:cl #:cl-parametric-types)
  (:shadowing-import-from #:cl-parametric-types #:defstruct)
  (:export #:my-exported-symbols...))

Basic usage

CL-PARAMETRIC-TYPES exports the following macros:

Invoking template functions

When a function is declared TEMPLATE, a macro is actually created with its name, to instantiate the appropriate function and to dispatch at compile time.

For example,

    (template (&optional (<t1> t) (<t2> t))
      (defstruct pair ()
        ((first  :type <t1>)
         (second :type <t2>))))

defines the parametric type PAIR, and also defines the symbols MAKE-PAIR, COPY-PAIR, PAIR-P, PAIR-FIRST and PAIR-SECOND as macros that, before all the usual parameters, expect an additional one - the list actual template arguments you want to use:

    ;; i.e. instead of (MAKE-PAIR :FIRST 1 :SECOND 2) you must also specify
    ;; the concrete types to instantiate PAIR and MAKE-PAIR:
    ;;
    (make-pair (bit fixnum) :first 1 :second 2)
    ; instantiating template-type (PAIR BIT FIXNUM) as <PAIR.BIT.FIXNUM>
    #S(<PAIR.BIT.FIXNUM> :FIRST 1 :SECOND 2)
    
    (defvar *pair* *) ;; store last result into *pair*
    *PAIR*

    (pair-first (bit fixnum) *pair*)
    1

Also, (SETF PAIR-FIRST) and (SETF PAIR-SECOND) work as expected:

    (setf (pair-first (bit fixnum) *pair*) 0)
    0

    *pair*
    #S(<PAIR.BIT.FIXNUM> :FIRST 0 :SECOND 2)
    

Partial template specialization

TEMPLATE also supports partial template specialization. For example, to specialize the function LESS defined above, adding a specialization that compares instances of PAIR, one can write:

    (template (<t1> <t2>)
      (:specialized-for ((pair <t1> <t2>))
      (defun less (a b)
        (declare (type (pair <t1> <t2>) a b))
        (let ((a1 (pair-first (<t1> <t2>)) a)
              (b1 (pair-first (<t1> <t2>)) b))
          (cond
            ((less (<t1>) a1 b1) t)
            ((less (<t1>) b1 a1) nil)
            (t
             (less (<t2>) (pair-second (<t1> <t2>) a)
                          (pair-second (<t1> <t2>) b)))))))

Note that the symbol LESS does not have any special meaning inside the function LESS, so you must always specify the template parameters, even for recursive calls, in order to use the macro LESS.

The equivalent C++ code would be:

    template<class T1, class T2>
    bool less<pair<T1, T2> >(pair<T1,T2> a, pair<T1,T2> b) 
    {
        T1 a1 = a.first, b1 = b.second;
        if (less<T1>(a1, b1))
            return true;
        if (less<T1>(b1, a1))
            return false;
        return less<T2>(a.second, b.second);
    };

Note that, just like in C++, partially specialized functions and types are used when their specialization pattern matches the actual types, ignoring any subclass/superclass relationship.

In other words, the function LESS specialized for (PAIR <T1> <T2>) will not be considered when trying to instantiate, compile and invoke (LESS (TRIPLE <T1> <T2> <T3>) ...) even if TRIPLE is a subclass of PAIR.

Predefined template functions and types

The package CL-PARAMETRIC-TYPES.STL aims at reimplementing most of C++ STL functions and types, translating them to idiomatic Common Lisp. See stl/README.md for details.

Appendix: design philosophy

Short version: maximum performance, zero runtime overhead, compile-time instantiation, compile-time overload resolution (i.e. dispatching).

Long version: to be written...

Appendix: why bringing C++-style templates to Common Lisp?

Short answer: because we can.

Long answer:

Several comparisons exists between C++ templates and Common Lisp macros, including at least:

Any serious comparison between C++ templates and CL macros will agree on some basic facts:

So, are C++-style templates really a missing feature in Common Lisp?

A Lisp advocate answer could be "no, because Common Lisp macros are equivalent to (and more elegant than) C++ templates"

Yet there are other points of view:

CL-PARAMETRIC-TYPES exists for all these reasons, and for an additional one:

scratch the author's personal itch about combining two of his favorite tools - Common Lisp and C++ templates.

Legal

CL-PARAMETRIC-TYPES is released under the terms of the [Lisp Lesser General Public License] (http://opensource.franz.com/preamble.html), known as the LLGPL.