diff --git a/.github/ci/packages-focal.apt b/.github/ci/packages-focal.apt index 50cee478b6..fe94da46f7 100644 --- a/.github/ci/packages-focal.apt +++ b/.github/ci/packages-focal.apt @@ -3,3 +3,4 @@ libdart-dev libdart-external-ikfast-dev libdart-external-odelcpsolver-dev libdart-utils-urdf-dev +python3-ignition-math6 diff --git a/.github/ci/packages.apt b/.github/ci/packages.apt index 4a15aa2ddf..5308887a5e 100644 --- a/.github/ci/packages.apt +++ b/.github/ci/packages.apt @@ -22,6 +22,8 @@ libsdformat12-dev libtinyxml2-dev libxi-dev libxmu-dev +python3-distutils +python3-pybind11 qml-module-qt-labs-folderlistmodel qml-module-qt-labs-settings qml-module-qtqml-models2 diff --git a/CMakeLists.txt b/CMakeLists.txt index eaf570f512..df48d9915a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.10.2 FATAL_ERROR) #============================================================================ # Initialize the project #============================================================================ -project(ignition-gazebo6 VERSION 6.5.0) +project(ignition-gazebo6 VERSION 6.7.0) #============================================================================ # Find ignition-cmake @@ -15,6 +15,7 @@ find_package(ignition-cmake2 2.8.0 REQUIRED) # Configure the project #============================================================================ ign_configure_project(VERSION_SUFFIX) +set (CMAKE_CXX_STANDARD 17) #============================================================================ # Set project-specific options @@ -37,6 +38,14 @@ endif() include(test/find_dri.cmake) FindDRI() +option(USE_SYSTEM_PATHS_FOR_PYTHON_INSTALLATION + "Install python modules in standard system paths in the system" + OFF) + +option(USE_DIST_PACKAGES_FOR_PYTHON + "Use dist-packages instead of site-package to install python modules" + OFF) + #============================================================================ # Search for project-specific dependencies #============================================================================ @@ -167,6 +176,26 @@ ign_find_package(IgnProtobuf PRETTY Protobuf) set(Protobuf_IMPORT_DIRS ${ignition-msgs8_INCLUDE_DIRS}) +#-------------------------------------- +# Find python +include(IgnPython) +find_package(PythonLibs QUIET) +if (NOT PYTHONLIBS_FOUND) + IGN_BUILD_WARNING("Python is missing: Python interfaces are disabled.") + message (STATUS "Searching for Python - not found.") +else() + message (STATUS "Searching for Python - found version ${PYTHONLIBS_VERSION_STRING}.") + + set(PYBIND11_PYTHON_VERSION 3) + find_package(pybind11 2.2 QUIET) + + if (${pybind11_FOUND}) + message (STATUS "Searching for pybind11 - found version ${pybind11_VERSION}.") + else() + IGN_BUILD_WARNING("pybind11 is missing: Python interfaces are disabled.") + message (STATUS "Searching for pybind11 - not found.") + endif() +endif() # Plugin install dirs set(IGNITION_GAZEBO_PLUGIN_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/${IGN_LIB_INSTALL_DIR}/ign-${IGN_DESIGNATION}-${PROJECT_VERSION_MAJOR}/plugins @@ -187,6 +216,9 @@ add_subdirectory(examples) #============================================================================ ign_create_packages() +if (${pybind11_FOUND}) + add_subdirectory(python) +endif() #============================================================================ # Configure documentation #============================================================================ diff --git a/Changelog.md b/Changelog.md index 36bfd47980..f69e217cfe 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,8 +1,65 @@ ## Ignition Gazebo 6.x -### Ignition Gazebo 6.X.X (202X-XX-XX) +### Ignition Gazebo 6.7.0 (2022-02-24) + +1. Added Python interfaces to some Ignition Gazebo methods + * [Pull request #1219](https://github.com/ignitionrobotics/ign-gazebo/pull/1219) + +1. Use pose multiplication instead of addition + * [Pull request #1369](https://github.com/ignitionrobotics/ign-gazebo/pull/1369) + +1. Disables Failing Buoyancy Tests on Win32 + * [Pull request #1368](https://github.com/ignitionrobotics/ign-gazebo/pull/1368) + +1. Extend ShaderParam system to support loading different shader languages + * [Pull request #1335](https://github.com/ignitionrobotics/ign-gazebo/pull/1335) + +1. Populate names of colliding entities in contact points message + * [Pull request #1351](https://github.com/ignitionrobotics/ign-gazebo/pull/1351) + +1. Refactor System functionality into SystemManager + * [Pull request #1340](https://github.com/ignitionrobotics/ign-gazebo/pull/1340) + +1. GzSceneManager: Prevent crash boom when inserted from menu + * [Pull request #1371](https://github.com/ignitionrobotics/ign-gazebo/pull/1371) + +### Ignition Gazebo 6.6.0 (2022-02-24) + +1. Fix accessing empty JointPosition component in lift drag plugin + * [Pull request #1366](https://github.com/ignitionrobotics/ign-gazebo/pull/1366) + +1. Add parameter to TrajectoryFollower stop rotation when bearing is reached + * [Pull request #1349](https://github.com/ignitionrobotics/ign-gazebo/pull/1349) + +1. Support disabling pose publisher from publishing top level model pose + * [Pull request #1342](https://github.com/ignitionrobotics/ign-gazebo/pull/1342) + +1. Added more sensor properties to scene/info topic + * [Pull request #1344](https://github.com/ignitionrobotics/ign-gazebo/pull/1344) + +1. Adding ability to pause/resume the trajectory follower behavior. + * [Pull request #1347](https://github.com/ignitionrobotics/ign-gazebo/pull/1347) + +1. Logs a warning if a mode is not clearly sepecified. + * [Pull request #1307](https://github.com/ignitionrobotics/ign-gazebo/pull/1307) + +1. JointStatePublisher publish parent, child and axis data + * [Pull request #1345](https://github.com/ignitionrobotics/ign-gazebo/pull/1345) + +1. Fixed light gui component inspector + * [Pull request #1337](https://github.com/ignitionrobotics/ign-gazebo/pull/1337) + +1. Fix UNIT_SdfGenerator_TEST + * [Pull request #1319](https://github.com/ignitionrobotics/ign-gazebo/pull/1319) + +1. Add elevator system + * [Pull request #535](https://github.com/ignitionrobotics/ign-gazebo/pull/535) + +1. Removed unused variables in shapes plugin + * [Pull request #1321](https://github.com/ignitionrobotics/ign-gazebo/pull/1321) + +### Ignition Gazebo 6.5.0 (2022-02-15) -### Ignition Gazebo 6.5.0 (202X-XX-XX) 1. New trajectory follower system * [Pull request #1332](https://github.com/ignitionrobotics/ign-gazebo/pull/1332) diff --git a/examples/scripts/python_api/README.md b/examples/scripts/python_api/README.md new file mode 100644 index 0000000000..a828853737 --- /dev/null +++ b/examples/scripts/python_api/README.md @@ -0,0 +1,6 @@ +# Gazebo Python API + +This example shows how to use Gazebo's Python API. + +For more information, see the +[Python interfaces](https://ignitionrobotics.org/api/gazebo/6.4/python_interfaces.html) tutorial. diff --git a/examples/scripts/python_api/gravity.sdf b/examples/scripts/python_api/gravity.sdf new file mode 100644 index 0000000000..ee1161fa5f --- /dev/null +++ b/examples/scripts/python_api/gravity.sdf @@ -0,0 +1,22 @@ + + + + + + + + + + + 0.4 + 0.4 + 0.4 + + 1.0 + + + + + diff --git a/examples/scripts/python_api/testFixture.py b/examples/scripts/python_api/testFixture.py new file mode 100644 index 0000000000..caedebdc84 --- /dev/null +++ b/examples/scripts/python_api/testFixture.py @@ -0,0 +1,85 @@ +#!/usr/bin/python3 +# Copyright (C) 2021 Open Source Robotics Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# If you compiled Ignition Gazebo from source you should modify your +# `PYTHONPATH`: +# +# export PYTHONPATH=$PYTHONPATH:/install/lib/python +# +# Now you can run the example: +# +# python3 examples/scripts/python_api/helperFixture.py + +import os +import time + +from ignition.common import set_verbosity +from ignition.gazebo import TestFixture, World, world_entity +from ignition.math import Vector3d +from sdformat import Element + +set_verbosity(4) + +file_path = os.path.dirname(os.path.realpath(__file__)) + +helper = TestFixture(os.path.join(file_path, 'gravity.sdf')) + +post_iterations = 0 +iterations = 0 +pre_iterations = 0 +first_iteration = True + + +def on_pre_udpate_cb(_info, _ecm): + global pre_iterations + global first_iteration + pre_iterations += 1 + if first_iteration: + first_iteration = False + world_e = world_entity(_ecm); + print('World entity is ', world_e) + w = World(world_e) + v = w.gravity(_ecm) + print('Gravity ', v) + modelEntity = w.model_by_name(_ecm, 'falling') + print('Entity for falling model is: ', modelEntity) + + +def on_udpate_cb(_info, _ecm): + global iterations + iterations += 1 + + +def on_post_udpate_cb(_info, _ecm): + global post_iterations + post_iterations += 1 + if _info.sim_time.seconds == 1: + print('Post update sim time: ', _info.sim_time) + + +helper.on_post_update(on_post_udpate_cb) +helper.on_update(on_udpate_cb) +helper.on_pre_update(on_pre_udpate_cb) +helper.finalize() + +server = helper.server() +server.run(False, 1000, False) + +while(server.is_running()): + time.sleep(0.1) + +print('iterations ', iterations) +print('post_iterations ', post_iterations) +print('pre_iterations ', pre_iterations) diff --git a/examples/worlds/empty_gui.sdf b/examples/worlds/empty_gui.sdf new file mode 100644 index 0000000000..84b5e172c4 --- /dev/null +++ b/examples/worlds/empty_gui.sdf @@ -0,0 +1,210 @@ + + + + + + + + + + + 3D View + false + docked + + + ogre2 + scene + 0.4 0.4 0.4 + 0.8 0.8 0.8 + -6 0 6 0 0.5 0 + + + + + 1.0 1.0 1.0 + 0.8 0.8 0.8 + + + + + true + 0 0 10 0 0 0 + 0.8 0.8 0.8 1 + 0.8 0.8 0.8 1 + 0.0 0.6 -0.9 + + + + true + + + + + 0 0 1 + + + + + + + 0 0 1 + 100 100 + + + + 0.8 0.8 0.8 1 + 0.8 0.8 0.8 1 + 0.8 0.8 0.8 1 + + + + + + + 0 0 0.5 0 0 0 + + + + 1 + 0 + 0 + 1 + 0 + 1 + + 1.0 + + + + + 1 1 1 + + + + + + + + 1 1 1 + + + + 1 0 0 1 + 1 0 0 1 + 1 0 0 1 + + + + + + + 0 -1.5 0.5 0 0 0 + + + + 2 + 0 + 0 + 2 + 0 + 2 + + 2.0 + + + + + 0.5 + 1.0 + + + + + + + + 0.5 + 1.0 + + + + 0 1 0 1 + 0 1 0 1 + 0 1 0 1 + + + + + + + 0 1.5 0.5 0 0 0 + + + + 3 + 0 + 0 + 3 + 0 + 3 + + 3.0 + + + + + 0.5 + + + + + + + + 0.5 + + + + 0 0 1 1 + 0 0 1 1 + 0 0 1 1 + + + + + + + diff --git a/include/ignition/gazebo/Link.hh b/include/ignition/gazebo/Link.hh index 9dd3d5ccc7..df967d30ca 100644 --- a/include/ignition/gazebo/Link.hh +++ b/include/ignition/gazebo/Link.hh @@ -227,6 +227,14 @@ namespace ignition public: void SetLinearVelocity(EntityComponentManager &_ecm, const math::Vector3d &_vel) const; + + /// \brief Set the angular velocity on this link. If this is set, wrenches + /// on this link will be ignored for the current time step. + /// \param[in] _ecm Entity Component manager. + /// \param[in] _vel Angular velocity to set in Link's Frame. + public: void SetAngularVelocity(EntityComponentManager &_ecm, + const math::Vector3d &_vel) const; + /// \brief Get the angular acceleration of the body in the world frame. /// \param[in] _ecm Entity-component manager. /// \return Angular acceleration of the body in the world frame or diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt new file mode 100644 index 0000000000..bd08e206db --- /dev/null +++ b/python/CMakeLists.txt @@ -0,0 +1,99 @@ +if(WIN32 AND CMAKE_BUILD_TYPE STREQUAL "Debug") + # pybind11 logic for setting up a debug build when both a debug and release + # python interpreter are present in the system seems to be pretty much broken. + # This works around the issue. + set(PYTHON_LIBRARIES "${PYTHON_DEBUG_LIBRARIES}") +endif() + + +if(USE_SYSTEM_PATHS_FOR_PYTHON_INSTALLATION) + if(${CMAKE_VERSION} VERSION_LESS "3.12.0") + execute_process( + COMMAND "${PYTHON_EXECUTABLE}" -c "if True: + from distutils import sysconfig as sc + print(sc.get_python_lib(plat_specific=True))" + OUTPUT_VARIABLE Python3_SITEARCH + OUTPUT_STRIP_TRAILING_WHITESPACE) + else() + # Get install variable from Python3 module + # Python3_SITEARCH is available from 3.12 on, workaround if needed: + find_package(Python3 COMPONENTS Interpreter) + endif() + + if(USE_DIST_PACKAGES_FOR_PYTHON) + string(REPLACE "site-packages" "dist-packages" IGN_PYTHON_INSTALL_PATH ${Python3_SITEARCH}) + endif() +else() + # If not a system installation, respect local paths + set(IGN_PYTHON_INSTALL_PATH ${IGN_LIB_INSTALL_DIR}/python) +endif() + +# Set the build location and install location for a CPython extension +function(configure_build_install_location _library_name) + # Install library for actual use + install(TARGETS ${_library_name} + DESTINATION "${IGN_PYTHON_INSTALL_PATH}/ignition" + ) +endfunction() + +pybind11_add_module(gazebo SHARED + src/ignition/gazebo/_ignition_gazebo_pybind11.cc + src/ignition/gazebo/EntityComponentManager.cc + src/ignition/gazebo/EventManager.cc + src/ignition/gazebo/TestFixture.cc + src/ignition/gazebo/Server.cc + src/ignition/gazebo/ServerConfig.cc + src/ignition/gazebo/UpdateInfo.cc + src/ignition/gazebo/Util.cc + src/ignition/gazebo/World.cc +) + +target_link_libraries(gazebo PRIVATE + ${PROJECT_LIBRARY_TARGET_NAME} + sdformat${SDF_VER}::sdformat${SDF_VER} + ignition-common${IGN_COMMON_VER}::ignition-common${IGN_COMMON_VER} +) + +# TODO(ahcorde): Move this module to ign-common +pybind11_add_module(common SHARED + src/ignition/common/_ignition_common_pybind11.cc + src/ignition/common/Console.cc +) + +target_link_libraries(common PRIVATE + ignition-common${IGN_COMMON_VER}::ignition-common${IGN_COMMON_VER} +) + +# TODO(ahcorde): Move this module to sdformat +pybind11_add_module(sdformat SHARED + src/ignition/sdformat/_sdformat_pybind11.cc + src/ignition/sdformat/Element.cc +) + +target_link_libraries(sdformat PRIVATE + sdformat${SDF_VER}::sdformat${SDF_VER} +) + +install(TARGETS sdformat + DESTINATION "${IGN_PYTHON_INSTALL_PATH}" +) + +configure_build_install_location(gazebo) +configure_build_install_location(common) + +if (BUILD_TESTING) + set(python_tests + testFixture_TEST + ) + + foreach (test ${python_tests}) + add_test(NAME ${test}.py COMMAND + "${PYTHON_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/python/test/${test}.py") + + set(_env_vars) + list(APPEND _env_vars "PYTHONPATH=${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/python/") + list(APPEND _env_vars "LD_LIBRARY_PATH=${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}:$ENV{LD_LIBRARY_PATH}") + set_tests_properties(${test}.py PROPERTIES + ENVIRONMENT "${_env_vars}") + endforeach() +endif() diff --git a/python/src/ignition/common/Console.cc b/python/src/ignition/common/Console.cc new file mode 100644 index 0000000000..8d904a4487 --- /dev/null +++ b/python/src/ignition/common/Console.cc @@ -0,0 +1,31 @@ +// Copyright 2021 Open Source Robotics Foundation, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "Console.hh" + +namespace ignition +{ + namespace common + { + namespace python + { + void SetVerbosity(int _verbosity) + { + ignition::common::Console::SetVerbosity(_verbosity); + } + } + } +} diff --git a/python/src/ignition/common/Console.hh b/python/src/ignition/common/Console.hh new file mode 100644 index 0000000000..4b84fbf514 --- /dev/null +++ b/python/src/ignition/common/Console.hh @@ -0,0 +1,31 @@ +// Copyright 2021 Open Source Robotics Foundation, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef IGNITION_GAZEBO_PYTHON__CONSOLE_HH_ +#define IGNITION_GAZEBO_PYTHON__CONSOLE_HH_ + +#include + +namespace ignition +{ + namespace common + { + namespace python + { + void SetVerbosity(int _verbosity); + } + } +} + +#endif diff --git a/python/src/ignition/common/_ignition_common_pybind11.cc b/python/src/ignition/common/_ignition_common_pybind11.cc new file mode 100644 index 0000000000..12a67670fc --- /dev/null +++ b/python/src/ignition/common/_ignition_common_pybind11.cc @@ -0,0 +1,25 @@ +// Copyright 2021 Open Source Robotics Foundation, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "Console.hh" + +PYBIND11_MODULE(common, m) { + m.doc() = "Ignition Common Python Library."; + + m.def( + "set_verbosity", &ignition::common::python::SetVerbosity, + "Set verbosity level."); +} diff --git a/python/src/ignition/gazebo/EntityComponentManager.cc b/python/src/ignition/gazebo/EntityComponentManager.cc new file mode 100644 index 0000000000..1786ba387c --- /dev/null +++ b/python/src/ignition/gazebo/EntityComponentManager.cc @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include + +#include "EntityComponentManager.hh" + +namespace ignition +{ +namespace gazebo +{ +namespace python +{ +///////////////////////////////////////////////// +void defineGazeboEntityComponentManager(pybind11::object module) +{ + pybind11::class_( + module, "EntityComponentManager") + .def(pybind11::init<>()); +} +} // namespace python +} // namespace gazebo +} // namespace ignition diff --git a/python/src/ignition/gazebo/EntityComponentManager.hh b/python/src/ignition/gazebo/EntityComponentManager.hh new file mode 100644 index 0000000000..b982483631 --- /dev/null +++ b/python/src/ignition/gazebo/EntityComponentManager.hh @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IGNITION_GAZEBO_PYTHON__ENTITY_COMPONENT_MANAGER_HH_ +#define IGNITION_GAZEBO_PYTHON__ENTITY_COMPONENT_MANAGER_HH_ + +#include + +#include "ignition/gazebo/EntityComponentManager.hh" + +namespace ignition +{ +namespace gazebo +{ +namespace python +{ +/// Define a pybind11 wrapper for an ignition::gazebo::EntityComponentManager +/** + * \param[in] module a pybind11 module to add the definition to + */ +void +defineGazeboEntityComponentManager(pybind11::object module); +} // namespace python +} // namespace gazebo +} // namespace ignition + +#endif // IGNITION_GAZEBO_PYTHON__ENTITY_COMPONENT_MANAGER_HH_ diff --git a/python/src/ignition/gazebo/EventManager.cc b/python/src/ignition/gazebo/EventManager.cc new file mode 100644 index 0000000000..39ecbfb8b5 --- /dev/null +++ b/python/src/ignition/gazebo/EventManager.cc @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "ignition/gazebo/EventManager.hh" + +#include "EventManager.hh" + +namespace ignition +{ +namespace gazebo +{ +namespace python +{ +///////////////////////////////////////////////// +void defineGazeboEventManager(pybind11::object module) +{ + pybind11::class_(module, "EventManager") + .def(pybind11::init<>()); +} +} // namespace python +} // namespace gazebo +} // namespace ignition diff --git a/python/src/ignition/gazebo/EventManager.hh b/python/src/ignition/gazebo/EventManager.hh new file mode 100644 index 0000000000..bcd177edcb --- /dev/null +++ b/python/src/ignition/gazebo/EventManager.hh @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef IGNITION_GAZEBO_PYTHON__EVENT_MANAGER_HH_ +#define IGNITION_GAZEBO_PYTHON__EVENT_MANAGER_HH_ + +#include + +namespace ignition +{ +namespace gazebo +{ +namespace python +{ +/// Define a pybind11 wrapper for an ignition::gazebo::EventManager +/** + * \param[in] module a pybind11 module to add the definition to + */ +void +defineGazeboEventManager(pybind11::object module); +} // namespace python +} // namespace gazebo +} // namespace ignition + +#endif // IGNITION_GAZEBO_PYTHON__EVENT_MANAGER_HH_ diff --git a/python/src/ignition/gazebo/Server.cc b/python/src/ignition/gazebo/Server.cc new file mode 100644 index 0000000000..917560a8ae --- /dev/null +++ b/python/src/ignition/gazebo/Server.cc @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include + +#include +#include + +#include "Server.hh" + +namespace ignition +{ +namespace gazebo +{ +namespace python +{ +void defineGazeboServer(pybind11::object module) +{ + pybind11::class_>(module, "Server") + .def(pybind11::init()) + .def( + "run", &ignition::gazebo::Server::Run, + "Run the server. By default this is a non-blocking call, " + " which means the server runs simulation in a separate thread. Pass " + " in true to the _blocking argument to run the server in the current " + " thread.") + .def( + "has_entity", &ignition::gazebo::Server::HasEntity, + "Return true if the specified world has an entity with the provided name.") + .def( + "is_running", + pybind11::overload_cast<>(&ignition::gazebo::Server::Running, pybind11::const_), + "Get whether the server is running."); +} +} // namespace python +} // namespace gazebo +} // namespace ignition diff --git a/python/src/ignition/gazebo/Server.hh b/python/src/ignition/gazebo/Server.hh new file mode 100644 index 0000000000..6e969a035b --- /dev/null +++ b/python/src/ignition/gazebo/Server.hh @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef IGNITION_GAZEBO_PYTHON__SERVER_HH_ +#define IGNITION_GAZEBO_PYTHON__SERVER_HH_ + +#include + +namespace ignition +{ +namespace gazebo +{ +namespace python +{ +/// Define a pybind11 wrapper for an ignition::gazebo::Server +/** + * \param[in] module a pybind11 module to add the definition to + */ +void +defineGazeboServer(pybind11::object module); +} // namespace python +} // namespace gazebo +} // namespace ignition + +#endif // IGNITION_GAZEBO_PYTHON__SERVER_HH_ diff --git a/python/src/ignition/gazebo/ServerConfig.cc b/python/src/ignition/gazebo/ServerConfig.cc new file mode 100644 index 0000000000..d8b650aff1 --- /dev/null +++ b/python/src/ignition/gazebo/ServerConfig.cc @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include + +#include + +#include "ServerConfig.hh" + +namespace ignition +{ +namespace gazebo +{ +namespace python +{ +void defineGazeboServerConfig(pybind11::object module) +{ + pybind11::class_(module, "ServerConfig") + .def(pybind11::init<>()) + .def( + "set_sdf_file", &ignition::gazebo::ServerConfig::SetSdfFile, + "Set an SDF file to be used with the server."); +} +} // namespace python +} // namespace gazebo +} // namespace ignition diff --git a/python/src/ignition/gazebo/ServerConfig.hh b/python/src/ignition/gazebo/ServerConfig.hh new file mode 100644 index 0000000000..d955bd5f49 --- /dev/null +++ b/python/src/ignition/gazebo/ServerConfig.hh @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef IGNITION_GAZEBO_PYTHON__SERVER_CONFIG_HH_ +#define IGNITION_GAZEBO_PYTHON__SERVER_CONFIG_HH_ + +#include + +namespace ignition +{ +namespace gazebo +{ +namespace python +{ +/// Define a pybind11 wrapper for an ignition::gazebo::ServerConfig +/** + * \param[in] module a pybind11 module to add the definition to + */ +void +defineGazeboServerConfig(pybind11::object module); +} // namespace python +} // namespace gazebo +} // namespace ignition + +#endif // IGNITION_GAZEBO_PYTHON__SERVER_CONFIG_HH_ diff --git a/python/src/ignition/gazebo/TestFixture.cc b/python/src/ignition/gazebo/TestFixture.cc new file mode 100644 index 0000000000..7be546716a --- /dev/null +++ b/python/src/ignition/gazebo/TestFixture.cc @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ +#include +#include + +#include "TestFixture.hh" + +#include "ignition/gazebo/TestFixture.hh" + +#include "wrap_functions.hh" + +namespace ignition +{ +namespace gazebo +{ +namespace python +{ +void +defineGazeboTestFixture(pybind11::object module) +{ + pybind11::class_> testFixture(module, "TestFixture"); + + testFixture + .def(pybind11::init()) + .def( + "server", &TestFixture::Server, + pybind11::return_value_policy::reference, + "Get pointer to underlying server." + ) + .def( + "finalize", &TestFixture::Finalize, + pybind11::return_value_policy::reference, + "Finalize all the functions and add fixture to server." + ) + .def( + "on_pre_update", WrapCallbacks( + [](TestFixture* self, std::function _cb) + { + self->OnPreUpdate(_cb); + } + ), + pybind11::return_value_policy::reference, + "Wrapper around a system's pre-update callback" + ) + .def( + "on_update", WrapCallbacks( + [](TestFixture* self, std::function _cb) + { + self->OnUpdate(_cb); + } + ), + pybind11::return_value_policy::reference, + "Wrapper around a system's update callback" + ) + .def( + "on_post_update", WrapCallbacks( + [](TestFixture* self, std::function _cb) + { + self->OnPostUpdate(_cb); + } + ), + pybind11::return_value_policy::reference, + "Wrapper around a system's post-update callback" + ); + // TODO(ahcorde): This method is not compiling for the following reason: + // The EventManager class has an unordered_map which holds a unique_ptr + // This make the class uncopyable, anyhow we should not copy the class + // we just need the reference. Not really sure about what's going on here + // .def( + // "on_configure", WrapCallbacks( + // [](TestFixture* self, std::function &_sdf, + // EntityComponentManager &_ecm, + // EventManager &_eventMgr)> _cb) + // { + // self->OnConfigure(_cb); + // } + // ), + // pybind11::return_value_policy::reference, + // "Wrapper around a system's configure callback" + // ); +} +} +} +} diff --git a/python/src/ignition/gazebo/TestFixture.hh b/python/src/ignition/gazebo/TestFixture.hh new file mode 100644 index 0000000000..b355521943 --- /dev/null +++ b/python/src/ignition/gazebo/TestFixture.hh @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ +#ifndef IGNITION_GAZEBO_PYTHON__TEST_FIXTURE_HH_ +#define IGNITION_GAZEBO_PYTHON__TEST_FIXTURE_HH_ + +#include + +namespace ignition +{ +namespace gazebo +{ +namespace python +{ +/// Define a pybind11 wrapper for an ignition::gazebo::TestFixture +/** + * \param[in] module a pybind11 module to add the definition to + */ +void +defineGazeboTestFixture(pybind11::object module); +} +} +} + +#endif // IGNITION_GAZEBO_PYTHON__TEST_FIXTURE_HH_ diff --git a/python/src/ignition/gazebo/UpdateInfo.cc b/python/src/ignition/gazebo/UpdateInfo.cc new file mode 100644 index 0000000000..e31dc7cb92 --- /dev/null +++ b/python/src/ignition/gazebo/UpdateInfo.cc @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include + +#include + +#include "UpdateInfo.hh" + +namespace ignition +{ +namespace gazebo +{ +namespace python +{ +void defineGazeboUpdateInfo(pybind11::object module) +{ + pybind11::class_(module, "UpdateInfo") + .def(pybind11::init<>()) + .def_readwrite("sim_time", &ignition::gazebo::UpdateInfo::simTime) + .def_readwrite("real_time", &ignition::gazebo::UpdateInfo::realTime) + .def_readwrite("dt", &ignition::gazebo::UpdateInfo::dt) + .def_readwrite("paused", &ignition::gazebo::UpdateInfo::paused) + .def_readwrite("iterations", &ignition::gazebo::UpdateInfo::iterations); +} +} // namespace python +} // namespace gazebo +} // namespace ignition diff --git a/python/src/ignition/gazebo/UpdateInfo.hh b/python/src/ignition/gazebo/UpdateInfo.hh new file mode 100644 index 0000000000..f2ec4e910d --- /dev/null +++ b/python/src/ignition/gazebo/UpdateInfo.hh @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef IGNITION_GAZEBO_PYTHON__UPDATE_INFO_HH_ +#define IGNITION_GAZEBO_PYTHON__UPDATE_INFO_HH_ + +#include + +namespace ignition +{ +namespace gazebo +{ +namespace python +{ +/// Define a pybind11 wrapper for an ignition::gazebo::UpdateInfo +/** + * \param[in] module a pybind11 module to add the definition to + */ +void +defineGazeboUpdateInfo(pybind11::object module); +} // namespace python +} // namespace gazebo +} // namespace ignition + +#endif // IGNITION_GAZEBO_PYTHON__UPDATE_INFO_HH_ diff --git a/python/src/ignition/gazebo/Util.cc b/python/src/ignition/gazebo/Util.cc new file mode 100644 index 0000000000..3dd225f8a7 --- /dev/null +++ b/python/src/ignition/gazebo/Util.cc @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include + +#include + +#include +#include "Util.hh" + +namespace ignition +{ +namespace gazebo +{ +namespace python +{ +void defineGazeboUtil(pybind11::module &_module) +{ + _module.def("world_entity", + pybind11::overload_cast( + &ignition::gazebo::worldEntity), + "Get the first world entity that's found."); +} +} // namespace python +} // namespace gazebo +} // namespace ignition diff --git a/python/src/ignition/gazebo/Util.hh b/python/src/ignition/gazebo/Util.hh new file mode 100644 index 0000000000..34db116688 --- /dev/null +++ b/python/src/ignition/gazebo/Util.hh @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2022 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IGNITION_GAZEBO_PYTHON__UTIL_HH_ +#define IGNITION_GAZEBO_PYTHON__UTIL_HH_ + +#include + +#include + +namespace ignition +{ +namespace gazebo +{ +namespace python +{ +/// Define a pybind11 wrapper for a ignition::gazebo::Util +/// \param[in] _module a pybind11 module to add the definition to +void defineGazeboUtil(pybind11::module &_module); +} // namespace python +} // namespace gazebo +} // namespace ignition + +#endif // IGNITION_GAZEBO_PYTHON__WORLD_HH_ diff --git a/python/src/ignition/gazebo/World.cc b/python/src/ignition/gazebo/World.cc new file mode 100644 index 0000000000..6822c12f8f --- /dev/null +++ b/python/src/ignition/gazebo/World.cc @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include + +#include + +#include "World.hh" + +namespace ignition +{ +namespace gazebo +{ +namespace python +{ +void defineGazeboWorld(pybind11::object module) +{ + pybind11::class_(module, "World") + .def(pybind11::init()) + .def( + "model_by_name", &ignition::gazebo::World::ModelByName, + "Get the ID of a model entity which is an immediate child of " + " this world.") + .def( + "gravity", &ignition::gazebo::World::Gravity, + "Get the gravity in m/s^2."); +} +} // namespace python +} // namespace gazebo +} // namespace ignition diff --git a/python/src/ignition/gazebo/World.hh b/python/src/ignition/gazebo/World.hh new file mode 100644 index 0000000000..e77e42b4b6 --- /dev/null +++ b/python/src/ignition/gazebo/World.hh @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IGNITION_GAZEBO_PYTHON__WORLD_HH_ +#define IGNITION_GAZEBO_PYTHON__WORLD_HH_ + +#include + +#include + +namespace ignition +{ +namespace gazebo +{ +namespace python +{ +/// Define a pybind11 wrapper for an ignition::gazebo::World +/** + * \param[in] module a pybind11 module to add the definition to + */ +void +defineGazeboWorld(pybind11::object module); +} // namespace python +} // namespace gazebo +} // namespace ignition + +#endif // IGNITION_GAZEBO_PYTHON__WORLD_HH_ diff --git a/python/src/ignition/gazebo/_ignition_gazebo_pybind11.cc b/python/src/ignition/gazebo/_ignition_gazebo_pybind11.cc new file mode 100644 index 0000000000..2444a16f02 --- /dev/null +++ b/python/src/ignition/gazebo/_ignition_gazebo_pybind11.cc @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "EntityComponentManager.hh" +#include "EventManager.hh" +#include "Server.hh" +#include "ServerConfig.hh" +#include "TestFixture.hh" +#include "UpdateInfo.hh" +#include "Util.hh" +#include "World.hh" + +PYBIND11_MODULE(gazebo, m) { + m.doc() = "Ignition Gazebo Python Library."; + + ignition::gazebo::python::defineGazeboEntityComponentManager(m); + ignition::gazebo::python::defineGazeboEventManager(m); + ignition::gazebo::python::defineGazeboServer(m); + ignition::gazebo::python::defineGazeboServerConfig(m); + ignition::gazebo::python::defineGazeboTestFixture(m); + ignition::gazebo::python::defineGazeboUpdateInfo(m); + ignition::gazebo::python::defineGazeboWorld(m); + ignition::gazebo::python::defineGazeboUtil(m); +} diff --git a/python/src/ignition/gazebo/wrap_functions.hh b/python/src/ignition/gazebo/wrap_functions.hh new file mode 100644 index 0000000000..2a60b93668 --- /dev/null +++ b/python/src/ignition/gazebo/wrap_functions.hh @@ -0,0 +1,323 @@ +// This code from copied from: +// https://github.com/RobotLocomotion/drake/blob/6ee5e9325821277a62bd5cd5456ccf02ca25dab7/bindings/pydrake/common/wrap_function.h +// It's under BSD 3-Clause License + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include +#include +#include + +namespace internal { + +// Collects both a functor object and its signature for ease of inference. +template +struct function_info { + // TODO(eric.cousineau): Ensure that this permits copy elision when combined + // with `std::forward(func)`, while still behaving well with primitive + // types. + std::decay_t func; +}; + +// Factory method for `function_info<>`, to be used by `infer_function_info`. +template +auto make_function_info(Func&& func, Return (*infer)(Args...) = nullptr) { + (void)infer; + return function_info{std::forward(func)}; +} + +// SFINAE for functors. +// N.B. This *only* distinguished between function / method pointers and +// lambda objects. It does *not* distinguish among other types. +template +using enable_if_lambda_t = + std::enable_if_t>::value, T>; + +// Infers `function_info<>` from a function pointer. +template +auto infer_function_info(Return (*func)(Args...)) { + return make_function_info(func); +} + +// Infers `function_info<>` from a mutable method pointer. +template +auto infer_function_info(Return (Class::*method)(Args...)) { + auto func = [method](Class* self, Args... args) { + return (self->*method)(std::forward(args)...); + }; + return make_function_info(func); +} + +// Infers `function_info<>` from a const method pointer. +template +auto infer_function_info(Return (Class::*method)(Args...) const) { + auto func = [method](const Class* self, Args... args) { + return (self->*method)(std::forward(args)...); + }; + return make_function_info(func); +} + +// Helpers for general functor objects. +struct functor_helpers { + // Removes class from mutable method pointer for inferring signature + // of functor. + template + static auto remove_class_from_ptr(Return (Class::*)(Args...)) { + using Ptr = Return (*)(Args...); + return Ptr{}; + } + + // Removes class from const method pointer for inferring signature of functor. + template + static auto remove_class_from_ptr(Return (Class::*)(Args...) const) { + using Ptr = Return (*)(Args...); + return Ptr{}; + } + + // Infers function pointer from functor. + // @pre `Func` must have only *one* overload of `operator()`. + template + static auto infer_function_ptr() { + return remove_class_from_ptr(&Func::operator()); + } +}; + +// Infers `function_info<>` from a generic functor. +template > +auto infer_function_info(Func&& func) { + return make_function_info(std::forward(func), + functor_helpers::infer_function_ptr>()); +} + +// Implementation for wrapping a function by scanning and replacing arguments +// based on their types. +template