Lpp 1.21.2 is now available for download.
Lpp 1.21 is now available for download.
Lpp 1.19 is now available for download. Please see the copyright under which this software is covered.
Lpp documentation is available in HTML and PDF.
Lpp (Lisp Plus Plus), is a library of Lisp like functions and macros usable in C++ programs. The philosophy behind Lpp is to provide as close as possible the semantics and style of Lisp rather than try to force it to fit a static style of programming. Lpp tries to emulate Common Lisp as much as possible in this regard. By doing things this way part of the true power and flexibility of Lisp can coexist and mix with the static typing features of C++ even within functions and objects. The hope is that Lpp will be useful for the following
One of Lisp's advertised benefits is that of dynamically typed objects. Standard C++ does not offer this capability. Instead, the programmer is expected to created virtual functions whose objects dynamically dispatch through the use of indirect pointers to v-tables and then a v-table has a pointer to the virtual function for that object. While the basic idea of virtual functions is good, relying only on it for real world complex problems presents difficulties. One such problem is that it is impossible to write true generic code using virtual functions since all possible types must be accounted for. The default virtual function of a base class can only serve the declared sub-classes that do not define their own virtual functions. Contrast this with Lisp functions that usually do not have to account for the types of the objects that it operates on. As a very simple example, Lisp can compute the length of a list irrespective of the types of the objects in the list. Furthermore a provider could supply an object dynamically to a consumer of such a list that is not required to be seen by the compiler of the program operating on the list.
Lpp strives to provide the full power of Lisp in terms of dynamic typing. The way that Lpp achieves this is that all Lpp objects contain a type header that is a pointer to a type meta-object. In some sense this is similar to the v-table concept mentioned above for virtual function tables in that type meta-objects can and do contain type dispatcher functions that are similar to virtual functions. But the type meta-objects are automatic in Lpp as well as much of their standard behavior. And type meta-objects are first class Lpp objects so that they can be manipulated dynamically as ordinary objects can. For example a dispatcher function can be dynamically added to a type meta-object and then operate on subsequent ordinary objects of that type. Furthermore the type meta-objects maintain a run time type lattice. For example at run time a program can query whether an object is a number or an integer (integers being a subset of all numbers).
All Lpp objects as in Lisp have the same base type. In Lisp the
base type is type
t. In Lpp the base type is
intentionally left unknown and abstracted with a type definition
let. This was done so that the base type can be
experimented with while preserving Lpp syntax. Type
serves as the declaration type for all such Lpp objects when
referring to the Lpp object as a generic object. As in Lisp Lpp
objects are passed around as pointers with pointer abstraction
in Lpp function arguments and Lpp slots. These objects can be
mixed freely with other C++ classes and class members and can be
used with other C++ libraries that do not use the same class
names that Lpp uses.
Lpp provides a
the macro for descending into
specific Lpp objects. This is similar to the
special form in Common Lisp. Lpp automatically does a dynamic
type check on the object wrapped in a
which assures that specific object member references will always
be correct. In a debugged program compilation unit this type
checking can be turned off for maximum compilation
Lpp also provides other useful Lisp objects such as Cons cells, lists, symbols, strings, characters and numbers. As much as possible Lpp tries to use the Common Lisp function names and semantics for the operations on Lisp like objects.
Lpp chose address 0 as
nil. This is so that it is
easy to check for the end of lists or
in predicate arguments in C++ functions, operators and
nil and non-
is consistent with C++ predicate semantics on 0 and non-0. But
nil is 0, it is exactly equivalent to
nil. It is as if the symbol object
nil was an object at address 0.
Symbols are introduced into C++ programs easily with the
S() macro. For example,
S(!\@#$%^&*) are all legitimate symbols in Lpp.
Symbols after being interned are just as efficient but far more
enums. The default for symbols in
Lpp is case sensitive, so that
Foo are two distinct symbols.
Primitive C objects can easily be converted to their analog Lpp
objects using the
L constructor. For example
L("abc") will produce an Lpp String object and
L(cdr) will produce an Lpp first class function
Conversions back are also easy for example
iL(L(22)) where iL conversion is back to C++ integers.
The programmer can use dynamic Lpp objects when needed and use
simple C++ objects when absolute efficiency is needed.
Efficiency is an elusive property though. For example Lpp
strings seem less efficient on the surface, but since they have
a length they provide for faster string comparisons than
ordinary 0 terminated C++ strings. The names of such things as
L (the conversion macro),
S (the symbol macro) can be redefined per
compilation unit by the programmer without affecting the
operation of Lpp.
Lpp provides the basic Common Lisp I/O functions like
pprint ... etc, but also
provides that any Lpp object can appear in stream operators. So
obj is the Lpp symbol object
cout << "obj = " << obj would print as
obj = car. All Lpp objects have C++ stream print
methods that inherit from the basic
princ method of
an object. All new Lpp objects defined automatically get
default dispatcher functions defined for
prin1 methods can be set dynamically by the
programmer for Lpp type meta-objects.
Lpp provides mathematically correct rational numbers, ie. ratios whose numerator and denominator and integers are in the range minus to plus infinity. Lpp numbers automatically expand or shrink to any size when operated on by Lpp math functions. Overflow or underflow are impossible.
Lpp provides two debugger functions
pdc for examining Lpp objects in a debugger.
princ. So while in a debugger the user can
type, for example,
p pdc(list1) where
list1 is some variable in a program that contains
an Lpp list, the list and all of it's subcomponents will be
printed exactly as you would expect in a lisp interpreter. In
addition the function
ppdb is provided for pretty
printing such objects in a debugger.
Please direct comments on the Lpp software to William Paul Vrotney. For feedback on these web pages, please use the address in the footer below.