REPL in C++: types to the rescue

This post is a follow-up to my hands-on session article about C++ REPL.

Being able to  display readable types and content for your variables and functions can greatly improve the user experience inside a C++ REPL.

This article is very short, so that you can jump to the short (2 minutes) demonstration video below. It is based on CleanType, a C++ header-only library whose intent is to get readable types and content.

For those in a hurry, you can skip to the video transcript at the end of this page.

Some links before concluding this article:

  • The demo code for this REPL demo is available here
  • For those who were interested in the jupyter notebook C++ REPL environment, you can also try it inside jupyter notebook (beware, this may require a minute or two to load, mybinder.org is sometimes sluggish)
  • For those who prefer a demo in a more standard C++ environment, here is a simple demo, which you can also try interactively on gitpod (registration required)

Video Transcript

Note: Here is link to the same transcript as a gist, with nicer colors.


// REPL in C++: display readable types and
// variable contents.

// This demo uses cling, a fully compliant C++14 REPL,
// and asciinema, a terminal session recorder.
// It is based on CleanType (a C++ type introspection
// library)
// You can pause at any time, and copy-paste samples from it.

#include <cleantype/cleantype.hpp>

// The dumbest logger in the west...
#define LOG(...) std::cout << __VA_ARGS__ << "\n";

// First, let's define a variable for demonstration purpose
std::set<std::string> my_set { "Hello", "There"};

// let's ask CleanType to give us the type of "my_set"
// cleantype::full will return the *full* type info
LOG(  cleantype::full(my_set)  );
--> std::set<std::__cxx11::basic_string<char>, std::less<std::__cxx11::basic_string<char>>, std::allocator<std::__cxx11::basic_string<char>>> &


// Ouch, that was barely readable!
// cleantype::clean will return a *readable* type
LOG(  cleantype::clean(my_set) );
--> std::set<std::string> &

// Let's now show the content of "my_set" together
// with its type
LOG(  cleantype::show_details(my_set) );
--> std::set<std::string> & = [Hello, There]


// Yes, but what about lambdas? Could you guess
// the signature of the lambda below?
auto lambda_example = []() {
    // when C++ meets js...
    return +!!"";
    // See https://blog.knatten.org/2018/10/12/1662
};

// cleantype::lambda_clean returns the signature of lambda functions
LOG(  cleantype::lambda_clean(lambda_example) );
--> lambda: () -> int

// Ok, maybe this was too easy.
// Let's try with a generic lambda!
auto add = [](auto a, auto b) {
    return a + b;
};

// Now, can we see its signature?
// Yes, we just need to specify the args types.
LOG(  cleantype::lambda_clean<std::string, char>(add)  );
--> lambda: (std::string, char) -> std::string



// Can CleanType understand some more complex libraries
// like range-v3 where most variables, functions
// and lambdas are of type "auto"?
// Well...    yes!

#include <range/v3/all.hpp>

using namespace ranges;

auto square_yield_fn(int x) {
  return ranges::yield(x * x);
}
auto squares_view = view::for_each(view::ints(1), square_yield_fn);

// What is the type of squares_view?
// Let's see...
LOG( cleantype::clean(squares_view)  );
--> ranges::v3::join_view<ranges::v3::transform_view<ranges::v3::iota_view<int, void>, ranges::v3::single_view<int>(*)(int)>, void> &

// Let's make it more complex yet:
auto squares_take_10 = squares_view | view::take(10);

// As you will see below, CleanType can indent
// the types when they get more complex!

LOG(  cleantype::clean(squares_take_10)  );
--> ranges::v3::detail::take_exactly_view_<
    ranges::v3::join_view<
        ranges::v3::transform_view<
            ranges::v3::iota_view<
                int,
                void
            >,
            ranges::v3::single_view<
                int
            > (*)(int)
        >,
        void
    >,
    false
> &



// Thanks for watching!