An Interpreter, for C++?
Yes. Despite it's widely known that C++ is an AOT language, such an amazing thing as a C++ interpreter does exist. It's cling developed by Root Team of CERN on the top of LLVM and Clang. You may have played with some toy-like C interpreter already, but cling is a sophisticated project with all features a modern REPL for a regular script language is supposed to have, such as stack trace, variable shadowing and statement value echo back.
It's a shame that the official builds for cling are only available for x86/64 platforms currently, however, I managed to build it for the armhf (arm v7) architecture with a QEMU+chroot environment. Here is the repository containing the build script and a pre-compiled binary in release:
https://github.com/SdtElectronics/cling-build
Sonuds Interesting, But Why Bother?
Cling is one of the most imaginative projects I have ever seen. How useful it is depends on how creative you are. But here I'd like to specially address on its usage on embedded development. Accessing peripherals on a SoC is essentially wrapped by system calls, and only language can handle this easily is C. The compatibility with C of cling makes it able to execute system calls directly, which essentially implies you can do anything (allowed in user space) with it, dynamically and interactively. Granted, there are frameworks like microPython which allows you to interact with peripherals dynamically as well, but this is limited to those APIs already wrapped by the framework. In comparison, you can do anything a C program can do in user space with cling. Additionally, some performance-sensitive applications are preferably written in C/C++, and a REPL is desirable for fast-prototyping and debugging.
The text wall is tiresome. Let's see an example demonstrating interact with the new GPIO API, libgpiod, dynamically with cling:
A Quick Start for Cling
hello world:
[cling]$ #include <stdio.h>
[cling]$
[cling]$ printf("hello world\n");
hello world
You can include all headers contained in the search path of your system, including kernel headers. All symbols are ready to use after the include statement.
Echo Values of Statements:
What if you omit the semicolon at the end of a statement? Will you get an error? No, cling will echo the value of that statement in this condition. People have worked with matlab may feel familiar with this: If the ending semicolon isn't omitted, the echo in the console will be disabled, otherwise the echo will be printed:
[cling]$ __cplusplus
(long) 201703
Cling can echo statements of some objects and their references analytically. The supported objects are mainly STL containers:
[cling]$ #include <string>
[cling]$
[cling]$ std::string {"str"}
(std::string) "str"
[cling]$
[cling]$ #include <vector>
[cling]$
[cling]$ std::vector<char> ve{'c', 'h', 'a', 'r'}
(std::vector<char> &) { 'c', 'h', 'a', 'r' }
[cling]$
[cling]$ #include <map>
[cling]$
[cling]$ std::map<int, char> mp{std::pair<int,char>()}
(std::map<int, char> &) { 0 => '0x00' }
Names of functions are also statements. What values will be echoed for them?
[cling]$ atoi
(int (*)(const char *) throw()) Function @0xb6c7bcd1
at /usr/include/stdlib.h:104:
extern int atoi (const char *__nptr)
__THROW __attribute_pure__ __nonnull ((1))
It's the signature of the function!
I enabled RTTI in my build script, so the dynamic inspection of types by typeid is also supported:
[cling]$ auto b = std::string ("typed")
(std::basic_string<char, std::char_traits<char>, std::allocator<char> > &) "typed"
[cling]$ typeid(b).name()
(const char *) "NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE"
To convert this mangled type name to readable text, you may want to use c++filt:
c++filt _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >
Advanced Usages
Loading Files
Except standard C++ syntax, cling...
Read more »