---
layout: ../Site.layout.js
---
# C++ and embeddable common lisp habitat example ~ base64 encoding
[Habitat being jeremy_list's HaikuOS secure scuttlebutt C++ gossip protocol client](
https://codeberg.org/jeremylist/habitat). Of course, I use ANSI CL and not C++, and openbsd or maybe debian and not haikuos. So let's see if we can just make the C++ bits into lisp functions in that magical [embeddable common lisp](
https://ecl.common-lisp.dev/) way.
Here we continue directly from [making the conventional BSD port of `ecl` embeddable common lisp `--with-cxx`](/fundamental/cxx-ecl-openbsd-port). I'm aware that at least the [gentoo linux distribution has a C++ ecl port](
https://packages.gentoo.org/packages/dev-lisp/ecl) (in gentoo, called an `ebuild`).
## Set up common lisp / eev / emacs
`#P"~/ecl-habitat-test/eat-base64-habitat.ecl.lisp"` was my working file. This is the adaptation of eev's `M-x eeit` generated infrastructure I have found myself using.
```
#|
• (setq inferior-lisp-program "ecl")
• (slime)
• (setq eepitch-buffer-name "*slime-repl ECL*")
(compile-file "~/ecl-habitat-test/eat-base64-habitat.ecl.lisp" :load t)
|#
```
`compile-file` in embeddable common lisp provides a very nice low-level-ffi, sffi into either C or C++ (whatever `ecl` had been built with (`--with-cxx` or not)).
NOTE all of ecl's `sffi` stuff is `compile-file`-only. So we have to extract the `ffi` of this article into a lisp file and call `compile-file` .. `:load t` as indicated above. The interpreter doesn't know how to compile C (it only interprets lisp).
## Taking C++ `base64::encode1` from habitat's source
I eventually came to terms with the fact that habitat used a nonportable HaikuOS C++ string library. Instead, I pulled out the base64 encoding source C++ namespace fragment. I promoted the enums and integer-like types to ints, though ecl's sffi directly supports all these common C/C++ types, but they are a bit extraneous to this minimal ecl C++ sffi example.
(Edit: Simple fix mentioned everywhere else right after publishing) Actually, from what I can tell, at least my extracted version of b64 encoding here doesn't work, but the C++ embeddable common lisp works which is our focus. [jeremy_list](
https://thunix.net/~jeremylist/) will take a look once this article is up and point out where I went wrong here.
```
(ffi:clines "
namespace base64 {
enum Variant {
STANDARD,
URL,
};
int
encode1(int byte, int variant)
{
if (byte <= 25) {
return byte + 'A';
} else if (byte <= 51) {
return byte - 26 + 'a';
} else if (byte <= 61) {
return byte - 52 + '0';
} else if (byte == 62) {
if (variant == STANDARD)
return '+';
else
return '-';
} else {
if (variant == STANDARD)
return '/';
else
return '_';
}
}
}
")
```
## A lisp defun whose body is that C++ function
`ffi:defentry` ([sffi docs](
https://ecl.common-lisp.dev/static/manual/Foreign-Function-Interface.html#SFFI-Reference)) attaches a C/C++ function as body to a common lisp function. This is just what the form is like.
```
(ffi:defentry c-encode1
(:int :int)
(:int |base64::encode1|))
```
`ffi:defentry`'s arguements are
1. name (so the lisp function will be named `c-encode1`)
1. C/C++ argument types (I just promoted them to ints - if you want to [you can get the underlying types and enum right](
https://ecl.common-lisp.dev/static/manual/Foreign-Function-Interface.html#Primitive-Types))
1. `(return-type c-or-c++-function-name)`
## A common lisp stab at b64 encoding using that
In one big common lisp loop sorry. I just tried to literally match jeremy_list's code rather than refer to openbsd's b64encode myself. So I am not sure what translation mistake has crept in: Anyway, the function 'works' for what it is it happens to do.
```
(defun b64encode (bytes &optional (variant 0))
(loop
:with mask6 := #b111111
:for n :from 0 :by 4
:for byte-1 :in bytes :by #'cdddr
:for byte-2 :in (cdr bytes) :by #'cdddr ; I forgot this cdr
:for byte-3 :in (cddr bytes) :by #'cdddr ; and cddr originally here.
:for result-1
:= (c-encode1 (ash byte-1 -2) variant)
:for partial-1
:= (logand (ash byte-1 4)
mask6)
:for result-2
:= (c-encode1 (logior partial-1
(ash byte-2 -4))
variant)
:for partial-2
:= (logand (ash byte-2 2)
mask6)
:for result-3
:= (c-encode1 (logior (ash byte-3 -6)
partial-2)
variant)
:for result-4
:= (c-encode1 (logand byte-3 mask6)
variant)
:nconcing
(list
result-1 result-2 result-3 result-4)
:into b64s
:finally
(return
(coerce
(mapcar 'code-char b64s)
'string))))
```
## Seeing that working ("working")
```
#|
• (setq inferior-lisp-program "ecl")
• (slime)
• (setq eepitch-buffer-name "*slime-repl ECL*")
(load "eat-base64-habitat.ecl.lisp")
"abcdefghijkl"
;; "abcdefghijkl"
(coerce * 'list)
;; (#\a #\b #\c #\d #\e #\f #\g #\h #\i #\j #\k #\l)
(mapcar 'char-code *)
;; (97 98 99 100 101 102 103 104 105 106 107 108)
(b64encode *)
;; YWJjZGVmZ2hpamts ; now works.
;; "YWFhZGRkZ2dnampq" ; When I forgot those cdrs.
```
Well, this (Edit: Now works, simple typo above) seem[ed] wrong viz openbsd [b64encode(1)](
https://man.openbsd.org/b64encode):
```
• (eepitch-shell)
printf "abcdefghijkl" | b64encode lowercase
;; " | b64encode lowercase
;; begin-base64 644 lowercase
;; YWJjZGVmZ2hpamts
;; ====
|#
```
Edit: Now it's working. Forgot three `cdr`s.
Formerly: Though it seems oddly close doesn't it. But it's difficult for me to guess what has gone wrong.
Still, the C++ program code was made available in common lisp. So that has gone right.
# Conclusions
We saw C++ embeddable common lisp compile and connect common lisp to jeremy_list's original C++ namespace using its `sffi` (which is `ecl`; not the external `libffi` - I have met people who just guess only libffi exists), and used it to write a portable lisp version of the base64 encoding rather than a nonportable haikuos C++ one. So we can straightforwardly salvage other projects as starting points; especially C++ programs and libs.
A simple sanity check with openbsd shows something like 2/4 bytes seem to agree with openbsd's b64encode implementation. Edit: This turned out to be a simple fix.
Threeish future implications being ecl inhabiting a C++ program to serve [McCLIM application-frames](
https://mcclim.common-lisp.dev/main.html) into the running C++ program ; relatedly, starting a swank server inside a pre-existing C++ program and [slime-connecting](
https://slime.common-lisp.dev/doc/html/) into the running program from emacs ; lastly, and it's been on my todo-list a while - using the [enzyme library](
https://enzyme.mit.edu/) for automatic differentiation and friends with the previous, especially together with [my Leonardo system](/lispgames/LCKR-object-oriented-simulation-simulation/).
# Fin.
[Talk on the Mastodon as always please](
https://gamerplus.org/@screwlisp/114963441276577775).
Oh! One hour from right now 8am Zulu Sunday I'm going to have a peertube live with [Kasper Galkowski](
https://itch.io/profile/kgal) about common lisp indie gamedev with opengl, which is at least an ffi adjacent topic. Watch the Mastodon, again please.
[Edrx](
https://anggtwu.net/) has been working on emacs eev-mode [kitten](
https://kitten.small-web.org/) and has one [for me personally](
https://anggtwu.net/elisp/screwlispkitten.el.html) I need to work through - this will shape what these articles are like going forward.