/* ** Copyright (C) 2020 SoftBank Robotics Europe ** See COPYING for the license */ #include #include #include #include #include #include #include #include #include #include #include #include qiLogCategory("qi.python.application"); namespace py = pybind11; namespace qi { namespace py { namespace { template struct WithArgcArgv { F f; auto operator()(::py::list args, ArgsExtras... extras) const -> decltype(f(std::declval(), std::declval(), std::forward(extras)...)) { std::vector argsStr; std::transform(args.begin(), args.end(), std::back_inserter(argsStr), [](const ::py::handle& arg){ return arg.cast(); }); std::vector argsCStr; std::transform(argsStr.begin(), argsStr.end(), std::back_inserter(argsCStr), [](const std::string& s) { return const_cast(s.c_str()); }); int argc = argsCStr.size(); char** argv = argsCStr.data(); auto res = f(argc, argv, std::forward(extras)...); // The constructor of `qi::Application` modifies its parameters to // consume the arguments it processed. After the constructor, the first // `argc` elements of `argv` are the arguments that were not recognized by // the application. We then have to propagate the consumption of the // elements to Python, by clearing the list then reinserting the elements // that were left. args.attr("clear")(); for (std::size_t i = 0; i < static_cast(argc); ++i) args.insert(i, argv[i]); return res; } }; template WithArgcArgv, ExtraArgs...> withArgcArgv(F&& f) { return { std::forward(f) }; } } // namespace void exportApplication(::py::module& m) { using namespace ::py; using namespace ::py::literals; GILAcquire lock; class_( m, "Application") .def(init(withArgcArgv<>([](int& argc, char**& argv) { GILRelease unlock; return new Application(argc, argv); })), "args"_a) .def_static("run", &Application::run, call_guard()) .def_static("stop", &Application::stop, call_guard()); class_( m, "ApplicationSession") .def(init(withArgcArgv( [](int& argc, char**& argv, bool autoExit, const std::string& url) { GILRelease unlock; ApplicationSession::Config config; if (!autoExit) config.setOption(qi::ApplicationSession::Option_NoAutoExit); if (!url.empty()) config.setConnectUrl(url); return new ApplicationSession(argc, argv, config); })), "args"_a, "autoExit"_a, "url"_a) .def("run", &ApplicationSession::run, call_guard(), doc("Block until the end of the program (call " ":py:func:`qi.ApplicationSession.stop` to end the program).")) .def_static("stop", &ApplicationSession::stop, call_guard(), doc( "Ask the application to stop, the run function will return.")) .def("start", &ApplicationSession::startSession, call_guard(), doc("Start the connection of the session, once this function is " "called everything is fully initialized and working.")) .def_static("atRun", &ApplicationSession::atRun, call_guard(), "func"_a, doc( "Add a callback that will be executed when run() is called.")) .def_property_readonly("url", [](const ApplicationSession& app) { return app.url().str(); }, call_guard(), doc("The url given to the Application. It's the url " "used to connect the session.")) .def_property_readonly("session", [](const ApplicationSession& app) { return makeSession(app.session()); }, doc("The session associated to the application.")); } } // namespace py } // namespace qi