diff --git a/CMakeLists.txt b/CMakeLists.txt index 5273c3cb..6b1cba57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -189,6 +189,7 @@ set(XEUS_CPP_SRC src/xparser.cpp src/xutils.cpp src/xmagics/os.cpp + src/xmagics/multi_interpreter.cpp ) if(NOT EMSCRIPTEN) diff --git a/notebooks/sub-interpreters.ipynb b/notebooks/sub-interpreters.ipynb new file mode 100644 index 00000000..3772006f --- /dev/null +++ b/notebooks/sub-interpreters.ipynb @@ -0,0 +1,216 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "fb74181a-b881-4ae9-9224-495425707762", + "metadata": {}, + "source": [ + "**Default Interpreter of the Kernel**" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "e5375e1a-347b-461e-97ac-1a5bc708c896", + "metadata": { + "vscode": { + "languageId": "c++" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "201703\n" + ] + } + ], + "source": [ + "#include \n", + "std::cout << __cplusplus << std::endl;" + ] + }, + { + "cell_type": "markdown", + "id": "7ec1684f-ce9a-4614-8f7c-8391b68f7eb5", + "metadata": {}, + "source": [ + "**Creating New Sub-Interpreter with `-std=c++23`**\n", + "\n", + "_We give it a name `cpp23` to re-use later_" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "756696a7-c8db-4302-8794-029664673952", + "metadata": { + "vscode": { + "languageId": "c++" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Elapsed time (C++23): 5.97e-06 seconds\n" + ] + } + ], + "source": [ + "%%subinterp -Wall -O3 --std=c++23 --name cpp23\n", + "\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "\n", + "void version() { std::cout << __cplusplus << std::endl; }\n", + "\n", + "template constexpr auto to_vector(R&& r) { return std::vector>>{r.begin(), r.end()}; }\n", + "\n", + "void run() {\n", + " std::vector numbers(10'000);\n", + " for (int i = 0; i < 10'000; i++) numbers.push_back(i);\n", + " \n", + " auto start = std::chrono::high_resolution_clock::now();\n", + " std::vector result = to_vector(\n", + " numbers \n", + " | std::views::filter([](int n) { return n % 2 != 0; }) // filter out even numbers\n", + " | std::views::transform([](int n) { return n * 2; })); // multiply remaining by 2\n", + " auto end = std::chrono::high_resolution_clock::now();\n", + " \n", + " std::chrono::duration elapsed = end - start;\n", + " std::cout << \"Elapsed time (C++23): \" << elapsed.count() << \" seconds\\n\";\n", + "}\n", + "run();" + ] + }, + { + "cell_type": "markdown", + "id": "1a31f103-b92a-44e9-bf42-61709340bdc4", + "metadata": {}, + "source": [ + "**Creating Another Sub-Interpreter with `-std=c++11`**" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "4f3391f7-0a38-4cd9-af2b-33245a06db84", + "metadata": { + "vscode": { + "languageId": "c++" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Elapsed time (C++11): 5.22e-06 seconds\n" + ] + } + ], + "source": [ + "%%subinterp -O3 --std=c++11 --name cpp11\n", + "\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "\n", + "void version() { std::cout << __cplusplus << std::endl; }\n", + "\n", + "void run() {\n", + " std::vector numbers(10000);\n", + " for (int i = 0; i < 10000; i++) numbers.push_back(i);\n", + " std::vector result;\n", + "\n", + " auto start = std::chrono::high_resolution_clock::now();\n", + " std::copy_if(numbers.begin(), numbers.end(), std::back_inserter(result),\n", + " [](int n) { return n % 2 != 0; }); // filter out even numbers\n", + " std::transform(result.begin(), result.end(), result.begin(),\n", + " [](int n) { return n * 2; }); // multiply remaining by 2\n", + " auto end = std::chrono::high_resolution_clock::now();\n", + " \n", + " std::chrono::duration elapsed = end - start;\n", + " std::cout << \"Elapsed time (C++11): \" << elapsed.count() << \" seconds\\n\";\n", + "}\n", + "run();" + ] + }, + { + "cell_type": "markdown", + "id": "63cecedc-0e72-43fd-80e4-36a612a8042b", + "metadata": {}, + "source": [ + "**Re-Using the Interpreter Created Previously**" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "600da197-f902-4cf4-a401-a848e08aa638", + "metadata": { + "vscode": { + "languageId": "c++" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "202302\n" + ] + } + ], + "source": [ + "%%subinterp --use cpp23\n", + "version();" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "2c2fa915-a6de-4477-b4f8-6c342720c224", + "metadata": { + "vscode": { + "languageId": "c++" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "201103\n" + ] + } + ], + "source": [ + "%%subinterp --use cpp11\n", + "version();" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "C++17", + "language": "cpp", + "name": "xcpp17" + }, + "language_info": { + "codemirror_mode": "text/x-c++src", + "file_extension": ".cpp", + "mimetype": "text/x-c++src", + "name": "C++", + "version": "17" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/src/xinterpreter.cpp b/src/xinterpreter.cpp index 14972324..98bc2ebe 100644 --- a/src/xinterpreter.cpp +++ b/src/xinterpreter.cpp @@ -25,6 +25,8 @@ #include "xparser.hpp" #include "xsystem.hpp" +#include "xmagics/multi_interpreter.hpp" + using Args = std::vector; void* createInterpreter(const Args &ExtraArgs = {}) { @@ -371,6 +373,7 @@ __get_cxx_version () // preamble_manager["magics"].get_cast().register_magic("timeit", // timeit(&m_interpreter)); // preamble_manager["magics"].get_cast().register_magic("python", pythonexec()); + preamble_manager["magics"].get_cast().register_magic("subinterp", multi_interpreter()); preamble_manager["magics"].get_cast().register_magic("file", writefile()); #ifndef EMSCRIPTEN preamble_manager["magics"].get_cast().register_magic("xassist", xassist()); diff --git a/src/xmagics/multi_interpreter.cpp b/src/xmagics/multi_interpreter.cpp new file mode 100644 index 00000000..575ea002 --- /dev/null +++ b/src/xmagics/multi_interpreter.cpp @@ -0,0 +1,93 @@ +/************************************************************************************ + * Copyright (c) 2025, xeus-cpp contributors * + * * + * Distributed under the terms of the BSD 3-Clause License. * + * * + * The full license is in the file LICENSE, distributed with this software. * + ************************************************************************************/ + +#include "multi_interpreter.hpp" + +#include +#include +#include +#include +#include + +#include "CppInterOp/CppInterOp.h" + +static std::vector split(const std::string& input) +{ + std::istringstream buffer(input); + std::vector ret( + (std::istream_iterator(buffer)), + std::istream_iterator() + ); + return ret; +} + +static constexpr size_t len(std::string_view n) +{ + return n.size(); +} + +namespace xcpp +{ + void multi_interpreter::operator()(const std::string& line, const std::string& cell) + { + auto Args0 = split(line.substr(len("subinterp"))); + + Cpp::TInterp_t OldI = Cpp::GetInterpreter(); // we need to restore the old interpreter + Cpp::TInterp_t I = nullptr; + std::string name; + if (Args0[0] == "--use") + { + I = interpreters[Args0[1]]; + } + else + { + auto named = std::find(Args0.begin(), Args0.end(), "--name"); + if (named != Args0.end()) + { + name = *(named + 1); + } + } + + std::vector Args(I ? 0 : Args0.size()); + if (!I) + { + for (auto start = Args0.begin(), end = Args0.end(); start != end; start++) + { + if (*start == "--name") + { + start++; + continue; + } + Args.push_back((*start).c_str()); + } + } + + Cpp::BeginStdStreamCapture(Cpp::kStdErr); + Cpp::BeginStdStreamCapture(Cpp::kStdOut); + if (!I) + { + I = Cpp::CreateInterpreter(Args); // TODO: error handling + } + if (I) + { + Cpp::Declare(cell.c_str(), false, I); + } + std::cout << Cpp::EndStdStreamCapture(); + std::cerr << Cpp::EndStdStreamCapture(); + + if (!name.empty()) + { + interpreters[name] = I; // TODO: check if this is redefinition + Cpp::ActivateInterpreter(OldI); // restoring old interpreter + } + else + { + Cpp::DeleteInterpreter(I); + } + } +} // namespace xcpp diff --git a/src/xmagics/multi_interpreter.hpp b/src/xmagics/multi_interpreter.hpp new file mode 100644 index 00000000..7d83a2aa --- /dev/null +++ b/src/xmagics/multi_interpreter.hpp @@ -0,0 +1,33 @@ +/************************************************************************************ + * Copyright (c) 2025, xeus-cpp contributors * + * * + * Distributed under the terms of the BSD 3-Clause License. * + * * + * The full license is in the file LICENSE, distributed with this software. * + ************************************************************************************/ + +#ifndef XEUS_CPP_MULTI_INTERPRETER_MAGIC_HPP +#define XEUS_CPP_MULTI_INTERPRETER_MAGIC_HPP + +#include +#include + +#include "CppInterOp/CppInterOp.h" +#include "xeus-cpp/xmagics.hpp" + +namespace xcpp +{ + class multi_interpreter : public xmagic_cell + { + public: + + XEUS_CPP_API + void operator()(const std::string& line, const std::string& cell) override; + + private: + + std::unordered_map interpreters; + }; +} // namespace xcpp + +#endif // XEUS_CPP_MULTI_INTERPRETER_MAGIC_HPP