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:

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]$ 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]$ std::string {"str"}
(std::string) "str"
[cling]$ #include <vector>
[cling]$ std::vector<char> ve{'c', 'h', 'a', 'r'} 
(std::vector<char> &) { 'c', 'h', 'a', 'r' }
[cling]$ #include <map>
[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 supports some internal commands as well. These commands are started with a dot, and you may have already used one of them to quit cling: ".q". 

Let's see a command supporting load of external source files: ".L"

# echo 'int val = 42;' > tst.cpp
# ./cling
****************** CLING ******************
* Type C++ code and press enter to run it *
*             Type .q to exit             *
[cling]$ .L tst.cpp
[cling]$ val
(int) 42

 The symbols in loaded files are ready to use now. Notice that cling can NOT handle the dependency of source files loaded for you (it don't know where the dependencies are, apparently), thus if the loaded files depend on other files, you need to load them manually.

A similar command is ".x". It loads the designated file as well, however, if a function inside has the same name as the file, that function will be called immediately after loading:

# echo 'int tst(){return 42;}' > tst.cpp
# ./cling
****************** CLING ******************
* Type C++ code and press enter to run it *
*             Type .q to exit             *
[cling]$ .x tst.cpp
(int) 42

 One of the incredible features of cling is it can also load shared libraries (.so)! Notice that you'll need to include the corresponding headers to make the symbols in them accessible:

[cling]$ #include <gpiod.hpp>
[cling]$ .L /usr/lib/arm-linux-gnueabihf/
[cling]$ .L /usr/lib/arm-linux-gnueabihf/
[cling]$ gpiod::chip chip("/dev/gpiochip0")
(gpiod::chip &) @0xb6fa100c
[cling]$ auto lines = chip.get_line(236)
(gpiod::line &) @0xb6fa1014
[cling]$ lines.request({.consumer = std::string("tst"),
[cling]$ ?      
[cling]$ ?      .request_type = gpiod::line_request::DIRECTION_OUTPUT,
[cling]$ ?      
[cling]$ ?      .flags = gpiod::line_request::FLAG_OPEN_DRAIN})

Miscellaneous Commands

".I" which adds or prints include searching paths:

[cling]$ .I

".class", a powerful command which prints the layout of  specified class:

[cling]$ #include<limits>
[cling]$ .class std::numeric_limits<char>
struct std::numeric_limits<char>
SIZE: 1 FILE: limits LINE: 453
List of member variables --------------------------------------------------
limits          455 0x0      public: static constexpr _Bool is_specialized
limits          468 0x0      public: static constexpr int digits
limits          469 0x0      public: static constexpr int digits10
limits          471 0x0      public: static constexpr int max_digits10
limits          473 0x0      public: static constexpr _Bool is_signed
limits          474 0x0      public: static constexpr _Bool is_integer
limits          475 0x0      public: static constexpr _Bool is_exact
limits          476 0x0      public: static constexpr int radix
limits          484 0x0      public: static constexpr int min_exponent
limits          485 0x0      public: static constexpr int min_exponent10
limits          486 0x0      public: static constexpr int max_exponent
limits          487 0x0      public: static constexpr int max_exponent10
limits          489 0x0      public: static constexpr _Bool has_infinity
limits          490 0x0      public: static constexpr _Bool has_quiet_NaN
limits          491 0x0      public: static constexpr _Bool has_signaling_NaN
limits          492 0x0      public: static constexpr enum std::float_denorm_style has_denorm
limits          494 0x0      public: static constexpr _Bool has_denorm_loss
limits          508 0x0      public: static constexpr _Bool is_iec559
limits          509 0x0      public: static constexpr _Bool is_bounded
limits          510 0x0      public: static constexpr _Bool is_modulo
limits          512 0x0      public: static constexpr _Bool traps
limits          513 0x0      public: static constexpr _Bool tinyness_before
limits          514 0x0      public: static constexpr enum std::float_round_style round_style
List of member functions :---------------------------------------------------
filename     line:size busy function type and name
(compiled)     (NA):(NA) 0 public: static constexpr char min() noexcept;
(compiled)     (NA):(NA) 0 public: static constexpr char max() noexcept;
(compiled)     (NA):(NA) 0 public: static constexpr char lowest() noexcept;
(compiled)     (NA):(NA) 0 public: static constexpr char epsilon() noexcept;
(compiled)     (NA):(NA) 0 public: static constexpr char round_error() noexcept;
(compiled)     (NA):(NA) 0 public: static constexpr char infinity() noexcept;
(compiled)     (NA):(NA) 0 public: static constexpr char quiet_NaN() noexcept;
(compiled)     (NA):(NA) 0 public: static constexpr char signaling_NaN() noexcept;
(compiled)     (NA):(NA) 0 public: static constexpr char denorm_min() noexcept;

Finally, " .help" command which prints all available (not really, some commands hinted in the ROOT prompt seem to be omitted here) commands and their corresponding usages:

[cling]$ .help

 Cling (C/C++ interpreter) meta commands usage
 All commands must be preceded by a '.', except
 for the evaluation statement { }
 Syntax: .Command [arg0 arg1 ... argN]

   .L <filename>                - Load the given file or library

   .(x|X) <filename>[args]      - Same as .L and runs a function with
                                  signature: ret_type filename(args)

   .> <filename>                - Redirect command to a given file
      '>' or '1>'               - Redirects the stdout stream only
      '2>'                      - Redirects the stderr stream only
      '&>' (or '2>&1')          - Redirects both stdout and stderr
      '>>'                      - Appends to the given file

   .undo [n]                    - Unloads the last 'n' inputs lines

   .U <filename>                - Unloads the given file

   .I [path]                    - Shows the include path. If a path is given -
                                  adds the path to the include paths

   .O <level>                   - Sets the optimization level (0-3)
                                  (not yet implemented)

   .class <name>                - Prints out class <name> in a CINT-like style

   .files                       - Prints out some CINT-like file statistics

   .fileEx                      - Prints out some file statistics

   .g                           - Prints out information about global variable
                                  'name' - if no name is given, print them all

   .@                           - Cancels and ignores the multiline input

   .rawInput [0|1]              - Toggle wrapping and printing the
                                  execution results of the input

   .dynamicExtensions [0|1]     - Toggles the use of the dynamic scopes and the
                                  late binding

   .printDebug [0|1]            - Toggles the printing of input's corresponding
                                  state changes

   .storeState <filename>       - Store the interpreter's state to a given file

   .compareState <filename>     - Compare the interpreter's state with the one
                                  saved in a given file

   .stats [name]                - Show stats for internal data structures
                                  'ast'  abstract syntax tree stats
                                  'asttree [filter]'  abstract syntax tree layout
                                  'decl' dump ast declarations
                                  'undo' show undo stack

   .help                        - Shows this information

   .q                           - Exit the program

Notes on Serial Devices

It seems that cling would print characters not compatible with a serial console. In case you find problem using it on a device connected via a serial port, try out command below which executes cling within a wrapper:

script -qc ./cling | cat