Repository: duburcqa/jiminy (language: python)

Issue:
[python] Fix memory leak due to circular references
`atexit.register` is causing memory leak. Either use `weakref` or call `unregister` in the method that has been registered so that calling it manually break the circle. It happens at several places including [here](https://github.com/duburcqa/jiminy/blob/3e2036edff88fcf5b39c2c8149286be44388a4b8/python/jiminy_py/src/jiminy_py/viewer/meshcat/wrapper.py#L345)


Discussion / hints:

Fix patch:
diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml
index e6dca85b9..7f0f6eae7 100644
--- a/.github/workflows/macos.yml
+++ b/.github/workflows/macos.yml
@@ -31,7 +31,7 @@ jobs:
       CMAKE_C_COMPILER: "/usr/bin/clang"
       CMAKE_CXX_COMPILER: "/usr/bin/clang++"
       CMAKE_CXX_FLAGS: "-DEIGEN_MPL2_ONLY -Wno-deprecated-declarations"
-      MACOSX_DEPLOYMENT_TARGET: "10.15"
+      OSX_DEPLOYMENT_TARGET: "10.15"
       OSX_ARCHITECTURES: "x86_64;arm64"
       WHEEL_ARCH: "universal2"
 
@@ -64,7 +64,7 @@ jobs:
         "${PYTHON_EXECUTABLE}" -m pip install delocate twine
     - name: Build project dependencies
       run: |
-        MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OSX_ARCHITECTURES=${OSX_ARCHITECTURES} \
+        OSX_DEPLOYMENT_TARGET=${OSX_DEPLOYMENT_TARGET} OSX_ARCHITECTURES=${OSX_ARCHITECTURES} \
           BUILD_TYPE="${{ matrix.BUILD_TYPE }}" ./build_tools/build_install_deps_unix.sh
         "${PYTHON_EXECUTABLE}" -m pip install "torch==${TORCH_VERSION}" -f https://download.pytorch.org/whl/cpu
         "${PYTHON_EXECUTABLE}" -m pip install "gymnasium>=0.26,<0.29" "importlib-metadata>=3.3.0"
@@ -82,7 +82,7 @@ jobs:
         cmake "${RootDir}" -DCMAKE_INSTALL_PREFIX="${InstallDir}" -DCMAKE_PREFIX_PATH="${InstallDir}" \
               -DCMAKE_C_COMPILER="${CMAKE_C_COMPILER}" -DCMAKE_CXX_COMPILER="${CMAKE_CXX_COMPILER}" \
               -DCMAKE_OSX_ARCHITECTURES="${OSX_ARCHITECTURES}" \
-              -DCMAKE_OSX_DEPLOYMENT_TARGET="${MACOSX_DEPLOYMENT_TARGET}" \
+              -DCMAKE_OSX_DEPLOYMENT_TARGET="${OSX_DEPLOYMENT_TARGET}" \
               -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \
               -DBOOST_ROOT="${InstallDir}" -DBoost_INCLUDE_DIR="${InstallDir}/include" \
               -DBoost_NO_SYSTEM_PATHS=TRUE -DBoost_NO_BOOST_CMAKE=TRUE \
@@ -126,7 +126,7 @@ jobs:
         delocate-wheel -vvv --require-archs "${WHEEL_ARCH}" \
                        -w "${RootDir}/build/wheelhouse" "${RootDir}/build/pypi/dist/jiminy_py/"*.whl
         "${PYTHON_EXECUTABLE}" "${RootDir}/build_tools/wheel_addplat_macos.py" -vvv --rm-orig --clobber \
-            -p "macosx_${MACOSX_DEPLOYMENT_TARGET//./_}_${WHEEL_ARCH}" "${RootDir}/build/wheelhouse/"*.whl
+            -p "macosx_${OSX_DEPLOYMENT_TARGET//./_}_${WHEEL_ARCH}" "${RootDir}/build/wheelhouse/"*.whl
         "${PYTHON_EXECUTABLE}" -m pip install --force-reinstall --no-deps "${RootDir}/build/wheelhouse/"*.whl
     - name: Upload the wheel for Linux of jiminy_py
       if: matrix.BUILD_TYPE == 'Release'
diff --git a/build_tools/build_install_deps_unix.sh b/build_tools/build_install_deps_unix.sh
index abfab96ee..b89f3cf27 100755
--- a/build_tools/build_install_deps_unix.sh
+++ b/build_tools/build_install_deps_unix.sh
@@ -10,14 +10,14 @@ if [ -z ${BUILD_TYPE} ]; then
 fi
 
 ### Set the macos sdk version min if undefined
-if [ -z ${MACOSX_DEPLOYMENT_TARGET} ]; then
-  MACOSX_DEPLOYMENT_TARGET="10.15"
-  echo "MACOSX_DEPLOYMENT_TARGET is unset. Defaulting to '${MACOSX_DEPLOYMENT_TARGET}'."
+if [ -z ${OSX_DEPLOYMENT_TARGET} ]; then
+  OSX_DEPLOYMENT_TARGET="10.15"
+  echo "OSX_DEPLOYMENT_TARGET is unset. Defaulting to '${OSX_DEPLOYMENT_TARGET}'."
 fi
 
 ### Set the build architecture if undefined
 if [ -z ${OSX_ARCHITECTURES} ]; then
-  OSX_ARCHITECTURES="x86_64"
+  OSX_ARCHITECTURES="arm64"
   echo "OSX_ARCHITECTURES is unset. Defaulting to '${OSX_ARCHITECTURES}'."
 fi
 
@@ -32,6 +32,8 @@ if [ -z ${CMAKE_CXX_COMPILER} ]; then
 fi
 
 ### Set common CMAKE_C/CXX_FLAGS
+#   '_LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION' flag is required to compile `boost::container_hash::hash`
+#   with C++17 enabled from "old" releases of Boost.
 CMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} -D_LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION -Wno-deprecated-declarations"
 if [ "${BUILD_TYPE}" == "Release" ]; then
   CMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} -DNDEBUG -O3"
@@ -43,8 +45,8 @@ fi
 echo "CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}"
 
 ### Get the fullpath of Jiminy project
-ScriptDir="$(cd "$(dirname "$0")" >/dev/null 2>&1 && pwd)"
-RootDir="$(dirname $ScriptDir)"
+ScriptDir="$(cd "$(dirname -- $0)" >/dev/null 2>&1 && pwd)"
+RootDir="$(dirname -- ${ScriptDir})"
 
 ### Set the fullpath of the install directory, then creates it
 InstallDir="${RootDir}/install"
@@ -79,7 +81,7 @@ unset Boost_ROOT
 #   - Boost.Python == 1.75 fixes support of PyPy
 #   - Boost.Python == 1.76 fixes error handling at import
 #   - Boost >= 1.75 is required to compile ouf-of-the-box on MacOS for intel and Apple Silicon
-#   - Boost < 1.77 causes compilation failure with gcc-12.
+#   - Boost < 1.77 causes compilation failure with gcc-12 if not patched
 #   - Boost >= 1.77 affects the memory layout to improve alignment, breaking retro-compatibility
 if [ ! -d "${RootDir}/boost" ]; then
   git clone --depth 1 https://github.com/boostorg/boost.git "${RootDir}/boost"
@@ -206,6 +208,9 @@ git apply --reject --whitespace=fix "${RootDir}/build_tools/patch_deps_unix/pino
 #   * Set the cmake cache variable CMAKE_PREFIX_PATH
 #   * Set the environment variable Boost_DIR
 
+# Must add toolset to search path for boost build system to find it
+PATH="${PATH}:$(dirname -- ${CMAKE_C_COMPILER})"
+
 ### Build and install the build tool b2 (build-ception !)
 cd "${RootDir}/boost"
 ./bootstrap.sh --prefix="${InstallDir}" --with-python="${PYTHON_EXECUTABLE}"
@@ -234,7 +239,7 @@ else
 fi
 CMAKE_CXX_FLAGS_B2="-fPIC -std=c++11"
 if [ "${OSTYPE//[0-9.]/}" == "darwin" ]; then
-  CMAKE_CXX_FLAGS_B2="${CMAKE_CXX_FLAGS_B2} -mmacosx-version-min=${MACOSX_DEPLOYMENT_TARGET}"
+  CMAKE_CXX_FLAGS_B2="${CMAKE_CXX_FLAGS_B2} -mmacosx-version-min=${OSX_DEPLOYMENT_TARGET}"
 fi
 if grep -q ";" <<< "${OSX_ARCHITECTURES}" ; then
   CMAKE_CXX_FLAGS_B2="${CMAKE_CXX_FLAGS_B2} $(echo "-arch ${OSX_ARCHITECTURES}" | sed "s/;/ -arch /g")"
@@ -249,7 +254,7 @@ mkdir -p "${RootDir}/boost/build"
      architecture= address-model=64 $DebugOptionsB2 \
      threading=single link=static runtime-link=static \
      cxxflags="${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_B2}" \
-     linkflags="${CMAKE_CXX_FLAGS_B2}" \
+     linkflags="${CMAKE_CXX_FLAGS_B2}" toolset="$(basename -- ${CMAKE_C_COMPILER})" \
      variant="$BuildTypeB2" install -q -d0 -j2
 
 ./b2 --prefix="${InstallDir}" --build-dir="${RootDir}/boost/build" \
@@ -258,7 +263,7 @@ mkdir -p "${RootDir}/boost/build"
      architecture= address-model=64 $DebugOptionsB2 \
      threading=single link=shared runtime-link=shared \
      cxxflags="${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_B2}" \
-     linkflags="${CMAKE_CXX_FLAGS_B2}" \
+     linkflags="${CMAKE_CXX_FLAGS_B2}" toolset="$(basename -- ${CMAKE_C_COMPILER})" \
      variant="$BuildTypeB2" install -q -d0 -j2
 
 #################################### Build and install eigen3 ##########################################
@@ -277,7 +282,7 @@ cd "${RootDir}/eigenpy/build"
 cmake "${RootDir}/eigenpy" -Wno-dev -DCMAKE_CXX_STANDARD=17 \
       -DCMAKE_C_COMPILER="${CMAKE_C_COMPILER}" -DCMAKE_CXX_COMPILER="${CMAKE_CXX_COMPILER}" \
       -DCMAKE_INSTALL_PREFIX="${InstallDir}" -DCMAKE_PREFIX_PATH="${InstallDir}" \
-      -DCMAKE_OSX_ARCHITECTURES="${OSX_ARCHITECTURES}" -DCMAKE_OSX_DEPLOYMENT_TARGET="${MACOSX_DEPLOYMENT_TARGET}" \
+      -DCMAKE_OSX_ARCHITECTURES="${OSX_ARCHITECTURES}" -DCMAKE_OSX_DEPLOYMENT_TARGET="${OSX_DEPLOYMENT_TARGET}" \
       -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
       -DBUILD_SHARED_LIBS=ON -DBUILD_STATIC_LIBS=OFF \
       -DPYTHON_EXECUTABLE="$PYTHON_EXECUTABLE" -DPYTHON_STANDARD_LAYOUT=ON \
@@ -295,12 +300,11 @@ cd "${RootDir}/tinyxml/build"
 cmake "${RootDir}/tinyxml" -Wno-dev -DCMAKE_CXX_STANDARD=17 \
       -DCMAKE_C_COMPILER="${CMAKE_C_COMPILER}" -DCMAKE_CXX_COMPILER="${CMAKE_CXX_COMPILER}" \
       -DCMAKE_INSTALL_PREFIX="${InstallDir}" \
-      -DCMAKE_OSX_ARCHITECTURES="${OSX_ARCHITECTURES}" -DCMAKE_OSX_DEPLOYMENT_TARGET="${MACOSX_DEPLOYMENT_TARGET}" \
+      -DCMAKE_OSX_ARCHITECTURES="${OSX_ARCHITECTURES}" -DCMAKE_OSX_DEPLOYMENT_TARGET="${OSX_DEPLOYMENT_TARGET}" \
       -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
       -DBUILD_SHARED_LIBS=OFF -DBUILD_STATIC_LIBS=ON \
       -DCMAKE_CXX_FLAGS_RELEASE_INIT="" -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} -DTIXML_USE_STL" \
       -DCMAKE_BUILD_TYPE="$BUILD_TYPE"
-
 make install -j2
 
 ############################## Build and install console_bridge ########################################
@@ -310,7 +314,7 @@ cd "${RootDir}/console_bridge/build"
 cmake "${RootDir}/console_bridge" -Wno-dev -DCMAKE_CXX_STANDARD=17 \
       -DCMAKE_C_COMPILER="${CMAKE_C_COMPILER}" -DCMAKE_CXX_COMPILER="${CMAKE_CXX_COMPILER}" \
       -DCMAKE_INSTALL_PREFIX="${InstallDir}" \
-      -DCMAKE_OSX_ARCHITECTURES="${OSX_ARCHITECTURES}" -DCMAKE_OSX_DEPLOYMENT_TARGET="${MACOSX_DEPLOYMENT_TARGET}" \
+      -DCMAKE_OSX_ARCHITECTURES="${OSX_ARCHITECTURES}" -DCMAKE_OSX_DEPLOYMENT_TARGET="${OSX_DEPLOYMENT_TARGET}" \
       -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
       -DBUILD_SHARED_LIBS=OFF -DBUILD_STATIC_LIBS=ON \
       -DCMAKE_CXX_FLAGS_RELEASE_INIT="" -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS}" -DCMAKE_BUILD_TYPE="$BUILD_TYPE"
@@ -330,7 +334,7 @@ cd "${RootDir}/urdfdom/build"
 cmake "${RootDir}/urdfdom" -Wno-dev -DCMAKE_CXX_STANDARD=17 \
       -DCMAKE_C_COMPILER="${CMAKE_C_COMPILER}" -DCMAKE_CXX_COMPILER="${CMAKE_CXX_COMPILER}" \
       -DCMAKE_INSTALL_PREFIX="${InstallDir}" -DCMAKE_PREFIX_PATH="${InstallDir}" \
-      -DCMAKE_OSX_ARCHITECTURES="${OSX_ARCHITECTURES}" -DCMAKE_OSX_DEPLOYMENT_TARGET="${MACOSX_DEPLOYMENT_TARGET}" \
+      -DCMAKE_OSX_ARCHITECTURES="${OSX_ARCHITECTURES}" -DCMAKE_OSX_DEPLOYMENT_TARGET="${OSX_DEPLOYMENT_TARGET}" \
       -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
       -DBUILD_SHARED_LIBS=OFF -DBUILD_STATIC_LIBS=ON -DBUILD_TESTING=OFF \
       -DCMAKE_CXX_FLAGS_RELEASE_INIT="" -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS}" -DCMAKE_BUILD_TYPE="$BUILD_TYPE"
@@ -343,7 +347,7 @@ cd "${RootDir}/cppad/build"
 cmake "${RootDir}/cppad" -Wno-dev -DCMAKE_CXX_STANDARD=17 \
       -DCMAKE_C_COMPILER="${CMAKE_C_COMPILER}" -DCMAKE_CXX_COMPILER="${CMAKE_CXX_COMPILER}" \
       -DCMAKE_INSTALL_PREFIX="${InstallDir}" \
-      -DCMAKE_OSX_ARCHITECTURES="${OSX_ARCHITECTURES}" -DCMAKE_OSX_DEPLOYMENT_TARGET="${MACOSX_DEPLOYMENT_TARGET}" \
+      -DCMAKE_OSX_ARCHITECTURES="${OSX_ARCHITECTURES}" -DCMAKE_OSX_DEPLOYMENT_TARGET="${OSX_DEPLOYMENT_TARGET}" \
       -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
       -DCMAKE_CXX_FLAGS_RELEASE_INIT="" -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS}" -DCMAKE_BUILD_TYPE="$BUILD_TYPE"
 make install -j2
@@ -356,7 +360,7 @@ cd "${RootDir}/cppadcodegen/build"
 cmake "${RootDir}/cppadcodegen" -Wno-dev -DCMAKE_CXX_STANDARD=17 \
       -DCMAKE_C_COMPILER="${CMAKE_C_COMPILER}" -DCMAKE_CXX_COMPILER="${CMAKE_CXX_COMPILER}" \
       -DCMAKE_INSTALL_PREFIX="${InstallDir}" \
-      -DCMAKE_OSX_ARCHITECTURES="${OSX_ARCHITECTURES}" -DCMAKE_OSX_DEPLOYMENT_TARGET="${MACOSX_DEPLOYMENT_TARGET}" \
+      -DCMAKE_OSX_ARCHITECTURES="${OSX_ARCHITECTURES}" -DCMAKE_OSX_DEPLOYMENT_TARGET="${OSX_DEPLOYMENT_TARGET}" \
       -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
       -DGOOGLETEST_GIT=ON \
       -DCMAKE_CXX_FLAGS_RELEASE_INIT="" -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS}" -DCMAKE_BUILD_TYPE="$BUILD_TYPE"
@@ -371,14 +375,14 @@ cd "${RootDir}/assimp/build"
 cmake "${RootDir}/assimp" -Wno-dev -DCMAKE_CXX_STANDARD=17 \
       -DCMAKE_C_COMPILER="${CMAKE_C_COMPILER}" -DCMAKE_CXX_COMPILER="${CMAKE_CXX_COMPILER}" \
       -DCMAKE_INSTALL_PREFIX="${InstallDir}" \
-      -DCMAKE_OSX_ARCHITECTURES="${OSX_ARCHITECTURES}" -DCMAKE_OSX_DEPLOYMENT_TARGET="${MACOSX_DEPLOYMENT_TARGET}" \
+      -DCMAKE_OSX_ARCHITECTURES="${OSX_ARCHITECTURES}" -DCMAKE_OSX_DEPLOYMENT_TARGET="${OSX_DEPLOYMENT_TARGET}" \
       -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
       -DBUILD_SHARED_LIBS=OFF -DBUILD_STATIC_LIBS=ON \
       -DASSIMP_BUILD_ASSIMP_TOOLS=OFF -DASSIMP_BUILD_ZLIB=ON -DASSIMP_BUILD_TESTS=OFF \
       -DASSIMP_BUILD_SAMPLES=OFF -DBUILD_DOCS=OFF -DBUILD_TESTING=OFF \
       -DCMAKE_C_FLAGS="${CMAKE_CXX_FLAGS} -DHAVE_HIDDEN" -DCMAKE_CXX_FLAGS_RELEASE_INIT="" \
       -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} -Wno-strict-overflow -Wno-tautological-compare -Wno-array-compare $(
-      ) -Wno-alloc-size-larger-than -Wno-unknown-warning-option -Wno-unknown-warning" \
+      ) -Wno-alloc-size-larger-than -Wno-unknown-warning-option -Wno-unknown-warning -Wno-error=array-bounds" \
       -DCMAKE_BUILD_TYPE="$BUILD_TYPE"
 make install -j2
 
@@ -389,7 +393,7 @@ cd "${RootDir}/hpp-fcl/third-parties/qhull/build"
 cmake "${RootDir}/hpp-fcl/third-parties/qhull" -Wno-dev -DCMAKE_CXX_STANDARD=17 \
       -DCMAKE_C_COMPILER="${CMAKE_C_COMPILER}" -DCMAKE_CXX_COMPILER="${CMAKE_CXX_COMPILER}" \
       -DCMAKE_INSTALL_PREFIX="${InstallDir}" \
-      -DCMAKE_OSX_ARCHITECTURES="${OSX_ARCHITECTURES}" -DCMAKE_OSX_DEPLOYMENT_TARGET="${MACOSX_DEPLOYMENT_TARGET}" \
+      -DCMAKE_OSX_ARCHITECTURES="${OSX_ARCHITECTURES}" -DCMAKE_OSX_DEPLOYMENT_TARGET="${OSX_DEPLOYMENT_TARGET}" \
       -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
       -DBUILD_SHARED_LIBS=OFF -DBUILD_STATIC_LIBS=ON \
       -DCMAKE_C_FLAGS="${CMAKE_CXX_FLAGS}" -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} -Wno-conversion" \
@@ -401,7 +405,7 @@ cd "${RootDir}/hpp-fcl/build"
 cmake "${RootDir}/hpp-fcl" -Wno-dev -DCMAKE_CXX_STANDARD=17 \
       -DCMAKE_C_COMPILER="${CMAKE_C_COMPILER}" -DCMAKE_CXX_COMPILER="${CMAKE_CXX_COMPILER}" \
       -DCMAKE_INSTALL_PREFIX="${InstallDir}" -DCMAKE_PREFIX_PATH="${InstallDir}" \
-      -DCMAKE_OSX_ARCHITECTURES="${OSX_ARCHITECTURES}" -DCMAKE_OSX_DEPLOYMENT_TARGET="${MACOSX_DEPLOYMENT_TARGET}" \
+      -DCMAKE_OSX_ARCHITECTURES="${OSX_ARCHITECTURES}" -DCMAKE_OSX_DEPLOYMENT_TARGET="${OSX_DEPLOYMENT_TARGET}" \
       -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
       -DBUILD_SHARED_LIBS=ON -DBUILD_STATIC_LIBS=OFF \
       -DPYTHON_EXECUTABLE="$PYTHON_EXECUTABLE" -DPYTHON_STANDARD_LAYOUT=ON \
@@ -423,7 +427,7 @@ cd "${RootDir}/pinocchio/build"
 cmake "${RootDir}/pinocchio" -Wno-dev -DCMAKE_CXX_STANDARD=17 \
       -DCMAKE_C_COMPILER="${CMAKE_C_COMPILER}" -DCMAKE_CXX_COMPILER="${CMAKE_CXX_COMPILER}" \
       -DCMAKE_INSTALL_PREFIX="${InstallDir}" -DCMAKE_PREFIX_PATH="${InstallDir}" \
-      -DCMAKE_OSX_ARCHITECTURES="${OSX_ARCHITECTURES}" -DCMAKE_OSX_DEPLOYMENT_TARGET="${MACOSX_DEPLOYMENT_TARGET}" \
+      -DCMAKE_OSX_ARCHITECTURES="${OSX_ARCHITECTURES}" -DCMAKE_OSX_DEPLOYMENT_TARGET="${OSX_DEPLOYMENT_TARGET}" \
       -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
       -DBUILD_SHARED_LIBS=ON -DBUILD_STATIC_LIBS=OFF \
       -DPYTHON_EXECUTABLE="$PYTHON_EXECUTABLE" -DPYTHON_STANDARD_LAYOUT=ON \
diff --git a/build_tools/cmake/base.cmake b/build_tools/cmake/base.cmake
index 0e7a7e0d2..124ea2b31 100644
--- a/build_tools/cmake/base.cmake
+++ b/build_tools/cmake/base.cmake
@@ -55,7 +55,7 @@ else()
                    -Wno-unknown-warning -Wno-undefined-var-template \
                    -Wno-long-long -Wno-error=maybe-uninitialized \
                    -Wno-error=uninitialized -Wno-error=deprecated \
-                   -Wno-error=array-bounds")
+                   -Wno-error=array-bounds -Wno-error=redundant-move")
 endif()
 
 # Shared libraries need PIC
diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt
index f963b1735..776bdbdd3 100644
--- a/core/CMakeLists.txt
+++ b/core/CMakeLists.txt
@@ -114,7 +114,7 @@ target_include_directories(${PROJECT_NAME}-object PUBLIC
 )
 
 # Configure jsoncpp and hdf5 dependencies.
-# Note that defining only BUILD_INTERFACE will prevent the header files to be exported,
+# Note that defining only BUILD_INTERFACE will prevent the header files from being exported,
 # thus not making them available by a side-project depending on Jiminy. However, since
 # jsoncpp is installed on the system as an external project, those side-projects must
 # simply call find_package(jsoncpp) and it will be included automatically.
@@ -124,7 +124,7 @@ target_include_directories(${PROJECT_NAME}-object SYSTEM PUBLIC
     $<BUILD_INTERFACE:${INSTALL_DIR}/include>
 )
 
-# Linking with other libraries (in such a way to avoid any warnings compiling them)
+# Linking with other libraries (as a way to avoid any warnings compiling them)
 target_link_libraries_system(${PROJECT_NAME}-object pinocchio::pinocchio hpp-fcl::hpp-fcl Eigen3::Eigen)
 if(NOT "${urdfdom_LIBRARIES}" MATCHES ".*tinyxml.*")
     list(APPEND urdfdom_LIBRARIES "tinyxml")
@@ -133,7 +133,7 @@ target_link_libraries(${PROJECT_NAME}-object ${urdfdom_LIBRARIES})
 target_link_libraries(${PROJECT_NAME}-object jsoncpp::jsoncpp hdf5::hdf5_cpp hdf5::hdf5 hdf5::zlib)  # Beware the order is critical !
 if(WIN32)
     # Adding this dependency on Windows is necessary since HDF5 >= 1.14.3.
-    # Hopefully this broken transitive dependency when build static libary
+    # Hopefully this broken transitive dependency when building static libary
     # will be fixed. See: https://github.com/HDFGroup/hdf5/issues/3663
     target_link_libraries(${PROJECT_NAME}-object shlwapi)
 endif()
@@ -143,7 +143,7 @@ if(UNIX AND NOT APPLE)
     target_link_libraries(${PROJECT_NAME}-object ${CMAKE_DL_LIBS} -lrt)
 endif()
 
-# Create both static and shared libraries from intermediary object
+# Create both static and shared libraries from the intermediary object
 add_library(${PROJECT_NAME} STATIC $<TARGET_OBJECTS:${PROJECT_NAME}-object>)
 target_link_libraries(${PROJECT_NAME} ${PROJECT_NAME}-object)
 add_library(${LIBRARY_NAME} SHARED $<TARGET_OBJECTS:${PROJECT_NAME}-object>)
@@ -171,7 +171,7 @@ if(BUILD_EXAMPLES)
     add_subdirectory(examples)
 endif()
 
-# Specialize jiminy core configuration file and install it
+# Specialize Jiminy core configuration file and install it
 set(JIMINY_CONFIG_IN "${CMAKE_SOURCE_DIR}/build_tools/cmake/jiminyConfig.cmake.in")
 set(JIMINY_CONFIG_OUT "${CMAKE_BINARY_DIR}/cmake/jiminyConfig.cmake")
 configure_file(${JIMINY_CONFIG_IN} ${JIMINY_CONFIG_OUT} @ONLY)
diff --git a/core/examples/double_pendulum/double_pendulum.cc b/core/examples/double_pendulum/double_pendulum.cc
index 897e7f79d..378cff4b3 100644
--- a/core/examples/double_pendulum/double_pendulum.cc
+++ b/core/examples/double_pendulum/double_pendulum.cc
@@ -7,9 +7,10 @@
 #include <filesystem>
 
 #include "jiminy/core/fwd.h"
+#include "jiminy/core/hardware/fwd.h"
+#include "jiminy/core/telemetry/fwd.h"
 #include "jiminy/core/utilities/helpers.h"
 #include "jiminy/core/io/file_device.h"
-#include "jiminy/core/telemetry/telemetry_recorder.h"
 #include "jiminy/core/hardware/abstract_sensor.h"
 #include "jiminy/core/hardware/basic_motors.h"
 #include "jiminy/core/control/controller_functor.h"
diff --git a/core/examples/external_project/double_pendulum.cc b/core/examples/external_project/double_pendulum.cc
index 78ffee9d6..776fb51f5 100644
--- a/core/examples/external_project/double_pendulum.cc
+++ b/core/examples/external_project/double_pendulum.cc
@@ -7,9 +7,10 @@
 #include <filesystem>
 
 #include "jiminy/core/fwd.h"
+#include "jiminy/core/hardware/fwd.h"
+#include "jiminy/core/telemetry/fwd.h"
 #include "jiminy/core/utilities/helpers.h"
 #include "jiminy/core/io/file_device.h"
-#include "jiminy/core/telemetry/telemetry_recorder.h"
 #include "jiminy/core/hardware/abstract_sensor.h"
 #include "jiminy/core/hardware/basic_motors.h"
 #include "jiminy/core/control/controller_functor.h"
diff --git a/core/include/jiminy/core/constraints/abstract_constraint.h b/core/include/jiminy/core/constraints/abstract_constraint.h
index 2f2ccd0e2..ae5af5817 100644
--- a/core/include/jiminy/core/constraints/abstract_constraint.h
+++ b/core/include/jiminy/core/constraints/abstract_constraint.h
@@ -22,7 +22,7 @@ namespace jiminy
         DISABLE_COPY(AbstractConstraintBase)
 
     public:
-        AbstractConstraintBase() = default;
+        explicit AbstractConstraintBase() = default;
         virtual ~AbstractConstraintBase();
 
         /// \brief Refresh the internal buffers and proxies.
@@ -77,25 +77,25 @@ namespace jiminy
 
     public:
         /// \brief Lambda multipliers.
-        Eigen::VectorXd lambda_;
+        Eigen::VectorXd lambda_{};
 
     protected:
         /// \brief Model on which the constraint operates.
-        std::weak_ptr<const Model> model_;
+        std::weak_ptr<const Model> model_{};
         /// \brief Flag to indicate whether the constraint has been attached to a model.
-        bool isAttached_;
+        bool isAttached_{false};
         /// \brief Flag to indicate whether the constraint is enabled.
         ///
         /// \remarks Handling of this flag is done at Robot level.
-        bool isEnabled_;
+        bool isEnabled_{false};
         /// \brief Position-related baumgarte stabilization gain.
-        double kp_;
+        double kp_{0.0};
         /// \brief Velocity-related baumgarte stabilization gain.
-        double kd_;
+        double kd_{0.0};
         /// \brief Jacobian of the constraint.
-        Eigen::MatrixXd jacobian_;
+        Eigen::MatrixXd jacobian_{};
         /// \brief Drift of the constraint.
-        Eigen::VectorXd drift_;
+        Eigen::VectorXd drift_{};
     };
 
     template<class T>
diff --git a/core/include/jiminy/core/constraints/distance_constraint.h b/core/include/jiminy/core/constraints/distance_constraint.h
index 3d0b0cccc..d612a17fe 100644
--- a/core/include/jiminy/core/constraints/distance_constraint.h
+++ b/core/include/jiminy/core/constraints/distance_constraint.h
@@ -20,15 +20,15 @@ namespace jiminy
         auto shared_from_this() { return shared_from(this); }
 
     public:
-        DistanceConstraint(const std::string & firstFrameName,
-                           const std::string & secondFrameName);
+        explicit DistanceConstraint(const std::string & firstFrameName,
+                                    const std::string & secondFrameName) noexcept;
         virtual ~DistanceConstraint() = default;
 
-        const std::vector<std::string> & getFramesNames() const;
-        const std::vector<pinocchio::FrameIndex> & getFramesIdx() const;
+        const std::array<std::string, 2> & getFramesNames() const noexcept;
+        const std::array<pinocchio::FrameIndex, 2> & getFramesIdx() const noexcept;
 
         hresult_t setReferenceDistance(double distanceRef);
-        double getReferenceDistance() const;
+        double getReferenceDistance() const noexcept;
 
         virtual hresult_t reset(const Eigen::VectorXd & q,
                                 const Eigen::VectorXd & v) override final;
@@ -38,15 +38,13 @@ namespace jiminy
 
     private:
         /// \brief Names of the frames on which the constraint operates.
-        std::vector<std::string> framesNames_;
+        std::array<std::string, 2> frameNames_;
         /// \brief Corresponding frames indices.
-        std::vector<pinocchio::FrameIndex> framesIdx_;
+        std::array<pinocchio::FrameIndex, 2> frameIndices_{{0, 0}};
         /// \brief Reference Distance between the frames
-        double distanceRef_;
-        /// \brief Stores first frame jacobian in world.
-        Matrix6Xd firstFrameJacobian_;
-        /// \brief Stores second frame jacobian in world.
-        Matrix6Xd secondFrameJacobian_;
+        double distanceRef_{0.0};
+        /// \brief Stores frame jacobians in world.
+        std::array<Matrix6Xd, 2> frameJacobians_{};
     };
 }
 
diff --git a/core/include/jiminy/core/constraints/frame_constraint.h b/core/include/jiminy/core/constraints/frame_constraint.h
index 21fcc7824..a2e2ad8b1 100644
--- a/core/include/jiminy/core/constraints/frame_constraint.h
+++ b/core/include/jiminy/core/constraints/frame_constraint.h
@@ -23,21 +23,21 @@ namespace jiminy
     public:
         /// \param[in] frameName Name of the frame on which the constraint is to be
         /// applied.
-        FrameConstraint(
-            const std::string & frameName,
-            const std::array<bool, 6> & maskDoFs = {{true, true, true, true, true, true}});
+        explicit FrameConstraint(const std::string & frameName,
+                                 const std::array<bool, 6> & maskDoFs = {
+                                     {true, true, true, true, true, true}}) noexcept;
         virtual ~FrameConstraint() = default;
 
-        const std::string & getFrameName() const;
-        pinocchio::FrameIndex getFrameIdx() const;
+        const std::string & getFrameName() const noexcept;
+        pinocchio::FrameIndex getFrameIdx() const noexcept;
 
-        const std::vector<uint32_t> & getDofsFixed() const;
+        const std::vector<uint32_t> & getDofsFixed() const noexcept;
 
-        void setReferenceTransform(const pinocchio::SE3 & transformRef);
-        const pinocchio::SE3 & getReferenceTransform() const;
+        void setReferenceTransform(const pinocchio::SE3 & transformRef) noexcept;
+        const pinocchio::SE3 & getReferenceTransform() const noexcept;
 
-        void setNormal(const Eigen::Vector3d & normal);
-        const Eigen::Matrix3d & getLocalFrame() const;
+        void setNormal(const Eigen::Vector3d & normal) noexcept;
+        const Eigen::Matrix3d & getLocalFrame() const noexcept;
 
         virtual hresult_t reset(const Eigen::VectorXd & q,
                                 const Eigen::VectorXd & v) override final;
@@ -49,19 +49,19 @@ namespace jiminy
         /// \brief Name of the frame on which the constraint operates.
         const std::string frameName_;
         /// \brief Corresponding frame index.
-        pinocchio::FrameIndex frameIdx_;
+        pinocchio::FrameIndex frameIdx_{0};
         /// \brief Degrees of freedom to fix.
         std::vector<uint32_t> dofsFixed_;
         /// \brief Reference pose of the frame to enforce.
-        pinocchio::SE3 transformRef_;
+        pinocchio::SE3 transformRef_{};
         /// \brief Normal direction locally at the interface.
-        Eigen::Vector3d normal_;
+        Eigen::Vector3d normal_{};
         /// \brief Rotation matrix of the local frame in which to apply masking
-        Eigen::Matrix3d rotationLocal_;
+        Eigen::Matrix3d rotationLocal_{Eigen::Matrix3d::Identity()};
         /// \brief Stores full frame jacobian in reference frame.
-        Matrix6Xd frameJacobian_;
+        Matrix6Xd frameJacobian_{};
         /// \brief Stores full frame drift in reference frame.
-        pinocchio::Motion frameDrift_;
+        pinocchio::Motion frameDrift_{};
     };
 }  // namespace jiminy
 
diff --git a/core/include/jiminy/core/constraints/joint_constraint.h b/core/include/jiminy/core/constraints/joint_constraint.h
index 28a05f5d8..24a635e11 100644
--- a/core/include/jiminy/core/constraints/joint_constraint.h
+++ b/core/include/jiminy/core/constraints/joint_constraint.h
@@ -21,17 +21,17 @@ namespace jiminy
 
     public:
         /// \param[in] jointName Name of the joint.
-        JointConstraint(const std::string & jointName);
+        explicit JointConstraint(const std::string & jointName) noexcept;
         virtual ~JointConstraint() = default;
 
-        const std::string & getJointName() const;
-        pinocchio::JointIndex getJointIdx() const;
+        const std::string & getJointName() const noexcept;
+        pinocchio::JointIndex getJointModelIdx() const noexcept;
 
-        void setReferenceConfiguration(const Eigen::VectorXd & configurationRef);
-        const Eigen::VectorXd & getReferenceConfiguration() const;
+        void setReferenceConfiguration(const Eigen::VectorXd & configurationRef) noexcept;
+        const Eigen::VectorXd & getReferenceConfiguration() const noexcept;
 
-        void setRotationDir(bool isReversed);
-        bool getRotationDir();
+        void setRotationDir(bool isReversed) noexcept;
+        bool getRotationDir() noexcept;
 
         virtual hresult_t reset(const Eigen::VectorXd & q,
                                 const Eigen::VectorXd & v) override final;
@@ -43,11 +43,11 @@ namespace jiminy
         /// \brief Name of the joint on which the constraint operates.
         std::string jointName_;
         /// \brief Corresponding joint index.
-        pinocchio::JointIndex jointIdx_;
+        pinocchio::JointIndex jointModelIdx_{0};
         /// \brief Reference position of the joint to enforce.
-        Eigen::VectorXd configurationRef_;
+        Eigen::VectorXd configurationRef_{};
         /// \brief Whether to reverse the sign of the constraint.
-        bool isReversed_;
+        bool isReversed_{false};
     };
 }
 
diff --git a/core/include/jiminy/core/constraints/sphere_constraint.h b/core/include/jiminy/core/constraints/sphere_constraint.h
index 54f1ac954..881bb504f 100644
--- a/core/include/jiminy/core/constraints/sphere_constraint.h
+++ b/core/include/jiminy/core/constraints/sphere_constraint.h
@@ -29,14 +29,14 @@ namespace jiminy
         /// \param[in] groundNormal Normal to the ground in the world as a unit vector.
         SphereConstraint(const std::string & frameName,
                          double sphereRadius,
-                         const Eigen::Vector3d & groundNormal = Eigen::Vector3d::UnitZ());
+                         const Eigen::Vector3d & groundNormal = Eigen::Vector3d::UnitZ()) noexcept;
         virtual ~SphereConstraint() = default;
 
-        const std::string & getFrameName() const;
-        pinocchio::FrameIndex getFrameIdx() const;
+        const std::string & getFrameName() const noexcept;
+        pinocchio::FrameIndex getFrameIdx() const noexcept;
 
-        void setReferenceTransform(const pinocchio::SE3 & transformRef);
-        const pinocchio::SE3 & getReferenceTransform() const;
+        void setReferenceTransform(const pinocchio::SE3 & transformRef) noexcept;
+        const pinocchio::SE3 & getReferenceTransform() const noexcept;
 
         virtual hresult_t reset(const Eigen::VectorXd & /* q */,
                                 const Eigen::VectorXd & /* v */) override final;
@@ -48,17 +48,17 @@ namespace jiminy
         /// \brief Name of the frame on which the constraint operates.
         std::string frameName_;
         /// \brief Corresponding frame index.
-        pinocchio::FrameIndex frameIdx_;
+        pinocchio::FrameIndex frameIdx_{0};
         /// \brief Sphere radius.
         double radius_;
         /// \brief Ground normal, world frame.
         Eigen::Vector3d normal_;
         /// \brief Skew of ground normal, in world frame, scaled by radius.
-        Eigen::Matrix3d skewRadius_;
+        Eigen::Matrix3d skewRadius_{pinocchio::alphaSkew(radius_, normal_)};
         /// \brief Reference pose of the frame to enforce.
-        pinocchio::SE3 transformRef_;
+        pinocchio::SE3 transformRef_{};
         /// \brief Stores full frame jacobian in world.
-        Matrix6Xd frameJacobian_;
+        Matrix6Xd frameJacobian_{};
     };
 }
 
diff --git a/core/include/jiminy/core/constraints/wheel_constraint.h b/core/include/jiminy/core/constraints/wheel_constraint.h
index 91ebe2321..d2ddc348b 100644
--- a/core/include/jiminy/core/constraints/wheel_constraint.h
+++ b/core/include/jiminy/core/constraints/wheel_constraint.h
@@ -31,14 +31,14 @@ namespace jiminy
         WheelConstraint(const std::string & frameName,
                         double wheelRadius,
                         const Eigen::Vector3d & groundNormal,
-                        const Eigen::Vector3d & wheelAxis);
+                        const Eigen::Vector3d & wheelAxis) noexcept;
         virtual ~WheelConstraint() = default;
 
-        const std::string & getFrameName() const;
-        pinocchio::FrameIndex getFrameIdx() const;
+        const std::string & getFrameName() const noexcept;
+        pinocchio::FrameIndex getFrameIdx() const noexcept;
 
-        void setReferenceTransform(const pinocchio::SE3 & transformRef);
-        const pinocchio::SE3 & getReferenceTransform() const;
+        void setReferenceTransform(const pinocchio::SE3 & transformRef) noexcept;
+        const pinocchio::SE3 & getReferenceTransform() const noexcept;
 
         virtual hresult_t reset(const Eigen::VectorXd & /* q */,
                                 const Eigen::VectorXd & /* v */) override final;
@@ -50,7 +50,7 @@ namespace jiminy
         /// \brief Name of the frame on which the constraint operates.
         std::string frameName_;
         /// \brief Corresponding frame index.
-        pinocchio::FrameIndex frameIdx_;
+        pinocchio::FrameIndex frameIdx_{0};
         /// \brief Wheel radius.
         double radius_;
         /// \brief Ground normal, world frame.
@@ -58,13 +58,13 @@ namespace jiminy
         /// \brief Wheel axis, local frame.
         Eigen::Vector3d axis_;
         /// \brief Skew matrix of wheel axis, in world frame, scaled by radius.
-        Eigen::Matrix3d skewRadius_;
+        Eigen::Matrix3d skewRadius_{};
         /// \brief Derivative of skew matrix of wheel axis, in world frame, scaled by radius.
-        Eigen::Matrix3d dskewRadius_;
+        Eigen::Matrix3d dskewRadius_{};
         /// \brief Reference pose of the frame to enforce.
-        pinocchio::SE3 transformRef_;
+        pinocchio::SE3 transformRef_{};
         /// \brief Stores full frame jacobian in world.
-        Matrix6Xd frameJacobian_;
+        Matrix6Xd frameJacobian_{};
     };
 }
 
diff --git a/core/include/jiminy/core/control/abstract_controller.h b/core/include/jiminy/core/control/abstract_controller.h
index 4eb6a9c11..b9c80ae38 100644
--- a/core/include/jiminy/core/control/abstract_controller.h
+++ b/core/include/jiminy/core/control/abstract_controller.h
@@ -4,7 +4,7 @@
 #include <variant>
 
 #include "jiminy/core/fwd.h"
-#include "jiminy/core/telemetry/telemetry_sender.h"
+#include "jiminy/core/hardware/fwd.h"
 #include "jiminy/core/hardware/abstract_sensor.h"
 
 
@@ -13,6 +13,7 @@ namespace jiminy
     /// \brief Namespace of the telemetry object.
     inline constexpr std::string_view CONTROLLER_TELEMETRY_NAMESPACE{"HighLevelController"};
 
+    class TelemetrySender;
     class TelemetryData;
     class Robot;
     class Engine;
@@ -49,8 +50,8 @@ namespace jiminy
         DISABLE_COPY(AbstractController)
 
     public:
-        AbstractController();
-        virtual ~AbstractController() = default;
+        explicit AbstractController() noexcept;
+        virtual ~AbstractController();
 
         /// \brief Set the parameters of the controller.
         ///
@@ -134,7 +135,7 @@ namespace jiminy
                                            Eigen::VectorXd & uCustom) = 0;
 
         /// \brief Dictionary with the parameters of the controller.
-        GenericConfig getOptions() const;
+        GenericConfig getOptions() const noexcept;
 
         /// \brief Set the configuration options of the controller.
         ///
@@ -198,27 +199,27 @@ namespace jiminy
 
     public:
         /// \brief Structure with the parameters of the controller.
-        std::unique_ptr<const controllerOptions_t> baseControllerOptions_;
+        std::unique_ptr<const controllerOptions_t> baseControllerOptions_{nullptr};
         /// \brief Robot for which to compute the command and internal dynamics must be computed.
-        std::weak_ptr<const Robot> robot_;
-        SensorsDataMap sensorsData_;
+        std::weak_ptr<const Robot> robot_{};
+        SensorsDataMap sensorsData_{};
 
     protected:
         /// \brief Flag to determine whether the controller has been initialized or not.
-        bool isInitialized_;
+        bool isInitialized_{false};
         /// \brief Flag to determine whether the telemetry of the controller has been initialized.
-        bool isTelemetryConfigured_;
+        bool isTelemetryConfigured_{false};
         /// \brief Dictionary with the parameters of the controller.
-        GenericConfig ctrlOptionsHolder_;
+        GenericConfig ctrlOptionsHolder_{};
         /// \brief Telemetry sender used to register and update telemetry variables.
-        TelemetrySender telemetrySender_;
+        std::unique_ptr<TelemetrySender> telemetrySender_;
 
     private:
         /// \brief Vector of dynamically registered telemetry variables.
         static_map_t<std::string, std::variant<const double *, const int64_t *>>
-            registeredVariables_;
+            registeredVariables_{};
         /// \brief Vector of dynamically registered telemetry constants.
-        static_map_t<std::string, std::string> registeredConstants_;
+        static_map_t<std::string, std::string> registeredConstants_{};
     };
 }
 
diff --git a/core/include/jiminy/core/control/controller_functor.h b/core/include/jiminy/core/control/controller_functor.h
index ce838e12d..59ebf169c 100644
--- a/core/include/jiminy/core/control/controller_functor.h
+++ b/core/include/jiminy/core/control/controller_functor.h
@@ -32,8 +32,8 @@ namespace jiminy
         ///
         /// \param[in] commandFct 'Callable' computing the command.
         /// \param[in] internalDynamicsFct 'Callable' computing the internal dynamics.
-        ControllerFunctor(F1 & commandFct, F2 & internalDynamicsFct);
-        ControllerFunctor(F1 && commandFct, F2 && internalDynamicsFct);
+        explicit ControllerFunctor(F1 & commandFct, F2 & internalDynamicsFct) noexcept;
+        explicit ControllerFunctor(F1 && commandFct, F2 && internalDynamicsFct) noexcept;
 
         virtual ~ControllerFunctor() = default;
 
diff --git a/core/include/jiminy/core/control/controller_functor.hxx b/core/include/jiminy/core/control/controller_functor.hxx
index 0b2cf33da..ef26dd8c0 100644
--- a/core/include/jiminy/core/control/controller_functor.hxx
+++ b/core/include/jiminy/core/control/controller_functor.hxx
@@ -6,15 +6,17 @@
 namespace jiminy
 {
     template<typename F1, typename F2>
-    ControllerFunctor<F1, F2>::ControllerFunctor(F1 & commandFct, F2 & internalDynamicsFct) :
+    ControllerFunctor<F1, F2>::ControllerFunctor(F1 & commandFct,
+                                                 F2 & internalDynamicsFct) noexcept :
     AbstractController(),
-    commandFct_(commandFct),
-    internalDynamicsFct_(internalDynamicsFct)
+    commandFct_{commandFct},
+    internalDynamicsFct_{internalDynamicsFct}
     {
     }
 
     template<typename F1, typename F2>
-    ControllerFunctor<F1, F2>::ControllerFunctor(F1 && commandFct, F2 && internalDynamicsFct) :
+    ControllerFunctor<F1, F2>::ControllerFunctor(F1 && commandFct,
+                                                 F2 && internalDynamicsFct) noexcept :
     AbstractController(),
     commandFct_(std::move(commandFct)),
     internalDynamicsFct_(std::move(internalDynamicsFct))
diff --git a/core/include/jiminy/core/engine/engine.h b/core/include/jiminy/core/engine/engine.h
index d772361d4..2621e1956 100644
--- a/core/include/jiminy/core/engine/engine.h
+++ b/core/include/jiminy/core/engine/engine.h
@@ -15,8 +15,8 @@ namespace jiminy
         DISABLE_COPY(Engine)
 
     public:
-        Engine() = default;
-        virtual ~Engine() = default;
+        explicit Engine() = default;
+        ~Engine() = default;
 
         hresult_t initialize(std::shared_ptr<Robot> robot,
                              std::shared_ptr<AbstractController> controller,
diff --git a/core/include/jiminy/core/engine/engine_multi_robot.h b/core/include/jiminy/core/engine/engine_multi_robot.h
index 0b48258a1..56a44286e 100644
--- a/core/include/jiminy/core/engine/engine_multi_robot.h
+++ b/core/include/jiminy/core/engine/engine_multi_robot.h
@@ -5,7 +5,6 @@
 #include <functional>
 
 #include "jiminy/core/fwd.h"
-#include "jiminy/core/telemetry/telemetry_sender.h"
 #include "jiminy/core/engine/system.h"
 
 
@@ -43,6 +42,7 @@ namespace jiminy
     class AbstractStepper;
     class TelemetryData;
     class TelemetryRecorder;
+    class TelemetrySender;
     struct LogData;
 
     using ForceCouplingRegister = std::vector<ForceCoupling>;
@@ -69,18 +69,18 @@ namespace jiminy
         }
 
     public:
-        uint32_t iter;
-        uint32_t iterFailed;
-        double t;
-        double tPrev;
+        uint32_t iter{0U};
+        uint32_t iterFailed{0U};
+        double t{0.0};
+        double tPrev{0.0};
         /// \brief Internal buffer used for Kahan algorithm storing the residual sum of errors.
-        double tError;
-        double dt;
-        double dtLargest;
-        double dtLargestPrev;
-        std::vector<Eigen::VectorXd> qSplit;
-        std::vector<Eigen::VectorXd> vSplit;
-        std::vector<Eigen::VectorXd> aSplit;
+        double tError{0.0};
+        double dt{0.0};
+        double dtLargest{0.0};
+        double dtLargestPrev{0.0};
+        std::vector<Eigen::VectorXd> qSplit{};
+        std::vector<Eigen::VectorXd> vSplit{};
+        std::vector<Eigen::VectorXd> aSplit{};
     };
 
     class JIMINY_DLLAPI EngineMultiRobot
@@ -327,8 +327,8 @@ namespace jiminy
         DISABLE_COPY(EngineMultiRobot)
 
     public:
-        EngineMultiRobot();
-        virtual ~EngineMultiRobot();
+        explicit EngineMultiRobot() noexcept;
+        ~EngineMultiRobot();
 
         hresult_t addSystem(const std::string & systemName,
                             std::shared_ptr<Robot> robot,
@@ -488,11 +488,11 @@ namespace jiminy
         hresult_t getForcesProfile(const std::string & systemName,
                                    const ForceProfileRegister *& forcesProfilePtr) const;
 
-        GenericConfig getOptions() const;
+        GenericConfig getOptions() const noexcept;
         hresult_t setOptions(const GenericConfig & engineOptions);
         bool getIsTelemetryConfigured() const;
         std::vector<std::string> getSystemsNames() const;
-        hresult_t getSystemIdx(const std::string & systemName, int32_t & systemIdx) const;
+        hresult_t getSystemIdx(const std::string & systemName, std::ptrdiff_t & systemIdx) const;
         hresult_t getSystem(const std::string & systemName, systemHolder_t *& system);
         hresult_t getSystemState(const std::string & systemName,
                                  const systemState_t *& systemState) const;
@@ -654,30 +654,30 @@ namespace jiminy
                 const vector_aligned_t<ForceDerived> & fext);
 
     public:
-        std::unique_ptr<const engineOptions_t> engineOptions_;
-        std::vector<systemHolder_t> systems_;
+        std::unique_ptr<const engineOptions_t> engineOptions_{nullptr};
+        std::vector<systemHolder_t> systems_{};
 
     protected:
-        bool isTelemetryConfigured_;
-        bool isSimulationRunning_;
-        GenericConfig engineOptionsHolder_;
+        bool isTelemetryConfigured_{false};
+        bool isSimulationRunning_{false};
+        GenericConfig engineOptionsHolder_{};
 
     private:
-        std::unique_ptr<Timer> timer_;
-        contactModel_t contactModel_;
-        TelemetrySender telemetrySender_;
+        std::unique_ptr<Timer> timer_{std::make_unique<Timer>()};
+        contactModel_t contactModel_{contactModel_t::UNSUPPORTED};
+        std::unique_ptr<TelemetrySender> telemetrySender_;
         std::shared_ptr<TelemetryData> telemetryData_;
         std::unique_ptr<TelemetryRecorder> telemetryRecorder_;
-        std::unique_ptr<AbstractStepper> stepper_;
-        double stepperUpdatePeriod_;
-        StepperState stepperState_;
-        vector_aligned_t<systemDataHolder_t> systemsDataHolder_;
-        ForceCouplingRegister forcesCoupling_;
-        vector_aligned_t<ForceVector> contactForcesPrev_;
-        vector_aligned_t<ForceVector> fPrev_;
-        vector_aligned_t<MotionVector> aPrev_;
-        std::vector<double> energy_;
-        std::shared_ptr<LogData> logData_;
+        std::unique_ptr<AbstractStepper> stepper_{nullptr};
+        double stepperUpdatePeriod_{INF};
+        StepperState stepperState_{};
+        vector_aligned_t<systemDataHolder_t> systemsDataHolder_{};
+        ForceCouplingRegister forcesCoupling_{};
+        vector_aligned_t<ForceVector> contactForcesPrev_{};
+        vector_aligned_t<ForceVector> fPrev_{};
+        vector_aligned_t<MotionVector> aPrev_{};
+        std::vector<double> energy_{};
+        std::shared_ptr<LogData> logData_{nullptr};
     };
 }
 
diff --git a/core/include/jiminy/core/engine/system.h b/core/include/jiminy/core/engine/system.h
index 0032cd70d..a3d8cc0f0 100644
--- a/core/include/jiminy/core/engine/system.h
+++ b/core/include/jiminy/core/engine/system.h
@@ -14,7 +14,6 @@ namespace jiminy
 {
     class Robot;
     class AbstractConstraintSolver;
-    class AbstractConstraintBase;
     class AbstractController;
     class LockGuardLocal;
 
@@ -25,36 +24,37 @@ namespace jiminy
     struct JIMINY_DLLAPI ForceProfile
     {
     public:
-        ForceProfile() = default;
-        ForceProfile(const std::string & frameNameIn,
-                     pinocchio::FrameIndex frameIdxIn,
-                     double updatePeriodIn,
-                     const ForceProfileFunctor & forceFctIn);
+        explicit ForceProfile() = default;
+        explicit ForceProfile(const std::string & frameNameIn,
+                              pinocchio::FrameIndex frameIdxIn,
+                              double updatePeriodIn,
+                              const ForceProfileFunctor & forceFctIn) noexcept;
 
     public:
-        std::string frameName;
-        pinocchio::FrameIndex frameIdx;
-        double updatePeriod;
-        pinocchio::Force forcePrev;
-        ForceProfileFunctor forceFct;
+        std::string frameName{};
+        pinocchio::FrameIndex frameIdx{0};
+        double updatePeriod{0.0};
+        pinocchio::Force forcePrev{pinocchio::Force::Zero()};
+        ForceProfileFunctor forceFct{};
     };
 
     struct JIMINY_DLLAPI ForceImpulse
     {
     public:
-        ForceImpulse() = default;
-        ForceImpulse(const std::string & frameNameIn,
-                     pinocchio::FrameIndex frameIdxIn,
-                     double tIn,
-                     double dtIn,
-                     const pinocchio::Force & FIn);
+        explicit ForceImpulse() = default;
+        explicit ForceImpulse(const std::string & frameNameIn,
+                              pinocchio::FrameIndex frameIdxIn,
+                              double tIn,
+                              double dtIn,
+                              const pinocchio::Force & FIn) noexcept;
+
 
     public:
-        std::string frameName;
-        pinocchio::FrameIndex frameIdx;
-        double t;
-        double dt;
-        pinocchio::Force F;
+        std::string frameName{};
+        pinocchio::FrameIndex frameIdx{0};
+        double t{0.0};
+        double dt{0.0};
+        pinocchio::Force F{};
     };
 
     using ForceCouplingFunctor = std::function<pinocchio::Force(double /*t*/,
@@ -66,27 +66,27 @@ namespace jiminy
     struct ForceCoupling
     {
     public:
-        ForceCoupling() = default;
-        ForceCoupling(const std::string & systemName1In,
-                      int32_t systemIdx1In,
-                      const std::string & systemName2In,
-                      int32_t systemIdx2In,
-                      const std::string & frameName1In,
-                      pinocchio::FrameIndex frameIdx1In,
-                      const std::string & frameName2In,
-                      pinocchio::FrameIndex frameIdx2In,
-                      const ForceCouplingFunctor & forceFctIn);
+        explicit ForceCoupling() = default;
+        explicit ForceCoupling(const std::string & systemName1In,
+                               std::ptrdiff_t systemIdx1In,
+                               const std::string & systemName2In,
+                               std::ptrdiff_t systemIdx2In,
+                               const std::string & frameName1In,
+                               pinocchio::FrameIndex frameIdx1In,
+                               const std::string & frameName2In,
+                               pinocchio::FrameIndex frameIdx2In,
+                               const ForceCouplingFunctor & forceFctIn) noexcept;
 
     public:
-        std::string systemName1;
-        int32_t systemIdx1;
-        std::string systemName2;
-        int32_t systemIdx2;
-        std::string frameName1;
-        pinocchio::FrameIndex frameIdx1;
-        std::string frameName2;
-        pinocchio::FrameIndex frameIdx2;
-        ForceCouplingFunctor forceFct;
+        std::string systemName1{};
+        std::ptrdiff_t systemIdx1{-1};
+        std::string systemName2{};
+        std::ptrdiff_t systemIdx2{-1};
+        std::string frameName1{};
+        pinocchio::FrameIndex frameIdx1{0};
+        std::string frameName2{};
+        pinocchio::FrameIndex frameIdx2{0};
+        ForceCouplingFunctor forceFct{};
     };
 
     using ForceProfileRegister = std::vector<ForceProfile>;
@@ -98,49 +98,38 @@ namespace jiminy
 
     struct JIMINY_DLLAPI systemHolder_t
     {
-    public:
-        systemHolder_t();
-        systemHolder_t(const std::string & systemNameIn,
-                       std::shared_ptr<Robot> robotIn,
-                       std::shared_ptr<AbstractController> controllerIn,
-                       CallbackFunctor callbackFctIn);
-        systemHolder_t(const systemHolder_t & other) = default;
-        systemHolder_t(systemHolder_t && other) = default;
-        systemHolder_t & operator=(const systemHolder_t & other) = default;
-        systemHolder_t & operator=(systemHolder_t && other) = default;
-        ~systemHolder_t() = default;
-
-    public:
-        std::string name;
-        std::shared_ptr<Robot> robot;
-        std::shared_ptr<AbstractController> controller;
-        CallbackFunctor callbackFct;
+        std::string name{};
+        std::shared_ptr<Robot> robot{nullptr};
+        std::shared_ptr<AbstractController> controller{nullptr};
+        CallbackFunctor callbackFct{+[](double /* t */,
+                                        const Eigen::VectorXd & /* q */,
+                                        const Eigen::VectorXd & /* v */) -> bool
+                                    {
+                                        return false;
+                                    }};
     };
 
     struct JIMINY_DLLAPI systemState_t
     {
     public:
-        // Non-default constructor to be considered initialized even if not
-        systemState_t();
-
         hresult_t initialize(const Robot & robot);
         bool getIsInitialized() const;
 
         void clear();
 
     public:
-        Eigen::VectorXd q;
-        Eigen::VectorXd v;
-        Eigen::VectorXd a;
-        Eigen::VectorXd command;
-        Eigen::VectorXd u;
-        Eigen::VectorXd uMotor;
-        Eigen::VectorXd uInternal;
-        Eigen::VectorXd uCustom;
-        ForceVector fExternal;
+        Eigen::VectorXd q{};
+        Eigen::VectorXd v{};
+        Eigen::VectorXd a{};
+        Eigen::VectorXd command{};
+        Eigen::VectorXd u{};
+        Eigen::VectorXd uMotor{};
+        Eigen::VectorXd uInternal{};
+        Eigen::VectorXd uCustom{};
+        ForceVector fExternal{};
 
     private:
-        bool isInitialized_;
+        bool isInitialized_{false};
     };
 
     struct JIMINY_DLLAPI systemDataHolder_t
@@ -159,43 +148,43 @@ namespace jiminy
         ~systemDataHolder_t();
 
     public:
-        std::unique_ptr<LockGuardLocal> robotLock;
+        std::unique_ptr<LockGuardLocal> robotLock{nullptr};
 
-        ForceProfileRegister forcesProfile;
-        ForceImpulseRegister forcesImpulse;
+        ForceProfileRegister forcesProfile{};
+        ForceImpulseRegister forcesImpulse{};
         /// \brief Ordered list without repetitions of all the start/end times of the forces.
-        std::set<double> forcesImpulseBreaks;
+        std::set<double> forcesImpulseBreaks{};
         /// \brief Time of the next breakpoint associated with the impulse forces.
-        std::set<double>::const_iterator forcesImpulseBreakNextIt;
+        std::set<double>::const_iterator forcesImpulseBreakNextIt{};
         /// \brief Set of flags tracking whether each force is active.
         ///
         /// \details This flag is used to handle t-, t+ properly. Without it, it is impossible to
         ///          determine at time t if the force is active or not.
-        std::vector<bool> forcesImpulseActive;
+        std::vector<bool> forcesImpulseActive{};
 
-        uint32_t successiveSolveFailed;
-        std::unique_ptr<AbstractConstraintSolver> constraintSolver;
+        uint32_t successiveSolveFailed{0};
+        std::unique_ptr<AbstractConstraintSolver> constraintSolver{nullptr};
         /// \brief Store copy of constraints register for fast access.
-        constraintsHolder_t constraintsHolder;
+        constraintsHolder_t constraintsHolder{};
         /// \brief Contact forces for each contact frames in local frame.
-        ForceVector contactFramesForces;
+        ForceVector contactFramesForces{};
         /// \brief Contact forces for each geometries of each collision bodies in local frame.
-        vector_aligned_t<ForceVector> collisionBodiesForces;
+        vector_aligned_t<ForceVector> collisionBodiesForces{};
         /// \brief Jacobian of the joints in local frame. Used for computing `data.u`.
-        std::vector<Matrix6Xd> jointsJacobians;
+        std::vector<Matrix6Xd> jointsJacobians{};
 
-        std::vector<std::string> logFieldnamesPosition;
-        std::vector<std::string> logFieldnamesVelocity;
-        std::vector<std::string> logFieldnamesAcceleration;
-        std::vector<std::string> logFieldnamesForceExternal;
-        std::vector<std::string> logFieldnamesCommand;
-        std::vector<std::string> logFieldnamesMotorEffort;
-        std::string logFieldnameEnergy;
+        std::vector<std::string> logFieldnamesPosition{};
+        std::vector<std::string> logFieldnamesVelocity{};
+        std::vector<std::string> logFieldnamesAcceleration{};
+        std::vector<std::string> logFieldnamesForceExternal{};
+        std::vector<std::string> logFieldnamesCommand{};
+        std::vector<std::string> logFieldnamesMotorEffort{};
+        std::string logFieldnameEnergy{};
 
         /// \brief Internal buffer with the state for the integration loop.
-        systemState_t state;
+        systemState_t state{};
         /// \brief Internal state for the integration loop at the end of the previous iteration.
-        systemState_t statePrev;
+        systemState_t statePrev{};
     };
 }
 
diff --git a/core/include/jiminy/core/fwd.h b/core/include/jiminy/core/fwd.h
index 6b9ef2ded..d3781e2b4 100644
--- a/core/include/jiminy/core/fwd.h
+++ b/core/include/jiminy/core/fwd.h
@@ -137,10 +137,10 @@ namespace jiminy
                     this->damping == other.damping && this->inertia == other.inertia);
         };
 
-        std::string frameName;
-        Eigen::Vector3d stiffness;
-        Eigen::Vector3d damping;
-        Eigen::Vector3d inertia;
+        std::string frameName{};
+        Eigen::Vector3d stiffness{};
+        Eigen::Vector3d damping{};
+        Eigen::Vector3d inertia{};
     };
 
     using FlexibilityConfig = std::vector<FlexibleJointData>;
@@ -162,9 +162,6 @@ namespace jiminy
                                FlexibilityConfig,
                                std::unordered_map<std::string, boost::recursive_variant_>>::type>;
 
-    struct SensorDataTypeMap;
-    using SensorsDataMap = std::unordered_map<std::string, SensorDataTypeMap>;
-
     // Generic utilities used everywhere
     template<typename... Args>
     std::string toString(Args &&... args)
diff --git a/core/include/jiminy/core/hardware/abstract_motor.h b/core/include/jiminy/core/hardware/abstract_motor.h
index 0a698e765..7f0d1303f 100644
--- a/core/include/jiminy/core/hardware/abstract_motor.h
+++ b/core/include/jiminy/core/hardware/abstract_motor.h
@@ -2,7 +2,7 @@
 ///
 /// \details Any motor must inherit from this base class and implement its virtual methods.
 ///
-/// \remark Each motor added to a Jiminy Robot is downcasted as an instance of AbstractMotorBase
+/// \remark Each motor added to a Jiminy Robot is down-casted as an instance of AbstractMotorBase
 ///         and polymorphism is used to call the actual implementations.
 
 #ifndef JIMINY_ABSTRACT_MOTOR_H
@@ -77,9 +77,8 @@ namespace jiminy
         DISABLE_COPY(AbstractMotorBase)
 
     public:
-        /// \param[in] robot Robot.
         /// \param[in] name Name of the motor.
-        AbstractMotorBase(const std::string & name);
+        explicit AbstractMotorBase(const std::string & name) noexcept;
         virtual ~AbstractMotorBase();
 
         /// \brief Refresh the proxies.
@@ -97,7 +96,7 @@ namespace jiminy
         virtual hresult_t resetAll();
 
         /// \brief Configuration options of the motor.
-        GenericConfig getOptions() const;
+        GenericConfig getOptions() const noexcept;
 
         /// \brief Actual effort of the motor at the current time.
         double get() const;
@@ -134,10 +133,10 @@ namespace jiminy
         JointModelType getJointType() const;
 
         /// \brief Index of the joint associated with the motor in configuration vector.
-        int32_t getJointPositionIdx() const;
+        Eigen::Index getJointPositionIdx() const;
 
         /// \brief Index of the joint associated with the motor in the velocity vector.
-        int32_t getJointVelocityIdx() const;
+        Eigen::Index getJointVelocityIdx() const;
 
         /// \brief Maximum effort of the motor.
         double getCommandLimit() const;
@@ -199,34 +198,34 @@ namespace jiminy
 
     public:
         /// \brief Structure with the parameters of the motor.
-        std::unique_ptr<const abstractMotorOptions_t> baseMotorOptions_;
+        std::unique_ptr<const abstractMotorOptions_t> baseMotorOptions_{nullptr};
 
     protected:
         /// \brief Dictionary with the parameters of the motor.
-        GenericConfig motorOptionsHolder_;
+        GenericConfig motorOptionsHolder_{};
         /// \brief Flag to determine whether the controller has been initialized or not.
-        bool isInitialized_;
+        bool isInitialized_{false};
         /// \brief Flag to determine whether the motor is attached to a robot.
-        bool isAttached_;
+        bool isAttached_{false};
         /// \brief Robot for which the command and internal dynamics.
-        std::weak_ptr<const Robot> robot_;
+        std::weak_ptr<const Robot> robot_{};
         /// \brief Notify the robot that the configuration of the sensors have changed.
-        std::function<hresult_t(AbstractMotorBase &)> notifyRobot_;
+        std::function<hresult_t(AbstractMotorBase &)> notifyRobot_{};
         /// \brief Name of the motor.
         std::string name_;
         /// \brief Index of the motor in the measurement buffer.
-        std::size_t motorIdx_;
-        std::string jointName_;
-        pinocchio::JointIndex jointModelIdx_;
-        JointModelType jointType_;
-        int32_t jointPositionIdx_;
-        int32_t jointVelocityIdx_;
-        double commandLimit_;
-        double armature_;
+        std::size_t motorIdx_{0};
+        std::string jointName_{};
+        pinocchio::JointIndex jointModelIdx_{0};
+        JointModelType jointType_{JointModelType::UNSUPPORTED};
+        Eigen::Index jointPositionIdx_{0};
+        Eigen::Index jointVelocityIdx_{0};
+        double commandLimit_{0.0};
+        double armature_{0.0};
 
     private:
         /// \brief Shared data between every motors associated with the robot.
-        MotorSharedDataHolder_t * sharedHolder_;
+        MotorSharedDataHolder_t * sharedHolder_{nullptr};
     };
 }
 
diff --git a/core/include/jiminy/core/hardware/abstract_sensor.h b/core/include/jiminy/core/hardware/abstract_sensor.h
index b221b6167..ed1d599a6 100644
--- a/core/include/jiminy/core/hardware/abstract_sensor.h
+++ b/core/include/jiminy/core/hardware/abstract_sensor.h
@@ -3,7 +3,6 @@
 
 #include "jiminy/core/fwd.h"
 #include "jiminy/core/utilities/helpers.h"
-#include "jiminy/core/telemetry/telemetry_sender.h"
 
 #include <boost/circular_buffer.hpp>
 #include <boost/functional/hash.hpp>
@@ -18,94 +17,10 @@
 namespace jiminy
 {
     class TelemetryData;
+    class TelemetrySender;
     class Robot;
     class AbstractSensorBase;
 
-    // Sensor data holder
-    namespace details
-    {
-        struct SensorDataItem
-        {
-            std::string name;
-            std::size_t idx;
-            Eigen::Ref<const Eigen::VectorXd> value;
-        };
-    }
-
-    struct JIMINY_DLLAPI IndexByIndex
-    {
-    };
-    struct JIMINY_DLLAPI IndexByName
-    {
-    };
-
-    struct SensorDataTypeMap :
-    public boost::multi_index::multi_index_container<
-        details::SensorDataItem,
-        boost::multi_index::indexed_by<
-            boost::multi_index::ordered_unique<
-                boost::multi_index::tag<IndexByIndex>,
-                boost::multi_index::
-                    member<details::SensorDataItem, std::size_t, &details::SensorDataItem::idx>,
-                std::less<std::size_t>  // Ordering by ascending order
-                >,
-            boost::multi_index::hashed_unique<
-                boost::multi_index::tag<IndexByName>,
-                boost::multi_index::
-                    member<details::SensorDataItem, std::string, &details::SensorDataItem::name>>,
-            boost::multi_index::sequenced<>>>
-    {
-    public:
-        explicit SensorDataTypeMap(const Eigen::MatrixXd * sharedDataPtr = nullptr) :
-        multi_index_container(),
-        sharedDataPtr_(sharedDataPtr)
-        {
-        }
-
-        /// @brief Returning data associated with all sensors at once.
-        ///
-        /// @warning It is up to the sure to make sure that the data are up-to-date.
-        inline const Eigen::MatrixXd & getAll() const
-        {
-            if (sharedDataPtr_)
-            {
-                assert((size() == static_cast<std::size_t>(sharedDataPtr_->cols())) &&
-                       "Shared data inconsistent with sensors.");
-                return *sharedDataPtr_;
-            }
-            else
-            {
-                // Get sensors data size
-                Eigen::Index dataSize = 0;
-                if (size() > 0)
-                {
-                    dataSize = this->cbegin()->value.size();
-                }
-
-                // Resize internal buffer if needed
-                data_.resize(Eigen::NoChange, dataSize);
-
-                // Set internal buffer by copying sensor data sequentially
-                for (const auto & sensor : *this)
-                {
-                    assert(sensor.value.size() == dataSize &&
-                           "Cannot get all data at once for heterogeneous sensors.");
-                    data_.row(sensor.idx) = sensor.value;
-                }
-
-                return data_;
-            }
-        }
-
-    private:
-        const Eigen::MatrixXd * const sharedDataPtr_;
-        /* Internal buffer if no shared memory available.
-           It is useful if the sensors data is not contiguous in the first place,
-           which is likely to be the case when allocated from Python, or when
-           re-generating sensor data from log files. */
-        mutable Eigen::MatrixXd data_{};
-    };
-
     /// \brief Structure holding the data for every sensors of a given type.
     ///
     /// \details Every sensors of a given type must have the same 'behavior', e.g. the same delay
@@ -192,8 +107,8 @@ namespace jiminy
 
     public:
         /// \param[in] name Name of the sensor
-        AbstractSensorBase(const std::string & name);
-        virtual ~AbstractSensorBase() = default;
+        explicit AbstractSensorBase(const std::string & name) noexcept;
+        virtual ~AbstractSensorBase();
 
         /// \brief Reset the internal state of the sensors.
         ///
@@ -252,7 +167,7 @@ namespace jiminy
         virtual hresult_t setOptionsAll(const GenericConfig & sensorOptions) = 0;
 
         /// \brief Configuration options of the sensor.
-        GenericConfig getOptions() const;
+        GenericConfig getOptions() const noexcept;
 
         template<typename DerivedType>
         hresult_t set(const Eigen::MatrixBase<DerivedType> & value);
@@ -291,7 +206,7 @@ namespace jiminy
         virtual const std::string & getType() const = 0;
 
         /// \brief It is the size of the sensor's data vector.
-        virtual uint64_t getSize() const = 0;
+        virtual std::size_t getSize() const = 0;
 
         /// \brief Name of each element of the data measured by the sensor.
         virtual const std::vector<std::string> & getFieldnames() const = 0;
@@ -381,25 +296,25 @@ namespace jiminy
 
     public:
         /// \brief Structure with the parameters of the sensor
-        std::unique_ptr<const abstractSensorOptions_t> baseSensorOptions_;
+        std::unique_ptr<const abstractSensorOptions_t> baseSensorOptions_{nullptr};
 
     protected:
         /// \brief Dictionary with the parameters of the sensor.
-        GenericConfig sensorOptionsHolder_;
+        GenericConfig sensorOptionsHolder_{};
         /// \brief Flag to determine whether the sensor has been initialized.
-        bool isInitialized_;
+        bool isInitialized_{false};
         /// \brief Flag to determine whether the sensor is attached to a robot.
-        bool isAttached_;
+        bool isAttached_{false};
         /// \brief Flag to determine whether the telemetry of the sensor has been initialized.
-        bool isTelemetryConfigured_;
+        bool isTelemetryConfigured_{false};
         /// \brief Robot for which the command and internal dynamics Name of the sensor.
-        std::weak_ptr<const Robot> robot_;
+        std::weak_ptr<const Robot> robot_{};
         /// \brief Name of the sensor.
         std::string name_;
 
     private:
         /// \brief Telemetry sender of the sensor used to register and update telemetry variables.
-        TelemetrySender telemetrySender_;
+        std::unique_ptr<TelemetrySender> telemetrySender_;
     };
 
     template<typename T>
@@ -409,7 +324,7 @@ namespace jiminy
         DISABLE_COPY(AbstractSensorTpl)
 
     public:
-        AbstractSensorTpl(const std::string & name);
+        using AbstractSensorBase::AbstractSensorBase;
         virtual ~AbstractSensorTpl();
 
         auto shared_from_this() { return shared_from(this); }
@@ -422,7 +337,7 @@ namespace jiminy
         virtual std::size_t getIdx() const override final;
         virtual const std::string & getType() const override final;
         virtual const std::vector<std::string> & getFieldnames() const final;
-        virtual uint64_t getSize() const override final;
+        virtual std::size_t getSize() const override final;
 
         virtual Eigen::Ref<const Eigen::VectorXd> get() const override final;
 
@@ -452,10 +367,10 @@ namespace jiminy
         static const bool areFieldnamesGrouped_;
 
     protected:
-        std::size_t sensorIdx_;
+        std::size_t sensorIdx_{0};
 
     private:
-        SensorSharedDataHolder_t * sharedHolder_;
+        SensorSharedDataHolder_t * sharedHolder_{nullptr};
     };
 }
 
diff --git a/core/include/jiminy/core/hardware/abstract_sensor.hxx b/core/include/jiminy/core/hardware/abstract_sensor.hxx
index 27843cf20..6f114728c 100644
--- a/core/include/jiminy/core/hardware/abstract_sensor.hxx
+++ b/core/include/jiminy/core/hardware/abstract_sensor.hxx
@@ -34,14 +34,6 @@ namespace jiminy
 
     // ========================== AbstractSensorTpl ===============================
 
-    template<typename T>
-    AbstractSensorTpl<T>::AbstractSensorTpl(const std::string & name) :
-    AbstractSensorBase(name),
-    sensorIdx_(0),
-    sharedHolder_(nullptr)
-    {
-    }
-
     template<typename T>
     AbstractSensorTpl<T>::~AbstractSensorTpl()
     {
@@ -256,7 +248,7 @@ namespace jiminy
     }
 
     template<typename T>
-    uint64_t AbstractSensorTpl<T>::getSize() const
+    std::size_t AbstractSensorTpl<T>::getSize() const
     {
         return fieldnames_.size();
     }
diff --git a/core/include/jiminy/core/hardware/basic_motors.h b/core/include/jiminy/core/hardware/basic_motors.h
index 9d21eb661..88b244cf4 100644
--- a/core/include/jiminy/core/hardware/basic_motors.h
+++ b/core/include/jiminy/core/hardware/basic_motors.h
@@ -58,18 +58,18 @@ namespace jiminy
 
             motorOptions_t(const GenericConfig & options) :
             abstractMotorOptions_t(options),
-            enableFriction(boost::get<bool>(options.at("enableFriction"))),
-            frictionViscousPositive(boost::get<double>(options.at("frictionViscousPositive"))),
-            frictionViscousNegative(boost::get<double>(options.at("frictionViscousNegative"))),
-            frictionDryPositive(boost::get<double>(options.at("frictionDryPositive"))),
-            frictionDryNegative(boost::get<double>(options.at("frictionDryNegative"))),
-            frictionDrySlope(boost::get<double>(options.at("frictionDrySlope")))
+            enableFriction{boost::get<bool>(options.at("enableFriction"))},
+            frictionViscousPositive{boost::get<double>(options.at("frictionViscousPositive"))},
+            frictionViscousNegative{boost::get<double>(options.at("frictionViscousNegative"))},
+            frictionDryPositive{boost::get<double>(options.at("frictionDryPositive"))},
+            frictionDryNegative{boost::get<double>(options.at("frictionDryNegative"))},
+            frictionDrySlope{boost::get<double>(options.at("frictionDrySlope"))}
             {
             }
         };
 
     public:
-        SimpleMotor(const std::string & name);
+        explicit SimpleMotor(const std::string & name) noexcept;
         virtual ~SimpleMotor() = default;
 
         auto shared_from_this() { return shared_from(this); }
@@ -87,7 +87,7 @@ namespace jiminy
                                         double command) final override;
 
     private:
-        std::unique_ptr<const motorOptions_t> motorOptions_;
+        std::unique_ptr<const motorOptions_t> motorOptions_{nullptr};
     };
 }
 
diff --git a/core/include/jiminy/core/hardware/basic_sensors.h b/core/include/jiminy/core/hardware/basic_sensors.h
index d214f729d..0920893ab 100644
--- a/core/include/jiminy/core/hardware/basic_sensors.h
+++ b/core/include/jiminy/core/hardware/basic_sensors.h
@@ -13,7 +13,7 @@ namespace jiminy
     class JIMINY_DLLAPI ImuSensor : public AbstractSensorTpl<ImuSensor>
     {
     public:
-        ImuSensor(const std::string & name);
+        using AbstractSensorTpl<ImuSensor>::AbstractSensorTpl;
         virtual ~ImuSensor() = default;
 
         auto shared_from_this() { return shared_from(this); }
@@ -36,10 +36,10 @@ namespace jiminy
         virtual void measureData() final override;
 
     private:
-        std::string frameName_;
-        pinocchio::FrameIndex frameIdx_;
+        std::string frameName_{};
+        pinocchio::FrameIndex frameIdx_{0};
         /// \brief Sensor inverse rotation bias.
-        Eigen::Matrix3d sensorRotationBiasInv_;
+        Eigen::Matrix3d sensorRotationBiasInv_{Eigen::Matrix3d::Identity()};
     };
 
     template<>
@@ -52,7 +52,7 @@ namespace jiminy
     class JIMINY_DLLAPI ContactSensor : public AbstractSensorTpl<ContactSensor>
     {
     public:
-        ContactSensor(const std::string & name);
+        using AbstractSensorTpl<ContactSensor>::AbstractSensorTpl;
         virtual ~ContactSensor() = default;
 
         auto shared_from_this() { return shared_from(this); }
@@ -73,8 +73,8 @@ namespace jiminy
                               const ForceVector & fExternal) final override;
 
     private:
-        std::string frameName_;
-        pinocchio::FrameIndex frameIdx_;
+        std::string frameName_{};
+        pinocchio::FrameIndex frameIdx_{0};
     };
 
     template<>
@@ -87,7 +87,7 @@ namespace jiminy
     class JIMINY_DLLAPI ForceSensor : public AbstractSensorTpl<ForceSensor>
     {
     public:
-        ForceSensor(const std::string & name);
+        using AbstractSensorTpl<ForceSensor>::AbstractSensorTpl;
         virtual ~ForceSensor() = default;
 
         auto shared_from_this() { return shared_from(this); }
@@ -98,7 +98,7 @@ namespace jiminy
 
         const std::string & getFrameName() const;
         pinocchio::FrameIndex getFrameIdx() const;
-        pinocchio::JointIndex getJointIdx() const;
+        pinocchio::JointIndex getJointModelIdx() const;
 
     private:
         virtual hresult_t set(double t,
@@ -109,10 +109,10 @@ namespace jiminy
                               const ForceVector & fExternal) final override;
 
     private:
-        std::string frameName_;
-        pinocchio::FrameIndex frameIdx_;
-        pinocchio::JointIndex parentJointIdx_;
-        pinocchio::Force f_;
+        std::string frameName_{};
+        pinocchio::FrameIndex frameIdx_{0};
+        pinocchio::JointIndex parentJointModelIdx_{0};
+        pinocchio::Force f_{};
     };
 
     template<>
@@ -125,7 +125,7 @@ namespace jiminy
     class JIMINY_DLLAPI EncoderSensor : public AbstractSensorTpl<EncoderSensor>
     {
     public:
-        EncoderSensor(const std::string & name);
+        using AbstractSensorTpl<EncoderSensor>::AbstractSensorTpl;
         virtual ~EncoderSensor() = default;
 
         auto shared_from_this() { return shared_from(this); }
@@ -135,7 +135,7 @@ namespace jiminy
         virtual hresult_t refreshProxies() final override;
 
         const std::string & getJointName() const;
-        pinocchio::JointIndex getJointIdx() const;
+        pinocchio::JointIndex getJointModelIdx() const;
         JointModelType getJointType() const;
 
     private:
@@ -147,9 +147,9 @@ namespace jiminy
                               const ForceVector & fExternal) final override;
 
     private:
-        std::string jointName_;
-        pinocchio::JointIndex jointIdx_;
-        JointModelType jointType_;
+        std::string jointName_{};
+        pinocchio::JointIndex jointModelIdx_{0};
+        JointModelType jointType_{JointModelType::UNSUPPORTED};
     };
 
     template<>
@@ -162,7 +162,7 @@ namespace jiminy
     class JIMINY_DLLAPI EffortSensor : public AbstractSensorTpl<EffortSensor>
     {
     public:
-        EffortSensor(const std::string & name);
+        using AbstractSensorTpl<EffortSensor>::AbstractSensorTpl;
         virtual ~EffortSensor() = default;
 
         auto shared_from_this() { return shared_from(this); }
@@ -183,8 +183,8 @@ namespace jiminy
                               const ForceVector & fExternal) final override;
 
     private:
-        std::string motorName_;
-        std::size_t motorIdx_;
+        std::string motorName_{};
+        std::size_t motorIdx_{0};
     };
 
     template<>
diff --git a/core/include/jiminy/core/hardware/fwd.h b/core/include/jiminy/core/hardware/fwd.h
new file mode 100644
index 000000000..7c11588ac
--- /dev/null
+++ b/core/include/jiminy/core/hardware/fwd.h
@@ -0,0 +1,104 @@
+#ifndef JIMINY_FORWARD_SENSOR_H
+#define JIMINY_FORWARD_SENSOR_H
+
+#include "jiminy/core/fwd.h"
+
+#include <boost/multi_index/hashed_index.hpp>     // `boost::multi_index::hashed_unique`
+#include <boost/multi_index/member.hpp>           // `boost::multi_index::member`
+#include <boost/multi_index/ordered_index.hpp>    // `boost::multi_index::ordered_unique`
+#include <boost/multi_index/sequenced_index.hpp>  // `boost::multi_index::sequenced`
+#include <boost/multi_index/tag.hpp>              // `boost::multi_index::tag`
+#include <boost/multi_index_container.hpp>        // `boost::multi_index::multi_index_container`
+
+
+namespace jiminy
+{
+    // Sensor data holder
+    namespace details
+    {
+        struct SensorDataItem
+        {
+            std::string name;
+            std::size_t idx;
+            Eigen::Ref<const Eigen::VectorXd> value;
+        };
+    }
+
+    struct JIMINY_DLLAPI IndexByIndex
+    {
+    };
+    struct JIMINY_DLLAPI IndexByName
+    {
+    };
+
+    struct SensorDataTypeMap :
+    public boost::multi_index::multi_index_container<
+        details::SensorDataItem,
+        boost::multi_index::indexed_by<
+            boost::multi_index::ordered_unique<
+                boost::multi_index::tag<IndexByIndex>,
+                boost::multi_index::
+                    member<details::SensorDataItem, std::size_t, &details::SensorDataItem::idx>,
+                std::less<std::size_t>  // Ordering by ascending order
+                >,
+            boost::multi_index::hashed_unique<
+                boost::multi_index::tag<IndexByName>,
+                boost::multi_index::
+                    member<details::SensorDataItem, std::string, &details::SensorDataItem::name>>,
+            boost::multi_index::sequenced<>>>
+    {
+    public:
+        explicit SensorDataTypeMap(const Eigen::MatrixXd * sharedDataPtr = nullptr) noexcept :
+        multi_index_container(),
+        sharedDataPtr_{sharedDataPtr}
+        {
+        }
+
+        /// @brief Returning data associated with all sensors at once.
+        ///
+        /// @warning It is up to the sure to make sure that the data are up-to-date.
+        inline const Eigen::MatrixXd & getAll() const
+        {
+            if (sharedDataPtr_)
+            {
+                assert((size() == static_cast<std::size_t>(sharedDataPtr_->cols())) &&
+                       "Shared data inconsistent with sensors.");
+                return *sharedDataPtr_;
+            }
+            else
+            {
+                // Get sensors data size
+                Eigen::Index dataSize = 0;
+                if (size() > 0)
+                {
+                    dataSize = this->cbegin()->value.size();
+                }
+
+                // Resize internal buffer if needed
+                data_.resize(Eigen::NoChange, dataSize);
+
+                // Set internal buffer by copying sensor data sequentially
+                for (const auto & sensor : *this)
+                {
+                    assert(sensor.value.size() == dataSize &&
+                           "Cannot get all data at once for heterogeneous sensors.");
+                    data_.row(sensor.idx) = sensor.value;
+                }
+
+                return data_;
+            }
+        }
+
+    private:
+        const Eigen::MatrixXd * const sharedDataPtr_;
+        /* Internal buffer if no shared memory available.
+           It is useful if the sensors data is not contiguous in the first place,
+           which is likely to be the case when allocated from Python, or when
+           re-generating sensor data from log files. */
+        mutable Eigen::MatrixXd data_{};
+    };
+
+    using SensorsDataMap = std::unordered_map<std::string, SensorDataTypeMap>;
+}
+
+#endif  // JIMINY_FORWARD_SENSOR_H
diff --git a/core/include/jiminy/core/io/abstract_io_device.h b/core/include/jiminy/core/io/abstract_io_device.h
index 71f734710..01afbab4b 100644
--- a/core/include/jiminy/core/io/abstract_io_device.h
+++ b/core/include/jiminy/core/io/abstract_io_device.h
@@ -34,10 +34,12 @@ namespace jiminy
         NEW_ONLY = 0x040,
         /// \brief Do not create the device if it does not exists.
         EXISTING_ONLY = 0x080,
+#ifndef _WIN32
         /// \brief Open the device in non blocking mode.
         NON_BLOCKING = 0x100,
         /// \brief Open the device in sync mode (ensure that write are finished at return).
         SYNC = 0x200,
+#endif
     };
 
     // Facility operators to avoid cast.
@@ -52,7 +54,8 @@ namespace jiminy
     class JIMINY_DLLAPI AbstractIODevice
     {
     public:
-        AbstractIODevice();
+        explicit AbstractIODevice(openMode_t supportedModes) noexcept;
+        explicit AbstractIODevice(AbstractIODevice &&) = default;
         virtual ~AbstractIODevice() = default;
 
         /// \brief Open the device.
@@ -95,24 +98,24 @@ namespace jiminy
         ///
         /// \details For random-access devices, this function returns the size of the device.
         ///          For sequential devices, bytesAvailable() is returned.
-        virtual int64_t size();
+        virtual std::size_t size();
 
         /// \brief Move the current position cursor to pos if possible.
         ///
         /// \param pos Desired new position of the cursor.
         ///
         /// \return hresult_t::SUCCESS if successful, another hresult_t value otherwise.
-        virtual hresult_t seek(int64_t pos);
+        virtual hresult_t seek(std::ptrdiff_t pos);
 
         /// \brief The current cursor position (0 if there is not concept of position cursor).
-        virtual int64_t pos();
+        virtual std::ptrdiff_t pos();
 
         /// \brief Resize the device to provided size.
-        virtual hresult_t resize(int64_t size);
+        virtual hresult_t resize(std::size_t size);
 
         /// \brief Returns the number of bytes that are available for reading. Commonly used with
         ///        sequential device.
-        virtual int64_t bytesAvailable();
+        virtual std::size_t bytesAvailable();
 
         /// \brief Write data in the device.
         ///
@@ -123,15 +126,48 @@ namespace jiminy
         ///
         /// \return hresult_t::SUCCESS if successful, another hresult_t value otherwise.
         template<typename T>
-        hresult_t write(const T & valueIn);
+        std::enable_if_t<!is_contiguous_container_v<T> && std::is_trivially_copyable_v<T>,
+                         hresult_t>
+        write(const T & value);
 
-        /// \brief Write data in the device.
+        template<typename T>
+        std::enable_if_t<is_contiguous_container_v<T>, hresult_t> write(const T & value);
+
+        template<typename T>
+        std::enable_if_t<!is_contiguous_container_v<T> && !std::is_trivially_copyable_v<T>,
+                         hresult_t>
+        write(const T & value) = delete;
+
+        /// \brief Read data in the device.
         ///
-        /// \param data Buffer of data to write.
-        /// \param dataSize Number of bytes to write.
+        /// \details The default implementation manage only POD type. For specific type, the
+        ///          template shall be extended with specific implementation.
+        ///
+        /// \param Value to store read data.
         ///
         /// \return hresult_t::SUCCESS if successful, another hresult_t value otherwise.
-        virtual hresult_t write(const void * data, int64_t dataSize);
+        template<typename T>
+        std::enable_if_t<!is_contiguous_container_v<remove_cvref_t<T>> &&
+                             std::is_trivially_copyable_v<remove_cvref_t<T>>,
+                         hresult_t>
+        read(T && value);
+
+        template<typename T>
+        std::enable_if_t<is_contiguous_container_v<remove_cvref_t<T>>, hresult_t> read(T && value);
+
+        template<typename T>
+        std::enable_if_t<!is_contiguous_container_v<remove_cvref_t<T>> &&
+                             !std::is_trivially_copyable_v<remove_cvref_t<T>>,
+                         hresult_t>
+        read(T && value) = delete;
+
+        /// \brief Retrieve the latest error. Useful for calls that do not return an error code
+        ///        directly.
+        hresult_t getLastError() const;
+
+    protected:
+        virtual hresult_t doOpen(openMode_t mode) = 0;
+        virtual hresult_t doClose() = 0;
 
         /// \brief Write data in the device.
         ///
@@ -139,18 +175,15 @@ namespace jiminy
         /// \param dataSize Number of bytes to write.
         ///
         /// \return the number of bytes written, -1 in case of error.
-        virtual int64_t writeData(const void * data, int64_t dataSize) = 0;
+        virtual std::ptrdiff_t writeData(const void * data, std::size_t dataSize) = 0;
 
-        /// \brief Read data in the device.
-        ///
-        /// \details The default implementation manage only POD type. For specific type, the
-        ///          template shall be extended with specific implementation.
+        /// \brief Write data in the device.
         ///
-        /// \param Value to store read data.
+        /// \param data Buffer of data to write.
+        /// \param dataSize Number of bytes to write.
         ///
         /// \return hresult_t::SUCCESS if successful, another hresult_t value otherwise.
-        template<typename T>
-        hresult_t read(T & valueIn);
+        virtual hresult_t write(const void * data, std::size_t dataSize);
 
         /// \brief Read data from the device.
         ///
@@ -158,7 +191,7 @@ namespace jiminy
         /// \param dataSize Number of bytes to read.
         ///
         /// \return hresult_t::SUCCESS if successful, another hresult_t value otherwise.
-        virtual hresult_t read(void * data, int64_t dataSize);
+        virtual hresult_t read(void * data, std::size_t dataSize);
 
         /// \brief Read data in the device.
         ///
@@ -166,38 +199,15 @@ namespace jiminy
         /// \param dataSize Number of bytes to read.
         ///
         /// \return the number of bytes read, -1 in case of error.
-        virtual int64_t readData(void * data, int64_t dataSize) = 0;
-
-        /// \brief Retrieve the latest error. Useful for calls that do not return an error code
-        ///        directly.
-        hresult_t getLastError() const;
-
-        /// \brief Set the device blocking fashion.
-        ///
-        /// \return The latest generated error.
-        virtual hresult_t setBlockingMode(bool shouldBlock);
-
-        /// \brief Set the device backend (reset the old one if any).
-        bool isBackendValid();
-
-        /// \brief Set the device backend (reset the old one if any).
-        virtual void setBackend(std::unique_ptr<AbstractIODevice> io);
-
-        /// \brief Reset the device backend.
-        virtual void removeBackend();
+        virtual std::ptrdiff_t readData(void * data, std::size_t dataSize) = 0;
 
     protected:
-        virtual hresult_t doOpen(openMode_t mode) = 0;
-        virtual hresult_t doClose() = 0;
-
-        /// \brief Current opening mode.
-        openMode_t modes_;
         /// \brief Supported modes of the device.
-        openMode_t supportedModes_;
+        const openMode_t supportedModes_;
+        /// \brief Current opening mode.
+        openMode_t modes_{openMode_t::NOT_OPEN};
         /// \brief Latest generated error.
-        hresult_t lastError_;
-        /// \brief Backend to use if any.
-        std::unique_ptr<AbstractIODevice> io_;
+        hresult_t lastError_{hresult_t::SUCCESS};
     };
 }
 
diff --git a/core/include/jiminy/core/io/abstract_io_device.hxx b/core/include/jiminy/core/io/abstract_io_device.hxx
index 09874387f..eb93d8d0f 100644
--- a/core/include/jiminy/core/io/abstract_io_device.hxx
+++ b/core/include/jiminy/core/io/abstract_io_device.hxx
@@ -7,40 +7,45 @@
 
 namespace jiminy
 {
-    // Generic implementation - POD types
+    // POD types
     template<typename T>
-    hresult_t AbstractIODevice::write(const T & valueIn)
+    std::enable_if_t<!is_contiguous_container_v<T> && std::is_trivially_copyable_v<T>, hresult_t>
+    AbstractIODevice::write(const T & value)
     {
-        return write(reinterpret_cast<const uint8_t * const>(&valueIn), sizeof(T));
+        std::size_t toWrite = sizeof(T);
+        const void * const bufferPos = static_cast<const void *>(&value);
+        return write(bufferPos, toWrite);
     }
 
     template<typename T>
-    hresult_t AbstractIODevice::read(T & valueIn)
+    std::enable_if_t<!is_contiguous_container_v<remove_cvref_t<T>> &&
+                         std::is_trivially_copyable_v<remove_cvref_t<T>>,
+                     hresult_t>
+    AbstractIODevice::read(T && value)
     {
-        int64_t toRead = sizeof(T);
-        uint8_t * bufferPos = reinterpret_cast<uint8_t *>(&valueIn);
+        std::size_t toRead = sizeof(T);
+        void * const bufferPos = static_cast<void *>(&value);
         return read(bufferPos, toRead);
     }
 
-    //--------------------------------- Specific implementations --------------------------------//
-
-    // read
-    template<>
-    hresult_t AbstractIODevice::read<std::vector<uint8_t>>(std::vector<uint8_t> & v);
-    template<>
-    hresult_t AbstractIODevice::read<std::vector<char>>(std::vector<char> & v);
-    template<>
-    hresult_t AbstractIODevice::read<std::string>(std::string & str) = delete;
+    // Contiguous container
+    template<typename T>
+    std::enable_if_t<is_contiguous_container_v<T>, hresult_t>
+    AbstractIODevice::write(const T & value)
+    {
+        const std::size_t toWrite = value.size() * sizeof(typename T::value_type);
+        const void * const bufferPos = static_cast<const void *>(value.data());
+        return write(bufferPos, toWrite);
+    }
 
-    // write
-    template<>
-    hresult_t AbstractIODevice::write<std::string_view>(const std::string_view & str);
-    template<>
-    hresult_t AbstractIODevice::write<std::vector<uint8_t>>(const std::vector<uint8_t> & v);
-    template<>
-    hresult_t AbstractIODevice::write<std::vector<char>>(const std::vector<char> & v);
-    template<>
-    hresult_t AbstractIODevice::write<std::vector<uint64_t>>(const std::vector<uint64_t> & v);
+    template<typename T>
+    std::enable_if_t<is_contiguous_container_v<remove_cvref_t<T>>, hresult_t>
+    AbstractIODevice::read(T && value)
+    {
+        const std::size_t toRead = value.size() * sizeof(typename remove_cvref_t<T>::value_type);
+        void * const bufferPos = static_cast<void *>(value.data());
+        return read(bufferPos, toRead);
+    }
 }
 
 #endif  // JIMINY_ABSTRACT_IO_DEVICE_HXX
diff --git a/core/include/jiminy/core/io/file_device.h b/core/include/jiminy/core/io/file_device.h
index 9a14813f3..d0ec1da8c 100644
--- a/core/include/jiminy/core/io/file_device.h
+++ b/core/include/jiminy/core/io/file_device.h
@@ -13,17 +13,14 @@ namespace jiminy
     class JIMINY_DLLAPI FileDevice : public AbstractIODevice
     {
     public:
-        FileDevice(const std::string & filename);
+        explicit FileDevice(const std::string & filename) noexcept;
         virtual ~FileDevice();
 
-        int64_t size() override;
-        hresult_t resize(int64_t sizeIn) override;
-        hresult_t seek(int64_t pos) override;
-        int64_t pos() override;
-        int64_t bytesAvailable() override;
-
-        int64_t readData(void * data, int64_t dataSize) override;
-        int64_t writeData(const void * data, int64_t dataSize) override;
+        std::size_t size() override;
+        hresult_t seek(std::ptrdiff_t pos) override;
+        std::ptrdiff_t pos() override;
+        hresult_t resize(std::size_t size) override;
+        std::size_t bytesAvailable() override;
 
         const std::string & name() const;
 
@@ -31,9 +28,13 @@ namespace jiminy
         hresult_t doOpen(openMode_t mode) override;
         hresult_t doClose() override;
 
+        std::ptrdiff_t readData(void * data, std::size_t dataSize) override;
+        std::ptrdiff_t writeData(const void * data, std::size_t dataSize) override;
+
+    protected:
         std::string filename_;
         /// \brief File descriptor.
-        int32_t fileDescriptor_;
+        int32_t fileDescriptor_{-1};
     };
 }
 
diff --git a/core/include/jiminy/core/io/json_loader.h b/core/include/jiminy/core/io/json_loader.h
index 6ff14edac..7a53a087b 100644
--- a/core/include/jiminy/core/io/json_loader.h
+++ b/core/include/jiminy/core/io/json_loader.h
@@ -20,8 +20,7 @@ namespace jiminy
     class JIMINY_DLLAPI JsonLoader
     {
     public:
-        explicit JsonLoader(std::shared_ptr<AbstractIODevice> device);
-        ~JsonLoader() = default;
+        explicit JsonLoader(const std::shared_ptr<AbstractIODevice> & device) noexcept;
 
         /// \brief Load json data from device and parse it to root json.
         hresult_t load();
@@ -33,8 +32,8 @@ namespace jiminy
 
     protected:
         /// \brief Hold the parsed document.
-        std::unique_ptr<Json::Value> rootJson_;
-        std::vector<char> payload_;
+        std::unique_ptr<Json::Value> rootJson_{std::make_unique<Json::Value>()};
+        std::vector<char> payload_{};
         std::shared_ptr<AbstractIODevice> device_;
     };
 }
diff --git a/core/include/jiminy/core/io/json_writer.h b/core/include/jiminy/core/io/json_writer.h
index 45aad552c..c99007baf 100644
--- a/core/include/jiminy/core/io/json_writer.h
+++ b/core/include/jiminy/core/io/json_writer.h
@@ -16,15 +16,11 @@ namespace jiminy
     class JIMINY_DLLAPI JsonWriter
     {
     public:
-        JsonWriter(std::shared_ptr<AbstractIODevice> device);
-        ~JsonWriter() = default;
+        explicit JsonWriter(const std::shared_ptr<AbstractIODevice> & device) noexcept;
 
         /// \brief Dump current content to device.
         hresult_t dump(const Json::Value & input);
 
-        /// \brief In case the constructor can't init the backend use set to init.
-        void setBackend(std::shared_ptr<AbstractIODevice> device);
-
     private:
         std::shared_ptr<AbstractIODevice> device_;
     };
diff --git a/core/include/jiminy/core/io/memory_device.h b/core/include/jiminy/core/io/memory_device.h
index be8019150..193baf790 100644
--- a/core/include/jiminy/core/io/memory_device.h
+++ b/core/include/jiminy/core/io/memory_device.h
@@ -11,41 +11,29 @@ namespace jiminy
     class JIMINY_DLLAPI MemoryDevice : public AbstractIODevice
     {
     public:
-        MemoryDevice(uint64_t size);
-        MemoryDevice(const MemoryDevice & other);
+        explicit MemoryDevice(std::size_t size) noexcept;
         MemoryDevice(MemoryDevice && other);
-
-        MemoryDevice(std::vector<uint8_t> && initBuffer);
-
+        explicit MemoryDevice(std::vector<uint8_t> && initBuffer) noexcept;
         virtual ~MemoryDevice();
 
-        MemoryDevice & operator=(const MemoryDevice & other);
-        MemoryDevice & operator=(MemoryDevice && other);
-
-        int64_t size() override { return static_cast<int64_t>(buffer_.size()); }
+        std::size_t size() override { return buffer_.size(); }
+        hresult_t seek(std::ptrdiff_t pos) override;
+        std::ptrdiff_t pos() override { return currentPos_; }
+        hresult_t resize(std::size_t size) override;
+        std::size_t bytesAvailable() override { return size() - currentPos_; }
 
         bool isSequential() const override { return false; }
 
-        int64_t pos() override { return currentPos_; }
-
-        int64_t bytesAvailable() override { return size() - currentPos_; }
-
-        hresult_t seek(int64_t pos) override;
-
-        int64_t readData(void * data, int64_t dataSize) override;
-        int64_t writeData(const void * data, int64_t dataSize) override;
-
-        hresult_t setBlockingMode(bool) override;
-
-        hresult_t resize(int64_t size) override;
-
     protected:
         hresult_t doOpen(openMode_t modes) override;
         hresult_t doClose() override;
 
+        std::ptrdiff_t readData(void * data, std::size_t dataSize) override;
+        std::ptrdiff_t writeData(const void * data, std::size_t dataSize) override;
+
     private:
         std::vector<uint8_t> buffer_;
-        int64_t currentPos_;
+        std::ptrdiff_t currentPos_{0};
     };
 }
 
diff --git a/core/include/jiminy/core/robot/model.h b/core/include/jiminy/core/robot/model.h
index c640adb8e..ee9a77b8a 100644
--- a/core/include/jiminy/core/robot/model.h
+++ b/core/include/jiminy/core/robot/model.h
@@ -40,7 +40,7 @@ namespace jiminy
     struct JIMINY_DLLAPI constraintsHolder_t
     {
     public:
-        void clear();
+        void clear() noexcept;
 
         std::pair<constraintsMap_t *, constraintsMap_t::iterator> find(
             const std::string & key, constraintsHolderType_t holderType);
@@ -111,13 +111,13 @@ namespace jiminy
 
     public:
         /// \brief Store internal constraints related to joint bounds.
-        constraintsMap_t boundJoints;
+        constraintsMap_t boundJoints{};
         /// \brief Store internal constraints related to contact frames.
-        constraintsMap_t contactFrames;
+        constraintsMap_t contactFrames{};
         /// \brief Store internal constraints related to collision bounds.
-        std::vector<constraintsMap_t> collisionBodies;
+        std::vector<constraintsMap_t> collisionBodies{};
         /// \brief Store internal constraints registered by user.
-        constraintsMap_t registered;
+        constraintsMap_t registered{};
     };
 
     class JIMINY_DLLAPI Model : public std::enable_shared_from_this<Model>
@@ -245,7 +245,7 @@ namespace jiminy
         DISABLE_COPY(Model)
 
     public:
-        Model();
+        explicit Model() noexcept;
         virtual ~Model() = default;
 
         hresult_t initialize(const pinocchio::Model & pncModel,
@@ -317,7 +317,7 @@ namespace jiminy
 
         // Copy on purpose
         hresult_t setOptions(GenericConfig modelOptions);
-        GenericConfig getOptions() const;
+        GenericConfig getOptions() const noexcept;
 
         /// \remark This method are not intended to be called manually. The Engine is taking care
         ///         of it.
@@ -330,9 +330,9 @@ namespace jiminy
         const std::vector<std::string> & getMeshPackageDirs() const;
         bool getHasFreeflyer() const;
         // Getters without 'get' prefix for consistency with pinocchio C++ API
-        int32_t nq() const;
-        int32_t nv() const;
-        int32_t nx() const;
+        Eigen::Index nq() const;
+        Eigen::Index nv() const;
+        Eigen::Index nx() const;
 
         const std::vector<std::string> & getCollisionBodiesNames() const;
         const std::vector<std::string> & getContactFramesNames() const;
@@ -341,8 +341,8 @@ namespace jiminy
         const std::vector<pinocchio::FrameIndex> & getContactFramesIdx() const;
         const std::vector<std::string> & getRigidJointsNames() const;
         const std::vector<pinocchio::JointIndex> & getRigidJointsModelIdx() const;
-        const std::vector<int32_t> & getRigidJointsPositionIdx() const;
-        const std::vector<int32_t> & getRigidJointsVelocityIdx() const;
+        const std::vector<Eigen::Index> & getRigidJointsPositionIdx() const;
+        const std::vector<Eigen::Index> & getRigidJointsVelocityIdx() const;
         const std::vector<std::string> & getFlexibleJointsNames() const;
         const std::vector<pinocchio::JointIndex> & getFlexibleJointsModelIdx() const;
 
@@ -391,84 +391,84 @@ namespace jiminy
         virtual hresult_t refreshProxies();
 
     public:
-        pinocchio::Model pncModelOrig_;
-        pinocchio::Model pncModel_;
-        pinocchio::GeometryModel collisionModelOrig_;
-        pinocchio::GeometryModel collisionModel_;
-        pinocchio::GeometryModel visualModelOrig_;
-        pinocchio::GeometryModel visualModel_;
-        pinocchio::Data pncDataOrig_;
-        mutable pinocchio::Data pncData_;
-        mutable pinocchio::GeometryData collisionData_;
-        mutable pinocchio::GeometryData visualData_;
-        std::unique_ptr<const modelOptions_t> mdlOptions_;
+        pinocchio::Model pncModelOrig_{};
+        pinocchio::Model pncModel_{};
+        pinocchio::GeometryModel collisionModelOrig_{};
+        pinocchio::GeometryModel collisionModel_{};
+        pinocchio::GeometryModel visualModelOrig_{};
+        pinocchio::GeometryModel visualModel_{};
+        pinocchio::Data pncDataOrig_{};
+        mutable pinocchio::Data pncData_{};
+        mutable pinocchio::GeometryData collisionData_{};
+        mutable pinocchio::GeometryData visualData_{};
+        std::unique_ptr<const modelOptions_t> mdlOptions_{nullptr};
         /// \brief Buffer storing the contact forces.
-        ForceVector contactForces_;
+        ForceVector contactForces_{};
 
     protected:
-        bool isInitialized_;
-        std::string urdfPath_;
-        std::string urdfData_;
-        std::vector<std::string> meshPackageDirs_;
-        bool hasFreeflyer_;
-        GenericConfig mdlOptionsHolder_;
+        bool isInitialized_{false};
+        std::string urdfPath_{};
+        std::string urdfData_{};
+        std::vector<std::string> meshPackageDirs_{};
+        bool hasFreeflyer_{false};
+        GenericConfig mdlOptionsHolder_{};
 
         /// \brief Name of the collision bodies of the robot.
-        std::vector<std::string> collisionBodiesNames_;
+        std::vector<std::string> collisionBodiesNames_{};
         /// \brief Name of the contact frames of the robot.
-        std::vector<std::string> contactFramesNames_;
+        std::vector<std::string> contactFramesNames_{};
         /// \brief Indices of the collision bodies in the frame list of the robot.
-        std::vector<pinocchio::FrameIndex> collisionBodiesIdx_;
+        std::vector<pinocchio::FrameIndex> collisionBodiesIdx_{};
         /// \brief Indices of the collision pairs associated with each collision body.
-        std::vector<std::vector<pinocchio::PairIndex>> collisionPairsIdx_;
+        std::vector<std::vector<pinocchio::PairIndex>> collisionPairsIdx_{};
         /// \brief Indices of the contact frames in the frame list of the robot.
-        std::vector<pinocchio::FrameIndex> contactFramesIdx_;
+        std::vector<pinocchio::FrameIndex> contactFramesIdx_{};
         /// \brief Name of the actual joints of the robot, not taking into account the freeflyer.
-        std::vector<std::string> rigidJointsNames_;
+        std::vector<std::string> rigidJointsNames_{};
         /// \brief Index of the actual joints in the pinocchio robot.
-        std::vector<pinocchio::JointIndex> rigidJointsModelIdx_;
+        std::vector<pinocchio::JointIndex> rigidJointsModelIdx_{};
         /// \brief All the indices of the actual joints in the configuration vector of the robot
         ///        (ie including all the degrees of freedom).
-        std::vector<int32_t> rigidJointsPositionIdx_;
+        std::vector<Eigen::Index> rigidJointsPositionIdx_{};
         /// \brief All the indices of the actual joints in the velocity vector of the robot (ie
         ///        including all the degrees of freedom).
-        std::vector<int32_t> rigidJointsVelocityIdx_;
+        std::vector<Eigen::Index> rigidJointsVelocityIdx_{};
         /// \brief Name of the flexibility joints of the robot regardless of whether the
         ///        flexibilities are enabled.
-        std::vector<std::string> flexibleJointsNames_;
+        std::vector<std::string> flexibleJointsNames_{};
         /// \brief Index of the flexibility joints in the pinocchio robot regardless of whether the
         ///        flexibilities are enabled.
-        std::vector<pinocchio::JointIndex> flexibleJointsModelIdx_;
+        std::vector<pinocchio::JointIndex> flexibleJointsModelIdx_{};
 
-        constraintsHolder_t constraintsHolder_;  ///< Store constraints
+        constraintsHolder_t constraintsHolder_{};  ///< Store constraints
 
         /// \brief Upper position limit of the whole configuration vector (INF for non-physical
         ///        joints, ie flexibility joints and freeflyer, if any).
-        Eigen::VectorXd positionLimitMin_;
+        Eigen::VectorXd positionLimitMin_{};
         /// \brief Lower position limit of the whole configuration vector.
-        Eigen::VectorXd positionLimitMax_;
+        Eigen::VectorXd positionLimitMax_{};
         /// \brief Maximum absolute velocity of the whole velocity vector.
-        Eigen::VectorXd velocityLimit_;
+        Eigen::VectorXd velocityLimit_{};
 
         /// \brief Fieldnames of the elements in the configuration vector of the model.
-        std::vector<std::string> logFieldnamesPosition_;
+        std::vector<std::string> logFieldnamesPosition_{};
         /// \brief Fieldnames of the elements in the velocity vector of the model.
-        std::vector<std::string> logFieldnamesVelocity_;
+        std::vector<std::string> logFieldnamesVelocity_{};
         /// \brief Fieldnames of the elements in the acceleration vector of the model.
-        std::vector<std::string> logFieldnamesAcceleration_;
+        std::vector<std::string> logFieldnamesAcceleration_{};
         /// \brief Concatenated fieldnames of the external force applied at each joint of the
         ///        model, 'universe' excluded.
-        std::vector<std::string> logFieldnamesForceExternal_;
+        std::vector<std::string> logFieldnamesForceExternal_{};
 
     private:
-        pinocchio::Model pncModelFlexibleOrig_;
+        pinocchio::Model pncModelFlexibleOrig_{};
         /// \brief Vector of joints acceleration corresponding to a copy of data.a - temporary
         ///        buffer for computing constraints.
-        MotionVector jointsAcceleration_;
+        MotionVector jointsAcceleration_{};
 
-        int32_t nq_;
-        int32_t nv_;
-        int32_t nx_;
+        Eigen::Index nq_{0};
+        Eigen::Index nv_{0};
+        Eigen::Index nx_{0};
     };
 }
 
diff --git a/core/include/jiminy/core/robot/pinocchio_overload_algorithms.h b/core/include/jiminy/core/robot/pinocchio_overload_algorithms.h
index 875793f79..005570845 100644
--- a/core/include/jiminy/core/robot/pinocchio_overload_algorithms.h
+++ b/core/include/jiminy/core/robot/pinocchio_overload_algorithms.h
@@ -29,7 +29,6 @@
 #include "pinocchio/algorithm/cholesky.hpp"  // `pinocchio::cholesky::`
 
 #include "jiminy/core/fwd.h"
-#include "jiminy/core/traits.h"
 #include "jiminy/core/engine/engine_multi_robot.h"
 
 
@@ -456,7 +455,7 @@ namespace jiminy::pinocchio_overload
                                            ConfigVectorType,
                                            TangentVectorType1>
             Pass1;
-        for (int32_t i = 1; i < model.njoints; ++i)
+        for (int i = 1; i < model.njoints; ++i)
         {
             Pass1::run(model.joints[i],
                        data.joints[i],
@@ -465,13 +464,13 @@ namespace jiminy::pinocchio_overload
         }
 
         typedef AbaBackwardStep<Scalar, Options, JointCollectionTpl> Pass2;
-        for (int32_t i = model.njoints - 1; i > 0; --i)
+        for (int i = model.njoints - 1; i > 0; --i)
         {
             Pass2::run(model.joints[i], data.joints[i], typename Pass2::ArgsType(model, data));
         }
 
         typedef pinocchio::AbaForwardStep2<Scalar, Options, JointCollectionTpl> Pass3;
-        for (int32_t i = 1; i < model.njoints; ++i)
+        for (int i = 1; i < model.njoints; ++i)
         {
             Pass3::run(model.joints[i], data.joints[i], typename Pass3::ArgsType(model, data));
         }
diff --git a/core/include/jiminy/core/robot/robot.h b/core/include/jiminy/core/robot/robot.h
index 9f4b311fe..3ee65ed5f 100644
--- a/core/include/jiminy/core/robot/robot.h
+++ b/core/include/jiminy/core/robot/robot.h
@@ -2,6 +2,7 @@
 #define JIMINY_ROBOT_H
 
 #include "jiminy/core/fwd.h"
+#include "jiminy/core/hardware/fwd.h"
 #include "jiminy/core/utilities/helpers.h"
 #include "jiminy/core/robot/model.h"
 
@@ -29,7 +30,7 @@ namespace jiminy
         DISABLE_COPY(Robot)
 
     public:
-        Robot();
+        explicit Robot() noexcept;
         virtual ~Robot();
 
         auto shared_from_this() { return shared_from(this); }
@@ -81,7 +82,7 @@ namespace jiminy
                                                         const std::string & sensorName) const;
 
         hresult_t setOptions(const GenericConfig & robotOptions);
-        GenericConfig getOptions() const;
+        GenericConfig getOptions() const noexcept;
         hresult_t setMotorOptions(const std::string & motorName,
                                   const GenericConfig & motorOptions);
         hresult_t setMotorsOptions(const GenericConfig & motorsOptions);
@@ -118,8 +119,8 @@ namespace jiminy
 
         const std::vector<std::string> & getMotorsNames() const;
         std::vector<pinocchio::JointIndex> getMotorsModelIdx() const;
-        std::vector<std::vector<int32_t>> getMotorsPositionIdx() const;
-        std::vector<int32_t> getMotorsVelocityIdx() const;
+        std::vector<std::vector<Eigen::Index>> getMotorsPositionIdx() const;
+        std::vector<Eigen::Index> getMotorsVelocityIdx() const;
         const std::unordered_map<std::string, std::vector<std::string>> & getSensorsNames() const;
         const std::vector<std::string> & getSensorsNames(const std::string & sensorType) const;
 
@@ -140,26 +141,26 @@ namespace jiminy
         virtual hresult_t refreshProxies() override;
 
     protected:
-        bool isTelemetryConfigured_;
-        std::shared_ptr<TelemetryData> telemetryData_;
-        motorsHolder_t motorsHolder_;
-        sensorsGroupHolder_t sensorsGroupHolder_;
-        std::unordered_map<std::string, bool> sensorTelemetryOptions_;
+        bool isTelemetryConfigured_{false};
+        std::shared_ptr<TelemetryData> telemetryData_{nullptr};
+        motorsHolder_t motorsHolder_{};
+        sensorsGroupHolder_t sensorsGroupHolder_{};
+        std::unordered_map<std::string, bool> sensorTelemetryOptions_{};
         /// \brief Name of the motors.
-        std::vector<std::string> motorsNames_;
+        std::vector<std::string> motorsNames_{};
         /// \brief Name of the sensors.
-        std::unordered_map<std::string, std::vector<std::string>> sensorsNames_;
+        std::unordered_map<std::string, std::vector<std::string>> sensorsNames_{};
         /// \brief Fieldnames of the command.
-        std::vector<std::string> logFieldnamesCommand_;
+        std::vector<std::string> logFieldnamesCommand_{};
         /// \brief Fieldnames of the motors effort.
-        std::vector<std::string> logFieldnamesMotorEffort_;
+        std::vector<std::string> logFieldnamesMotorEffort_{};
         /// \brief The number of motors.
-        uint64_t nmotors_;
+        uint64_t nmotors_{0U};
 
     private:
-        std::unique_ptr<MutexLocal> mutexLocal_;
+        std::unique_ptr<MutexLocal> mutexLocal_{std::make_unique<MutexLocal>()};
         std::shared_ptr<MotorSharedDataHolder_t> motorsSharedHolder_;
-        sensorsSharedHolder_t sensorsSharedHolder_;
+        sensorsSharedHolder_t sensorsSharedHolder_{};
     };
 }
 
diff --git a/core/include/jiminy/core/solver/constraint_solvers.h b/core/include/jiminy/core/solver/constraint_solvers.h
index 533c02f2f..9faace665 100644
--- a/core/include/jiminy/core/solver/constraint_solvers.h
+++ b/core/include/jiminy/core/solver/constraint_solvers.h
@@ -11,11 +11,11 @@ namespace jiminy
 
     struct ConstraintBlock
     {
-        double lo;
-        double hi;
-        bool isZero;
-        Eigen::Index fIdx[3];
-        std::uint_fast8_t fSize;
+        double lo{0.0};
+        double hi{0.0};
+        bool isZero{false};
+        Eigen::Index fIdx[3]{-1, -1, -1};
+        std::uint_fast8_t fSize{0};
     };
 
     struct ConstraintData
@@ -24,22 +24,21 @@ namespace jiminy
         DISABLE_COPY(ConstraintData)
 
     public:
-        ConstraintData() = default;
+        explicit ConstraintData() = default;
         ConstraintData(ConstraintData && constraintData) = default;
 
     public:
-        AbstractConstraintBase * constraint;
-        Eigen::Index startIdx;
-        bool isInactive;
-        Eigen::Index dim;
-        ConstraintBlock blocks[3];
-        std::uint_fast8_t nBlocks;
+        AbstractConstraintBase * constraint{nullptr};
+        Eigen::Index startIdx{-1};
+        bool isInactive{false};
+        Eigen::Index dim{-1};
+        ConstraintBlock blocks[3]{};
+        std::uint_fast8_t nBlocks{0};
     };
 
     class JIMINY_DLLAPI AbstractConstraintSolver
     {
     public:
-        AbstractConstraintSolver() = default;
         virtual ~AbstractConstraintSolver() = default;
 
         /// \brief Compute the solution of the Nonlinear Complementary Problem:
@@ -60,14 +59,14 @@ namespace jiminy
         DISABLE_COPY(PGSSolver)
 
     public:
-        PGSSolver(const pinocchio::Model * model,
-                  pinocchio::Data * data,
-                  constraintsHolder_t * constraintsHolder,
-                  double friction,
-                  double torsion,
-                  double tolAbs,
-                  double tolRel,
-                  uint32_t maxIter);
+        explicit PGSSolver(const pinocchio::Model * model,
+                           pinocchio::Data * data,
+                           constraintsHolder_t * constraintsHolder,
+                           double friction,
+                           double torsion,
+                           double tolAbs,
+                           double tolRel,
+                           uint32_t maxIter) noexcept;
         virtual ~PGSSolver() = default;
 
         virtual bool SolveBoxedForwardDynamics(double dampingInv,
@@ -91,18 +90,18 @@ namespace jiminy
         double tolRel_;
 
         /// \brief Matrix holding the jacobian of the constraints.
-        Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> J_;
+        Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> J_{};
         /// \brief Vector holding the drift of the constraints.
-        Eigen::VectorXd gamma_;
+        Eigen::VectorXd gamma_{};
         /// \brief Vector holding the multipliers of the constraints.
-        Eigen::VectorXd lambda_;
-        std::vector<ConstraintData> constraintsData_;
+        Eigen::VectorXd lambda_{};
+        std::vector<ConstraintData> constraintsData_{};
 
-        Eigen::VectorXd b_;
-        Eigen::VectorXd y_;
-        Eigen::VectorXd yPrev_;
+        Eigen::VectorXd b_{};
+        Eigen::VectorXd y_{};
+        Eigen::VectorXd yPrev_{};
 
-        bool isLcpFullyUpToDate_;
+        bool isLcpFullyUpToDate_{false};
     };
 }
 
diff --git a/core/include/jiminy/core/stepper/abstract_runge_kutta_stepper.h b/core/include/jiminy/core/stepper/abstract_runge_kutta_stepper.h
index 081366db2..1af8a3ece 100644
--- a/core/include/jiminy/core/stepper/abstract_runge_kutta_stepper.h
+++ b/core/include/jiminy/core/stepper/abstract_runge_kutta_stepper.h
@@ -15,12 +15,12 @@ namespace jiminy
     public:
         /// \param[in] f Dynamics function, with signature `a = f(t, q, v)`.
         /// \param[in] robots Robots whose dynamics the stepper will work on.
-        AbstractRungeKuttaStepper(const systemDynamics & f,
-                                  const std::vector<const Robot *> & robots,
-                                  const Eigen::MatrixXd & RungeKuttaMatrix,
-                                  const Eigen::VectorXd & bWeights,
-                                  const Eigen::VectorXd & cNodes,
-                                  bool isFSAL);
+        explicit AbstractRungeKuttaStepper(const systemDynamics & f,
+                                           const std::vector<const Robot *> & robots,
+                                           const Eigen::MatrixXd & RungeKuttaMatrix,
+                                           const Eigen::VectorXd & bWeights,
+                                           const Eigen::VectorXd & cNodes,
+                                           bool isFSAL) noexcept;
         virtual ~AbstractRungeKuttaStepper() = default;
 
     protected:
diff --git a/core/include/jiminy/core/stepper/abstract_stepper.h b/core/include/jiminy/core/stepper/abstract_stepper.h
index 82dd1063e..4b8882742 100644
--- a/core/include/jiminy/core/stepper/abstract_stepper.h
+++ b/core/include/jiminy/core/stepper/abstract_stepper.h
@@ -27,7 +27,8 @@ namespace jiminy
     public:
         /// \param[in] f Dynamics function, with signature `a = f(t, q, v)`.
         /// \param[in] robots Robots whose dynamics the stepper will work on.
-        AbstractStepper(const systemDynamics & f, const std::vector<const Robot *> & robots);
+        explicit AbstractStepper(const systemDynamics & f,
+                                 const std::vector<const Robot *> & robots) noexcept;
         virtual ~AbstractStepper() = default;
 
         /// \brief Attempt to integrate the system from time t to time t + dt.
diff --git a/core/include/jiminy/core/stepper/euler_explicit_stepper.h b/core/include/jiminy/core/stepper/euler_explicit_stepper.h
index 82c9adb96..4a7b7e2a6 100644
--- a/core/include/jiminy/core/stepper/euler_explicit_stepper.h
+++ b/core/include/jiminy/core/stepper/euler_explicit_stepper.h
@@ -11,9 +11,7 @@ namespace jiminy
     class JIMINY_DLLAPI EulerExplicitStepper : public AbstractStepper
     {
     public:
-        /// \param[in] f Dynamics function, with signature `a = f(t, q, v)`.
-        /// \param[in] robots Robots whose dynamics the stepper will work on.
-        EulerExplicitStepper(const systemDynamics & f, const std::vector<const Robot *> & robots);
+        using AbstractStepper::AbstractStepper;
 
     protected:
         /// \brief Internal tryStep method wrapping the arguments as state_t and stateDerivative_t.
diff --git a/core/include/jiminy/core/stepper/lie_group.h b/core/include/jiminy/core/stepper/lie_group.h
index c98b60b3a..f0009028a 100644
--- a/core/include/jiminy/core/stepper/lie_group.h
+++ b/core/include/jiminy/core/stepper/lie_group.h
@@ -11,7 +11,6 @@
 #include "pinocchio/algorithm/joint-configuration.hpp"  // `pinocchio::integrate`, `pinocchio::difference`
 
 #include "jiminy/core/fwd.h"
-#include "jiminy/core/traits.h"
 #include "jiminy/core/robot/robot.h"
 
 
@@ -1034,11 +1033,26 @@ namespace Eigen
         {                                                                                      \
         }                                                                                      \
                                                                                                \
-        const Robot * const & robot() const { return robot_; }                                 \
-        Eigen::Ref<Eigen::VectorXd> & VAR1() { return EIGEN_CAT(VAR1, Ref_); }                 \
-        const Eigen::Ref<Eigen::VectorXd> & VAR1() const { return EIGEN_CAT(VAR1, Ref_); }     \
-        Eigen::Ref<Eigen::VectorXd> & VAR2() { return EIGEN_CAT(VAR2, Ref_); }                 \
-        const Eigen::Ref<Eigen::VectorXd> & VAR2() const { return EIGEN_CAT(VAR2, Ref_); }     \
+        const Robot * const & robot() const                                                    \
+        {                                                                                      \
+            return robot_;                                                                     \
+        }                                                                                      \
+        Eigen::Ref<Eigen::VectorXd> & VAR1()                                                   \
+        {                                                                                      \
+            return EIGEN_CAT(VAR1, Ref_);                                                      \
+        }                                                                                      \
+        const Eigen::Ref<Eigen::VectorXd> & VAR1() const                                       \
+        {                                                                                      \
+            return EIGEN_CAT(VAR1, Ref_);                                                      \
+        }                                                                                      \
+        Eigen::Ref<Eigen::VectorXd> & VAR2()                                                   \
+        {                                                                                      \
+            return EIGEN_CAT(VAR2, Ref_);                                                      \
+        }                                                                                      \
+        const Eigen::Ref<Eigen::VectorXd> & VAR2() const                                       \
+        {                                                                                      \
+            return EIGEN_CAT(VAR2, Ref_);                                                      \
+        }                                                                                      \
                                                                                                \
     protected:                                                                                 \
         const Robot * robot_;                                                                  \
diff --git a/core/include/jiminy/core/stepper/runge_kutta4_stepper.h b/core/include/jiminy/core/stepper/runge_kutta4_stepper.h
index 65d9adab6..353214397 100644
--- a/core/include/jiminy/core/stepper/runge_kutta4_stepper.h
+++ b/core/include/jiminy/core/stepper/runge_kutta4_stepper.h
@@ -30,7 +30,8 @@ namespace jiminy
     public:
         /// \param[in] f Dynamics function, with signature `a = f(t, q, v)`.
         /// \param[in] robots Robots whose dynamics the stepper will work on.
-        RungeKutta4Stepper(const systemDynamics & f, const std::vector<const Robot *> & robots);
+        RungeKutta4Stepper(const systemDynamics & f,
+                           const std::vector<const Robot *> & robots) noexcept;
     };
 }
 
diff --git a/core/include/jiminy/core/stepper/runge_kutta_dopri_stepper.h b/core/include/jiminy/core/stepper/runge_kutta_dopri_stepper.h
index 4261b7eb6..edf98b077 100644
--- a/core/include/jiminy/core/stepper/runge_kutta_dopri_stepper.h
+++ b/core/include/jiminy/core/stepper/runge_kutta_dopri_stepper.h
@@ -49,10 +49,10 @@ namespace jiminy
         /// \param[in] robots Robots whose dynamics the stepper will work on.
         /// \param[in] tolRel Relative tolerance used to assess step success and timestep update.
         /// \param[in] tolAbs Absolute tolerance used to assess step success and timestep update.
-        RungeKuttaDOPRIStepper(const systemDynamics & f,
-                               const std::vector<const Robot *> & robots,
-                               double tolRel,
-                               double tolAbs);
+        explicit RungeKuttaDOPRIStepper(const systemDynamics & f,
+                                        const std::vector<const Robot *> & robots,
+                                        double tolRel,
+                                        double tolAbs) noexcept;
 
     protected:
         /// \brief Determine if step has succeeded or failed, and adjust dt.
diff --git a/core/include/jiminy/core/telemetry/fwd.h b/core/include/jiminy/core/telemetry/fwd.h
new file mode 100644
index 000000000..8c591a1b8
--- /dev/null
+++ b/core/include/jiminy/core/telemetry/fwd.h
@@ -0,0 +1,36 @@
+#ifndef JIMINY_FORWARD_TELEMETRY_H
+#define JIMINY_FORWARD_TELEMETRY_H
+
+#include "jiminy/core/fwd.h"
+
+
+namespace jiminy
+{
+    /// \brief Version of the telemetry format.
+    inline constexpr int32_t TELEMETRY_VERSION{1};
+    /// \brief Number of integers in the data section.
+    inline constexpr std::string_view NUM_INTS{"NumIntEntries"};
+    /// \brief Number of floats in the data section.
+    inline constexpr std::string_view NUM_FLOATS{"NumFloatEntries"};
+    /// \brief Marker of the beginning the constants section.
+    inline constexpr std::string_view START_CONSTANTS{"StartConstants"};
+    /// \brief Marker of the beginning the columns section.
+    inline constexpr std::string_view START_COLUMNS{"StartColumns"};
+    /// \brief Marker of the beginning of a line of data.
+    inline constexpr std::string_view START_LINE_TOKEN{"StartLine"};
+    /// \brief Marker of the beginning of the data section.
+    inline constexpr std::string_view START_DATA{"StartData"};
+
+    struct JIMINY_DLLAPI LogData
+    {
+        int32_t version;
+        static_map_t<std::string, std::string> constants;
+        double timeUnit;
+        VectorX<int64_t> times;
+        std::vector<std::string> variableNames;
+        MatrixX<int64_t> integerValues;
+        MatrixX<double> floatValues;
+    };
+}
+
+#endif  // JIMINY_FORWARD_TELEMETRY_H
diff --git a/core/include/jiminy/core/telemetry/telemetry_data.h b/core/include/jiminy/core/telemetry/telemetry_data.h
index ff99af969..5fb4ef903 100644
--- a/core/include/jiminy/core/telemetry/telemetry_data.h
+++ b/core/include/jiminy/core/telemetry/telemetry_data.h
@@ -8,21 +8,6 @@
 
 namespace jiminy
 {
-    /// \brief Version of the telemetry format.
-    inline constexpr int32_t TELEMETRY_VERSION{1};
-    /// \brief Number of integers in the data section.
-    inline constexpr std::string_view NUM_INTS{"NumIntEntries"};
-    /// \brief Number of floats in the data section.
-    inline constexpr std::string_view NUM_FLOATS{"NumFloatEntries"};
-    /// \brief Marker of the beginning the constants section.
-    inline constexpr std::string_view START_CONSTANTS{"StartConstants"};
-    /// \brief Marker of the beginning the columns section.
-    inline constexpr std::string_view START_COLUMNS{"StartColumns"};
-    /// \brief Marker of the beginning of a line of data.
-    inline constexpr std::string_view START_LINE_TOKEN{"StartLine"};
-    /// \brief Marker of the beginning of the data section.
-    inline constexpr std::string_view START_DATA{"StartData"};
-
     /// \brief This class manages the data structures of the telemetry.
     class JIMINY_DLLAPI TelemetryData
     {
@@ -30,11 +15,10 @@ namespace jiminy
         DISABLE_COPY(TelemetryData)
 
     public:
-        TelemetryData();
-        ~TelemetryData() = default;
+        explicit TelemetryData() = default;
 
         /// \brief Reset the telemetry before starting to use the telemetry.
-        void reset();
+        void reset() noexcept;
 
         /// \brief Register a new variable in for telemetry.
         ///
@@ -69,13 +53,13 @@ namespace jiminy
         // Must use dequeue to preserve pointer addresses after resize
 
         /// \brief Memory to handle constants.
-        std::deque<std::pair<std::string, std::string>> constantsRegistry_;
+        std::deque<std::pair<std::string, std::string>> constantsRegistry_{};
         /// \brief Memory to handle integers.
-        std::deque<std::pair<std::string, int64_t>> integersRegistry_;
+        std::deque<std::pair<std::string, int64_t>> integersRegistry_{};
         /// \brief Memory to handle floats.
-        std::deque<std::pair<std::string, double>> floatsRegistry_;
+        std::deque<std::pair<std::string, double>> floatsRegistry_{};
         /// \brief Whether registering is available.
-        bool isRegisteringAvailable_;
+        bool isRegisteringAvailable_{true};
     };
 }
 
diff --git a/core/include/jiminy/core/telemetry/telemetry_recorder.h b/core/include/jiminy/core/telemetry/telemetry_recorder.h
index 938777bc5..7eb345f89 100644
--- a/core/include/jiminy/core/telemetry/telemetry_recorder.h
+++ b/core/include/jiminy/core/telemetry/telemetry_recorder.h
@@ -9,17 +9,7 @@
 
 namespace jiminy
 {
-    struct JIMINY_DLLAPI LogData
-    {
-        int32_t version;
-        static_map_t<std::string, std::string> constants;
-        double timeUnit;
-        VectorX<int64_t> times;
-        std::vector<std::string> variableNames;
-        MatrixX<int64_t> integerValues;
-        MatrixX<double> floatValues;
-    };
-
+    struct LogData;
     class TelemetryData;
 
     /// \class This class is responsible of writing recorded data to devices.
@@ -29,7 +19,7 @@ namespace jiminy
         DISABLE_COPY(TelemetryRecorder)
 
     public:
-        TelemetryRecorder() = default;
+        explicit TelemetryRecorder() = default;
         ~TelemetryRecorder();
 
         /// \brief Initialize the recorder.
@@ -64,29 +54,29 @@ namespace jiminy
         hresult_t createNewChunk();
 
     private:
-        std::deque<MemoryDevice> flows_;
+        std::deque<MemoryDevice> flows_{};
 
-        bool isInitialized_;
+        bool isInitialized_{false};
 
-        int64_t recordedBytesLimits_;
-        int64_t recordedBytesDataLine_;
+        std::size_t recordedBytesLimits_{0};
+        std::size_t recordedBytesDataLine_{0};
         /// \brief Bytes recorded in the file.
-        int64_t recordedBytes_;
+        std::size_t recordedBytes_{0};
         /// \brief Size in byte of the header.
-        int64_t headerSize_;
+        std::size_t headerSize_{0};
 
         /// \brief Pointer to the integer registry.
-        const std::deque<std::pair<std::string, int64_t>> * integersRegistry_;
+        const std::deque<std::pair<std::string, int64_t>> * integersRegistry_{nullptr};
         /// \brief Size in bytes of the integer data section.
-        int64_t integerSectionSize_;
+        int64_t integerSectionSize_{-1};
         /// \brief Pointer to the float registry.
-        const std::deque<std::pair<std::string, double>> * floatsRegistry_;
+        const std::deque<std::pair<std::string, double>> * floatsRegistry_{nullptr};
         /// \brief Size in bytes of the float data section.
-        int64_t floatSectionSize_;
+        int64_t floatSectionSize_{-1};
 
         /// \brief Precision to use when logging the time.
-        double timeUnitInv_;
+        double timeUnitInv_{NAN};
     };
 }
 
-#endif  // JIMINY_TELEMETRY_RECORDER_H
\ No newline at end of file
+#endif  // JIMINY_TELEMETRY_RECORDER_H
diff --git a/core/include/jiminy/core/telemetry/telemetry_sender.h b/core/include/jiminy/core/telemetry/telemetry_sender.h
index 6a7c25adf..855c31d1a 100644
--- a/core/include/jiminy/core/telemetry/telemetry_sender.h
+++ b/core/include/jiminy/core/telemetry/telemetry_sender.h
@@ -13,8 +13,6 @@ namespace jiminy
 {
     class TelemetryData;
 
-    inline constexpr std::string_view DEFAULT_TELEMETRY_NAMESPACE{"Uninitialized Object"};
-
     /// \brief Class to inherit for sending telemetry data.
     class JIMINY_DLLAPI TelemetrySender
     {
@@ -28,9 +26,8 @@ namespace jiminy
         template<typename... T>
         using telemetry_data_registry_t = std::vector<std::variant<telemetry_data_pair_t<T>...>>;
 
-        explicit TelemetrySender();
+        explicit TelemetrySender() = default;
         explicit TelemetrySender(TelemetrySender &&) = default;
-        ~TelemetrySender() = default;
 
         /// \brief Configure the object.
         ///
@@ -52,7 +49,7 @@ namespace jiminy
         /// \param[in] value Pointer to the newly recorded field.
         template<typename Scalar>
         std::enable_if_t<std::is_arithmetic_v<Scalar>, hresult_t>
-        registerVariable(const std::string & name, const Scalar * value);
+        registerVariable(const std::string & name, const Scalar * valuePtr);
 
         template<typename KeyType, typename Derived>
         hresult_t registerVariable(const std::vector<KeyType> & fieldnames,
@@ -75,13 +72,13 @@ namespace jiminy
 
     protected:
         /// \brief Name of the logged object.
-        std::string objectName_;
+        std::string objectName_{"Uninitialized Object"};
 
     private:
-        std::shared_ptr<TelemetryData> telemetryData_;
+        std::shared_ptr<TelemetryData> telemetryData_{nullptr};
         /// \brief Associate each variable pointer provided by the user to their reserved position
         ///        in the contiguous storage of telemetry data.
-        telemetry_data_registry_t<double, int64_t> bufferPosition_;
+        telemetry_data_registry_t<double, int64_t> bufferPosition_{};
     };
 }
 
diff --git a/core/include/jiminy/core/telemetry/telemetry_sender.hxx b/core/include/jiminy/core/telemetry/telemetry_sender.hxx
index 2e4910a58..acf5cf25c 100644
--- a/core/include/jiminy/core/telemetry/telemetry_sender.hxx
+++ b/core/include/jiminy/core/telemetry/telemetry_sender.hxx
@@ -9,17 +9,18 @@ namespace jiminy
 {
     template<typename Scalar>
     std::enable_if_t<std::is_arithmetic_v<Scalar>, hresult_t>
-    TelemetrySender::registerVariable(const std::string & name, const Scalar * value)
+    TelemetrySender::registerVariable(const std::string & name, const Scalar * valuePtr)
     {
         Scalar * positionInBuffer = nullptr;
         const std::string fullName =
             addCircumfix(name, objectName_, {}, TELEMETRY_FIELDNAME_DELIMITER);
-
         hresult_t returnCode = telemetryData_->registerVariable(fullName, positionInBuffer);
+
         if (returnCode == hresult_t::SUCCESS)
         {
-            bufferPosition_.emplace_back(telemetry_data_pair_t<Scalar>{value, positionInBuffer});
-            *positionInBuffer = *value;
+            *positionInBuffer = *valuePtr;
+            bufferPosition_.emplace_back(
+                telemetry_data_pair_t<Scalar>{valuePtr, positionInBuffer});
         }
 
         return returnCode;
diff --git a/core/include/jiminy/core/traits.h b/core/include/jiminy/core/traits.h
index 9660b3700..020b32311 100644
--- a/core/include/jiminy/core/traits.h
+++ b/core/include/jiminy/core/traits.h
@@ -26,6 +26,49 @@ namespace pinocchio
 
 namespace jiminy
 {
+    // ******************************** is_contiguous_container ******************************** //
+
+    namespace details
+    {
+        template<class...>
+        struct voider
+        {
+            using type = void;
+        };
+
+        template<class... Ts>
+        using void_t = typename voider<Ts...>::type;
+
+        template<template<class...> class, class, class...>
+        struct canApplyImply : std::false_type
+        {
+        };
+
+        template<template<class...> class Z, class... Ts>
+        struct canApplyImply<Z, void_t<Z<Ts...>>, Ts...> : std::true_type
+        {
+        };
+
+        template<template<class...> class Z, class... Ts>
+        using canApply = canApplyImply<Z, void, Ts...>;
+
+        template<class T>
+        using sizeFunType = decltype(std::declval<T>().size());
+
+        template<class T>
+        using hasSize = canApply<sizeFunType, T>;
+
+        template<class T, class I = std::size_t>
+        using rawIndexFunType = decltype(std::declval<T>().data()[std::declval<I>()]);
+
+        template<class T, class I = std::size_t>
+        using isContiguousIndexable = canApply<rawIndexFunType, T, I>;
+    }
+
+    template<class T, class I = std::size_t>
+    inline constexpr bool is_contiguous_container_v =
+        std::conjunction<details::hasSize<T>, details::isContiguousIndexable<T, I>>::value;
+
     // ************************************* remove_cvref ************************************** //
 
     // TODO: Remove it when moving to C++20 as it has been added to the standard library
@@ -72,6 +115,21 @@ namespace jiminy
     template<typename T>
     inline constexpr bool is_vector_v = is_vector<std::decay_t<T>>::value;
 
+    // **************************************** is_array *************************************** //
+
+    template<typename T>
+    struct is_array : std::false_type
+    {
+    };
+
+    template<typename T, int I>
+    struct is_array<std::array<T, I>> : std::true_type
+    {
+    };
+
+    template<typename T>
+    inline constexpr bool is_array_v = is_array<std::decay_t<T>>::value;
+
     // **************************************** is_map ***************************************** //
 
     namespace details::is_map
@@ -209,11 +267,11 @@ namespace jiminy
     };
 
     template<typename T>
-    inline constexpr bool is_eigen_ref_v = is_eigen_ref<T>::value;
+    inline constexpr bool is_eigen_ref_v = is_eigen_ref<std::decay_t<T>>::value;
 
     // *************************************** is_eigen **************************************** //
 
-    namespace details::is_eigen
+    namespace details::is_eigen_plain
     {
         template<typename T, int RowsAtCompileTime, int ColsAtCompileTime>
         std::true_type test(const Eigen::Matrix<T, RowsAtCompileTime, ColsAtCompileTime> *);
@@ -225,21 +283,24 @@ namespace jiminy
         struct Test : public decltype(test(std::declval<std::add_pointer_t<T>>()))
         {
         };
-    }  // namespace details::is_eigen
+    }
 
     template<typename T, typename Enable = void>
-    struct is_eigen : public std::false_type
+    struct is_eigen_plain : public std::false_type
     {
     };
 
     template<typename T>
-    struct is_eigen<T, typename std::enable_if_t<details::is_eigen::Test<T>::value>> :
+    struct is_eigen_plain<T, typename std::enable_if_t<details::is_eigen_plain::Test<T>::value>> :
     std::true_type
     {
     };
 
     template<typename T>
-    inline constexpr bool is_eigen_v = is_eigen<T>::value;
+    inline constexpr bool is_eigen_plain_v = is_eigen_plain<std::decay_t<T>>::value;
+
+    template<class T>
+    inline constexpr bool is_eigen_v = std::disjunction<is_eigen_plain<T>, is_eigen_ref<T>>::value;
 
     // ********************************** is_pinocchio_joint_ ********************************** //
 
diff --git a/core/include/jiminy/core/utilities/helpers.h b/core/include/jiminy/core/utilities/helpers.h
index 4610b4359..0a0de2f5f 100644
--- a/core/include/jiminy/core/utilities/helpers.h
+++ b/core/include/jiminy/core/utilities/helpers.h
@@ -24,15 +24,15 @@ namespace jiminy
         DISABLE_COPY(MutexLocal)
 
     public:
-        MutexLocal();
+        explicit MutexLocal() = default;
         MutexLocal(MutexLocal && other) = default;
 
         ~MutexLocal();
 
-        bool isLocked() const;
+        bool isLocked() const noexcept;
 
     private:
-        std::shared_ptr<bool> isLocked_;
+        std::shared_ptr<bool> isLocked_{std::make_shared<bool>(false)};
     };
 
     class JIMINY_DLLAPI LockGuardLocal
@@ -41,7 +41,7 @@ namespace jiminy
         DISABLE_COPY(LockGuardLocal)
 
     public:
-        LockGuardLocal(MutexLocal & mutexLocal);
+        explicit LockGuardLocal(MutexLocal & mutexLocal) noexcept;
         LockGuardLocal(LockGuardLocal && other) = default;
 
         ~LockGuardLocal();
@@ -57,14 +57,15 @@ namespace jiminy
         using Time = std::chrono::high_resolution_clock;
 
     public:
-        Timer();
-        void tic();
-        void toc();
+        explicit Timer() noexcept;
+
+        void tic() noexcept;
+        void toc() noexcept;
 
     public:
-        std::chrono::time_point<Time> t0;
-        std::chrono::time_point<Time> tf;
-        double dt;
+        std::chrono::time_point<Time> t0{};
+        std::chrono::time_point<Time> tf{};
+        double dt{0.0};
     };
 
     // ****************************** Generic template utilities ******************************* //
@@ -123,9 +124,6 @@ namespace jiminy
 
     // ********************** Math utilities *************************
 
-    template<typename T0, typename T1, typename... Ts>
-    typename std::common_type_t<T0, T1, Ts...> min(T0 && val1, T1 && val2, Ts &&... vs);
-
     template<typename DerivedType1, typename DerivedType2, typename DerivedType3>
     Eigen::MatrixBase<DerivedType1> clamp(const Eigen::MatrixBase<DerivedType1> & data,
                                           const Eigen::MatrixBase<DerivedType2> & minThr,
diff --git a/core/include/jiminy/core/utilities/helpers.hxx b/core/include/jiminy/core/utilities/helpers.hxx
index 3176149d3..229c3d975 100644
--- a/core/include/jiminy/core/utilities/helpers.hxx
+++ b/core/include/jiminy/core/utilities/helpers.hxx
@@ -44,18 +44,6 @@ namespace jiminy
 
     // *************************** Math *****************************
 
-    template<typename T>
-    T min(T && t)
-    {
-        return std::forward<T>(t);
-    }
-
-    template<typename T0, typename T1, typename... Ts>
-    typename std::common_type_t<T0, T1, Ts...> min(T0 && val1, T1 && val2, Ts &&... vs)
-    {
-        return min(std::min(val1, val2), std::forward<Ts>(vs)...);
-    }
-
     template<typename DerivedType>
     auto clamp(const Eigen::MatrixBase<DerivedType> & data, double minThr, double maxThr)
     {
diff --git a/core/include/jiminy/core/utilities/json.hxx b/core/include/jiminy/core/utilities/json.hxx
index 522ca522b..d2f722099 100644
--- a/core/include/jiminy/core/utilities/json.hxx
+++ b/core/include/jiminy/core/utilities/json.hxx
@@ -1,6 +1,6 @@
 #include "json/json.h"
 
-#include "jiminy/core/traits.h"
+#include "jiminy/core/fwd.h"
 #include "jiminy/core/io/json_writer.h"
 
 
@@ -99,7 +99,7 @@ namespace jiminy
     template<>
     Json::Value convertToJson<GenericConfig>(const GenericConfig & value);
 
-    // ************* Convertion from JSON utilities *****************
+    // ************* Conversion from JSON utilities *****************
 
     template<typename T>
     std::enable_if_t<!is_vector_v<T>, T> convertFromJson(const Json::Value & /* value */)
diff --git a/core/include/jiminy/core/utilities/pinocchio.h b/core/include/jiminy/core/utilities/pinocchio.h
index 3738f769e..91b5cc01c 100644
--- a/core/include/jiminy/core/utilities/pinocchio.h
+++ b/core/include/jiminy/core/utilities/pinocchio.h
@@ -1,25 +1,28 @@
 #ifndef JIMINY_PINOCCHIO_H
 #define JIMINY_PINOCCHIO_H
 
-#include <chrono>
 #include <optional>
-#include <type_traits>
+
+#include "pinocchio/multibody/fcl.hpp"  // `pinocchio::GeometryType`
 
 #include "jiminy/core/fwd.h"
 
 
 namespace jiminy
 {
-    JointModelType getJointType(const pinocchio::JointModel & jointModel);
+    JointModelType getJointType(const pinocchio::JointModel & jointModel) noexcept;
 
-    hresult_t getJointTypeFromIdx(
-        const pinocchio::Model & model, pinocchio::JointIndex idIn, JointModelType & jointTypeOut);
+    hresult_t getJointTypeFromIdx(const pinocchio::Model & model,
+                                  pinocchio::JointIndex jointModelIdx,
+                                  JointModelType & jointTypeOut);
 
-    hresult_t getJointNameFromPositionIdx(
-        const pinocchio::Model & model, pinocchio::JointIndex idIn, std::string & jointNameOut);
+    hresult_t getJointNameFromPositionIdx(const pinocchio::Model & model,
+                                          pinocchio::JointIndex jointModelIdx,
+                                          std::string & jointNameOut);
 
-    hresult_t getJointNameFromVelocityIdx(
-        const pinocchio::Model & model, pinocchio::JointIndex idIn, std::string & jointNameOut);
+    hresult_t getJointNameFromVelocityIdx(const pinocchio::Model & model,
+                                          pinocchio::JointIndex jointModelIdx,
+                                          std::string & jointNameOut);
 
     hresult_t getJointTypePositionSuffixes(JointModelType jointTypeIn,
                                            std::vector<std::string_view> & jointTypeSuffixesOut);
@@ -43,24 +46,24 @@ namespace jiminy
 
     hresult_t getJointPositionIdx(const pinocchio::Model & model,
                                   const std::string & jointName,
-                                  std::vector<int32_t> & jointPositionIdx);
+                                  std::vector<Eigen::Index> & jointPositionIdx);
     hresult_t getJointPositionIdx(const pinocchio::Model & model,
                                   const std::string & jointName,
-                                  int32_t & jointPositionFirstIdx);
+                                  Eigen::Index & jointPositionFirstIdx);
     hresult_t getJointsPositionIdx(const pinocchio::Model & model,
                                    const std::vector<std::string> & jointsNames,
-                                   std::vector<int32_t> & jointsPositionIdx,
+                                   std::vector<Eigen::Index> & jointsPositionIdx,
                                    bool firstJointIdxOnly = false);
 
     hresult_t getJointVelocityIdx(const pinocchio::Model & model,
                                   const std::string & jointName,
-                                  std::vector<int32_t> & jointVelocityIdx);
+                                  std::vector<Eigen::Index> & jointVelocityIdx);
     hresult_t getJointVelocityIdx(const pinocchio::Model & model,
                                   const std::string & jointName,
-                                  int32_t & jointVelocityFirstIdx);
+                                  Eigen::Index & jointVelocityFirstIdx);
     hresult_t getJointsVelocityIdx(const pinocchio::Model & model,
                                    const std::vector<std::string> & jointsNames,
-                                   std::vector<int32_t> & jointsVelocityIdx,
+                                   std::vector<Eigen::Index> & jointsVelocityIdx,
                                    bool firstJointIdxOnly = false);
 
     hresult_t JIMINY_DLLAPI isPositionValid(const pinocchio::Model & model,
diff --git a/core/include/jiminy/core/utilities/random.h b/core/include/jiminy/core/utilities/random.h
index 5d566b3a2..08d3bf5de 100644
--- a/core/include/jiminy/core/utilities/random.h
+++ b/core/include/jiminy/core/utilities/random.h
@@ -10,7 +10,8 @@ namespace jiminy
 {
     // ************ Random number generator utilities ***************
 
-    void JIMINY_DLLAPI resetRandomGenerators(const std::optional<uint32_t> & seed = std::nullopt);
+    void JIMINY_DLLAPI resetRandomGenerators(
+        const std::optional<uint32_t> & seed = std::nullopt) noexcept;
 
     hresult_t JIMINY_DLLAPI getRandomSeed(uint32_t & seed);
 
@@ -31,15 +32,65 @@ namespace jiminy
 
     // ************ Continuous 1D Perlin processes ***************
 
+    namespace details
+    {
+        /// \brief Lower Cholesky factor of a Toeplitz positive semi-definite matrix having 1.0 on
+        /// its
+        ///        main diagonal.
+        ///
+        /// \details In practice, it is advisable to combine this algorithm with Tikhonov
+        ///          regularization of relative magnitude 1e-9 to avoid numerical instabilities
+        ///          because of double machine precision.
+        ///
+        /// \see Michael Stewart, Cholesky factorization of semi-definite Toeplitz matrices. Linear
+        ///      Algebra and its Applications, Volume 254, pages 497-525, 1997.
+        ///
+        /// \see
+        /// https://people.sc.fsu.edu/~jburkardt/cpp_src/toeplitz_cholesky/toeplitz_cholesky.html
+        ///
+        /// \param[in] a First row of the matrix to decompose.
+        template<typename DerivedType>
+        MatrixX<typename DerivedType::Scalar>
+        standardToeplitzCholeskyLower(const Eigen::MatrixBase<DerivedType> & coeffs)
+        {
+            using Scalar = typename DerivedType::Scalar;
+
+            // Initialize lower Cholesky factor
+            const Eigen::Index n = coeffs.size();
+            MatrixX<Scalar> l{n, n};
+
+            /* Compute compressed representation of the matrix.
+               It coincides with the Schur generator for Toepliz matrices. */
+            Eigen::Matrix<Scalar, 2, Eigen::Dynamic> g{2, n};
+            g.rowwise() = coeffs.transpose();
+
+            // Run progressive Schur algorithm, adapted to Toepliz matrices
+            l.col(0) = g.row(0);
+            g.row(0).tail(n - 1) = g.row(0).head(n - 1).eval();
+            Eigen::Matrix<Scalar, 2, 2> H{Eigen::Matrix<Scalar, 2, 2>::Ones()};
+            for (Eigen::Index i = 1; i < n; ++i)
+            {
+                const double rho = -g(1, i) / g(0, i);
+                // H << 1.0, rho,
+                //      rho, 1.0;
+                Eigen::Map<Eigen::Vector4d>(H.data()).template segment<2>(1).fill(rho);
+                g.rightCols(n - i) = H * g.rightCols(n - i) / std::sqrt((1.0 - rho) * (1.0 + rho));
+                l.col(i).tail(n - i) = g.row(0).tail(n - i);
+                g.row(0).tail(n - i - 1) = g.row(0).segment(i, n - i - 1).eval();
+            }
+
+            return l;
+        }
+    }
+
     class JIMINY_DLLAPI PeriodicGaussianProcess
     {
     public:
         DISABLE_COPY(PeriodicGaussianProcess)
 
     public:
-        PeriodicGaussianProcess(double wavelength, double period, double scale = 1.0);
-
-        ~PeriodicGaussianProcess() = default;
+        explicit PeriodicGaussianProcess(
+            double wavelength, double period, double scale = 1.0) noexcept;
 
         void reset();
 
@@ -49,23 +100,34 @@ namespace jiminy
         double getPeriod() const;
         double getDt() const;
 
-    protected:
-        void initialize();
-
     private:
         const double wavelength_;
         const double period_;
         const double scale_;
-        const double dt_;
-        const int32_t numTimes_;
-
-        bool isInitialized_;
-        Eigen::VectorXd values_;
-        Eigen::MatrixXd covSqrtRoot_;
+        const double dt_{0.02 * wavelength_};
+        const int numTimes_{static_cast<int>(std::ceil(period_ / dt_))};
+
+        Eigen::VectorXd values_{numTimes_};
+        /// \brief Cholesky decomposition (LLT) of the covariance matrix.
+        ///
+        /// \details All decompositions are equivalent as the covariance matrix is symmetric,
+        ///          namely eigen-values, singular-values, Cholesky and Schur decompositions.
+        ///          Yet, Cholesky is by far the most efficient one.
+        ///          See: https://math.stackexchange.com/q/22825/375496
+        ///          Moreover, the covariance is a positive semi-definite Toepliz matrix, which
+        ///          means that the computational complexity can be reduced even further using an
+        ///          specialized Cholesky decomposition algorithm. */
+        Eigen::MatrixXd covSqrtRoot_{
+            details::standardToeplitzCholeskyLower(Eigen::VectorXd::NullaryExpr(
+                numTimes_,
+                [numTimes = numTimes_, wavelength = wavelength_](double i) {
+                    return std::exp(-2.0 *
+                                    std::pow(std::sin(M_PI / numTimes * i) / wavelength, 2));
+                }))};
     };
 
 
-    /// \see Based on "Smooth random functions, random ODEs, and Gaussianprocesses":
+    /// \see Based on "Smooth random functions, random ODEs, and Gaussian processes":
     ///      https://hal.inria.fr/hal-01944992/file/random_revision2.pdf */
     class JIMINY_DLLAPI PeriodicFourierProcess
     {
@@ -73,9 +135,8 @@ namespace jiminy
         DISABLE_COPY(PeriodicFourierProcess)
 
     public:
-        PeriodicFourierProcess(double wavelength, double period, double scale = 1.0);
-
-        ~PeriodicFourierProcess() = default;
+        explicit PeriodicFourierProcess(
+            double wavelength, double period, double scale = 1.0) noexcept;
 
         void reset();
 
@@ -83,30 +144,34 @@ namespace jiminy
 
         double getWavelength() const;
         double getPeriod() const;
-        int32_t getNumHarmonics() const;
+        int getNumHarmonics() const;
         double getDt() const;
 
-    protected:
-        void initialize();
-
     private:
         const double wavelength_;
         const double period_;
         const double scale_;
-        const double dt_;
-        const int32_t numTimes_;
-        const int32_t numHarmonics_;
-
-        bool isInitialized_;
-        Eigen::VectorXd values_;
-        Eigen::MatrixXd cosMat_;
-        Eigen::MatrixXd sinMat_;
+        const double dt_{0.02 * wavelength_};
+        const int numTimes_{static_cast<int>(std::ceil(period_ / dt_))};
+        const int numHarmonics_{static_cast<int>(std::ceil(period_ / wavelength_))};
+
+        Eigen::VectorXd values_{numTimes_};
+        const Eigen::MatrixXd cosMat_{
+            Eigen::MatrixXd::NullaryExpr(numTimes_,
+                                         numHarmonics_,
+                                         [numTimes = numTimes_](double i, double j)
+                                         { return std::cos(2 * M_PI / numTimes * i * j); })};
+        const Eigen::MatrixXd sinMat_{
+            Eigen::MatrixXd::NullaryExpr(numTimes_,
+                                         numHarmonics_,
+                                         [numTimes = numTimes_](double i, double j)
+                                         { return std::cos(2 * M_PI / numTimes * i * j); })};
     };
 
     class JIMINY_DLLAPI AbstractPerlinNoiseOctave
     {
     public:
-        AbstractPerlinNoiseOctave(double wavelength, double scale);
+        explicit AbstractPerlinNoiseOctave(double wavelength, double scale) noexcept;
         virtual ~AbstractPerlinNoiseOctave() = default;
 
         virtual void reset();
@@ -128,14 +193,13 @@ namespace jiminy
         const double wavelength_;
         const double scale_;
 
-        double shift_;
+        double shift_{0.0};
     };
 
     class JIMINY_DLLAPI RandomPerlinNoiseOctave : public AbstractPerlinNoiseOctave
     {
     public:
-        RandomPerlinNoiseOctave(double wavelength, double scale);
-
+        using AbstractPerlinNoiseOctave::AbstractPerlinNoiseOctave;
         virtual ~RandomPerlinNoiseOctave() = default;
 
         virtual void reset() override final;
@@ -144,14 +208,13 @@ namespace jiminy
         virtual double grad(int32_t knot, double delta) const override final;
 
     private:
-        uint32_t seed_;
+        uint32_t seed_{0};
     };
 
     class JIMINY_DLLAPI PeriodicPerlinNoiseOctave : public AbstractPerlinNoiseOctave
     {
     public:
-        PeriodicPerlinNoiseOctave(double wavelength, double period, double scale);
-
+        explicit PeriodicPerlinNoiseOctave(double wavelength, double period, double scale);
         virtual ~PeriodicPerlinNoiseOctave() = default;
 
         virtual void reset() override final;
@@ -162,7 +225,7 @@ namespace jiminy
     private:
         const double period_;
 
-        std::vector<uint8_t> perm_;
+        std::array<uint8_t, 256> perm_{};
     };
 
     /// \brief  Sum of Perlin noise octaves.
@@ -191,55 +254,42 @@ namespace jiminy
         DISABLE_COPY(AbstractPerlinProcess)
 
     public:
-        AbstractPerlinProcess(double wavelength, double scale = 1.0, uint32_t numOctaves = 8U);
-        virtual ~AbstractPerlinProcess() = default;
-
         void reset();
 
         double operator()(const float & t);
 
-        double getWavelength() const;
-        uint32_t getNumOctaves() const;
-        double getScale() const;
+        double getWavelength() const noexcept;
+        std::size_t getNumOctaves() const noexcept;
+        double getScale() const noexcept;
 
     protected:
-        virtual void initialize() = 0;
+        explicit AbstractPerlinProcess(
+            double scale,
+            std::vector<std::unique_ptr<AbstractPerlinNoiseOctave>> && octaves) noexcept;
 
     protected:
-        const double wavelength_;
-        const uint32_t numOctaves_;
         const double scale_;
 
-        bool isInitialized_;
         std::vector<std::unique_ptr<AbstractPerlinNoiseOctave>> octaves_;
-        double amplitude_;
+        double amplitude_{0.0};
 
-        double grad_;
+        double grad_{0.0};
     };
 
     class JIMINY_DLLAPI RandomPerlinProcess : public AbstractPerlinProcess
     {
     public:
-        RandomPerlinProcess(double wavelength, double scale = 1.0, uint32_t numOctaves = 6U);
-
-        virtual ~RandomPerlinProcess() = default;
-
-    protected:
-        virtual void initialize() override final;
+        explicit RandomPerlinProcess(
+            double wavelength, double scale = 1.0, std::size_t numOctaves = 6U);
     };
 
     class PeriodicPerlinProcess : public AbstractPerlinProcess
     {
     public:
-        PeriodicPerlinProcess(
-            double wavelength, double period, double scale = 1.0, uint32_t numOctaves = 6U);
-
-        virtual ~PeriodicPerlinProcess() = default;
+        explicit PeriodicPerlinProcess(
+            double wavelength, double period, double scale = 1.0, std::size_t numOctaves = 6U);
 
-        double getPeriod() const;
-
-    protected:
-        virtual void initialize() override final;
+        double getPeriod() const noexcept;
 
     private:
         const double period_;
diff --git a/core/src/constraints/distance_constraint.cc b/core/src/constraints/distance_constraint.cc
index 07c475ed1..4007240eb 100644
--- a/core/src/constraints/distance_constraint.cc
+++ b/core/src/constraints/distance_constraint.cc
@@ -9,27 +9,23 @@
 namespace jiminy
 {
     template<>
-    const std::string AbstractConstraintTpl<DistanceConstraint>::type_("DistanceConstraint");
+    const std::string AbstractConstraintTpl<DistanceConstraint>::type_{"DistanceConstraint"};
 
     DistanceConstraint::DistanceConstraint(const std::string & firstFrameName,
-                                           const std::string & secondFrameName) :
+                                           const std::string & secondFrameName) noexcept :
     AbstractConstraintTpl(),
-    framesNames_{firstFrameName, secondFrameName},
-    framesIdx_(),
-    distanceRef_(0.0),
-    firstFrameJacobian_(),
-    secondFrameJacobian_()
+    frameNames_{{firstFrameName, secondFrameName}}
     {
     }
 
-    const std::vector<std::string> & DistanceConstraint::getFramesNames() const
+    const std::array<std::string, 2> & DistanceConstraint::getFramesNames() const noexcept
     {
-        return framesNames_;
+        return frameNames_;
     }
 
-    const std::vector<pinocchio::FrameIndex> & DistanceConstraint::getFramesIdx() const
+    const std::array<pinocchio::FrameIndex, 2> & DistanceConstraint::getFramesIdx() const noexcept
     {
-        return framesIdx_;
+        return frameIndices_;
     }
 
     hresult_t DistanceConstraint::setReferenceDistance(double distanceRef)
@@ -44,7 +40,7 @@ namespace jiminy
         return hresult_t::SUCCESS;
     }
 
-    double DistanceConstraint::getReferenceDistance() const
+    double DistanceConstraint::getReferenceDistance() const noexcept
     {
         return distanceRef_;
     }
@@ -63,26 +59,22 @@ namespace jiminy
         }
 
         // Get frames indices
-        framesIdx_.clear();
-        framesIdx_.reserve(framesNames_.size());
-        for (const std::string & frameName : framesNames_)
+        for (uint8_t i = 0; i < 2; ++i)
         {
-            pinocchio::FrameIndex frameIdx = 0;
             if (returnCode == hresult_t::SUCCESS)
             {
-                returnCode = ::jiminy::getFrameIdx(model->pncModel_, frameName, frameIdx);
-            }
-            if (returnCode == hresult_t::SUCCESS)
-            {
-                framesIdx_.emplace_back(frameIdx);
+                returnCode =
+                    ::jiminy::getFrameIdx(model->pncModel_, frameNames_[i], frameIndices_[i]);
             }
         }
 
         if (returnCode == hresult_t::SUCCESS)
         {
             // Initialize frames jacobians buffers
-            firstFrameJacobian_.setZero(6, model->pncModel_.nv);
-            secondFrameJacobian_.setZero(6, model->pncModel_.nv);
+            for (Matrix6Xd & frameJacobian : frameJacobians_)
+            {
+                frameJacobian.setZero(6, model->pncModel_.nv);
+            }
 
             // Initialize jacobian, drift and multipliers
             jacobian_.setZero(1, model->pncModel_.nv);
@@ -91,8 +83,8 @@ namespace jiminy
 
             // Compute the current distance and use it as reference
             const Eigen::Vector3d deltaPosition =
-                model->pncData_.oMf[framesIdx_[0]].translation() -
-                model->pncData_.oMf[framesIdx_[1]].translation();
+                model->pncData_.oMf[frameIndices_[0]].translation() -
+                model->pncData_.oMf[frameIndices_[1]].translation();
             distanceRef_ = deltaPosition.norm();
         }
 
@@ -112,40 +104,43 @@ namespace jiminy
         auto model = model_.lock();
 
         // Compute direction between frames
-        const Eigen::Vector3d deltaPosition = model->pncData_.oMf[framesIdx_[0]].translation() -
-                                              model->pncData_.oMf[framesIdx_[1]].translation();
+        const Eigen::Vector3d deltaPosition = model->pncData_.oMf[frameIndices_[0]].translation() -
+                                              model->pncData_.oMf[frameIndices_[1]].translation();
         const double deltaPositionNorm = deltaPosition.norm();
         const Eigen::Vector3d direction = deltaPosition / deltaPositionNorm;
 
         // Compute relative velocity between frames
-        const pinocchio::Motion velocity0 = getFrameVelocity(
-            model->pncModel_, model->pncData_, framesIdx_[0], pinocchio::LOCAL_WORLD_ALIGNED);
-        const pinocchio::Motion velocity1 = getFrameVelocity(
-            model->pncModel_, model->pncData_, framesIdx_[1], pinocchio::LOCAL_WORLD_ALIGNED);
-        Eigen::Vector3d deltaVelocity = velocity0.linear() - velocity1.linear();
+        std::array<pinocchio::Motion, 2> frameVelocities{};
+        frameVelocities[0] = getFrameVelocity(
+            model->pncModel_, model->pncData_, frameIndices_[0], pinocchio::LOCAL_WORLD_ALIGNED);
+        frameVelocities[1] = getFrameVelocity(
+            model->pncModel_, model->pncData_, frameIndices_[1], pinocchio::LOCAL_WORLD_ALIGNED);
+        Eigen::Vector3d deltaVelocity = frameVelocities[0].linear() - frameVelocities[1].linear();
 
         // Get jacobian in local frame: J_1 - J_2
-        getFrameJacobian(model->pncModel_,
-                         model->pncData_,
-                         framesIdx_[0],
-                         pinocchio::LOCAL_WORLD_ALIGNED,
-                         firstFrameJacobian_);
-        getFrameJacobian(model->pncModel_,
-                         model->pncData_,
-                         framesIdx_[1],
-                         pinocchio::LOCAL_WORLD_ALIGNED,
-                         secondFrameJacobian_);
+        for (uint8_t i = 0; i < 2; ++i)
+        {
+            getFrameJacobian(model->pncModel_,
+                             model->pncData_,
+                             frameIndices_[i],
+                             pinocchio::LOCAL_WORLD_ALIGNED,
+                             frameJacobians_[i]);
+        }
         jacobian_.noalias() =
-            direction.transpose() * (firstFrameJacobian_ - secondFrameJacobian_).topRows<3>();
+            direction.transpose() * (frameJacobians_[0] - frameJacobians_[1]).topRows<3>();
 
         // Get drift in local frame
-        pinocchio::Motion accel0 = getFrameAcceleration(
-            model->pncModel_, model->pncData_, framesIdx_[0], pinocchio::LOCAL_WORLD_ALIGNED);
-        accel0.linear() += velocity0.angular().cross(velocity0.linear());
-        pinocchio::Motion accel1 = getFrameAcceleration(
-            model->pncModel_, model->pncData_, framesIdx_[1], pinocchio::LOCAL_WORLD_ALIGNED);
-        accel1.linear() += velocity1.angular().cross(velocity1.linear());
-        drift_[0] = direction.dot(accel0.linear() - accel1.linear());
+        std::array<pinocchio::Motion, 2> frameAccelerations{};
+        for (uint8_t i = 0; i < 2; ++i)
+        {
+            frameAccelerations[i] = getFrameAcceleration(model->pncModel_,
+                                                         model->pncData_,
+                                                         frameIndices_[i],
+                                                         pinocchio::LOCAL_WORLD_ALIGNED);
+            frameAccelerations[i].linear() +=
+                frameVelocities[i].angular().cross(frameVelocities[i].linear());
+        }
+        drift_[0] = direction.dot(frameAccelerations[0].linear() - frameAccelerations[1].linear());
 
         /* dDir.T * (dp_A - dp_B) =
                [(dp_A - dp_B) ** 2 - (dir.T * (dp_A - dp_B)) ** 2] / norm(p_A - p_B) */
diff --git a/core/src/constraints/frame_constraint.cc b/core/src/constraints/frame_constraint.cc
index db06d4482..c5d80d04b 100644
--- a/core/src/constraints/frame_constraint.cc
+++ b/core/src/constraints/frame_constraint.cc
@@ -13,7 +13,7 @@ namespace jiminy
     {
         std::vector<uint32_t> vec;
         vec.reserve(N);
-        for (uint32_t i = 0; i < N; ++i)
+        for (uint8_t i = 0; i < N; ++i)
         {
             if (mask[i])
             {
@@ -24,56 +24,42 @@ namespace jiminy
     }
 
     template<>
-    const std::string AbstractConstraintTpl<FrameConstraint>::type_("FrameConstraint");
+    const std::string AbstractConstraintTpl<FrameConstraint>::type_{"FrameConstraint"};
 
     FrameConstraint::FrameConstraint(const std::string & frameName,
-                                     const std::array<bool, 6> & maskDoFs) :
+                                     const std::array<bool, 6> & maskDoFs) noexcept :
     AbstractConstraintTpl(),
-    frameName_(frameName),
-    frameIdx_(0),
-    dofsFixed_(maskToVector<6>(maskDoFs)),
-    transformRef_(),
-    normal_(),
-    rotationLocal_(Eigen::Matrix3d::Identity()),
-    frameJacobian_(),
-    frameDrift_()
+    frameName_{frameName},
+    dofsFixed_{maskToVector<6>(maskDoFs)}
     {
-        dofsFixed_.clear();
-        for (uint32_t i = 0; i < 6; ++i)
-        {
-            if (maskDoFs[i])
-            {
-                dofsFixed_.push_back(i);
-            }
-        }
     }
 
-    const std::string & FrameConstraint::getFrameName() const
+    const std::string & FrameConstraint::getFrameName() const noexcept
     {
         return frameName_;
     }
 
-    pinocchio::FrameIndex FrameConstraint::getFrameIdx() const
+    pinocchio::FrameIndex FrameConstraint::getFrameIdx() const noexcept
     {
         return frameIdx_;
     }
 
-    const std::vector<uint32_t> & FrameConstraint::getDofsFixed() const
+    const std::vector<uint32_t> & FrameConstraint::getDofsFixed() const noexcept
     {
         return dofsFixed_;
     }
 
-    void FrameConstraint::setReferenceTransform(const pinocchio::SE3 & transformRef)
+    void FrameConstraint::setReferenceTransform(const pinocchio::SE3 & transformRef) noexcept
     {
         transformRef_ = transformRef;
     }
 
-    const pinocchio::SE3 & FrameConstraint::getReferenceTransform() const
+    const pinocchio::SE3 & FrameConstraint::getReferenceTransform() const noexcept
     {
         return transformRef_;
     }
 
-    void FrameConstraint::setNormal(const Eigen::Vector3d & normal)
+    void FrameConstraint::setNormal(const Eigen::Vector3d & normal) noexcept
     {
         normal_ = normal;
         rotationLocal_.col(2) = normal_;
@@ -81,7 +67,7 @@ namespace jiminy
         rotationLocal_.col(0) = rotationLocal_.col(1).cross(rotationLocal_.col(2));
     }
 
-    const Eigen::Matrix3d & FrameConstraint::getLocalFrame() const
+    const Eigen::Matrix3d & FrameConstraint::getLocalFrame() const noexcept
     {
         return rotationLocal_;
     }
diff --git a/core/src/constraints/joint_constraint.cc b/core/src/constraints/joint_constraint.cc
index 21cfc1468..bc01f6f48 100644
--- a/core/src/constraints/joint_constraint.cc
+++ b/core/src/constraints/joint_constraint.cc
@@ -1,5 +1,4 @@
-#include "jiminy/core/traits.h"
-#include "jiminy/core/robot/robot.h"
+#include "jiminy/core/robot/model.h"
 
 #include "jiminy/core/constraints/joint_constraint.h"
 
@@ -7,38 +6,36 @@
 namespace jiminy
 {
     template<>
-    const std::string AbstractConstraintTpl<JointConstraint>::type_("JointConstraint");
+    const std::string AbstractConstraintTpl<JointConstraint>::type_{"JointConstraint"};
 
-    JointConstraint::JointConstraint(const std::string & jointName) :
+    JointConstraint::JointConstraint(const std::string & jointName) noexcept :
     AbstractConstraintTpl(),
-    jointName_(jointName),
-    jointIdx_(0),
-    configurationRef_(),
-    isReversed_(false)
+    jointName_{jointName}
     {
     }
 
-    const std::string & JointConstraint::getJointName() const
+    const std::string & JointConstraint::getJointName() const noexcept
     {
         return jointName_;
     }
 
-    pinocchio::JointIndex JointConstraint::getJointIdx() const
+    pinocchio::JointIndex JointConstraint::getJointModelIdx() const noexcept
     {
-        return jointIdx_;
+        return jointModelIdx_;
     }
 
-    void JointConstraint::setReferenceConfiguration(const Eigen::VectorXd & configurationRef)
+    void JointConstraint::setReferenceConfiguration(
+        const Eigen::VectorXd & configurationRef) noexcept
     {
         configurationRef_ = configurationRef;
     }
 
-    const Eigen::VectorXd & JointConstraint::getReferenceConfiguration() const
+    const Eigen::VectorXd & JointConstraint::getReferenceConfiguration() const noexcept
     {
         return configurationRef_;
     }
 
-    void JointConstraint::setRotationDir(bool isReversed)
+    void JointConstraint::setRotationDir(bool isReversed) noexcept
     {
         // Update the Jacobian
         if (isReversed_ != isReversed)
@@ -50,7 +47,7 @@ namespace jiminy
         isReversed_ = isReversed;
     }
 
-    bool JointConstraint::getRotationDir()
+    bool JointConstraint::getRotationDir() noexcept
     {
         return isReversed_;
     }
@@ -70,8 +67,8 @@ namespace jiminy
         // Get joint index
         if (returnCode == hresult_t::SUCCESS)
         {
-            jointIdx_ = model->pncModel_.getJointId(jointName_);
-            if (jointIdx_ == static_cast<uint32_t>(model->pncModel_.njoints))
+            jointModelIdx_ = model->pncModel_.getJointId(jointName_);
+            if (jointModelIdx_ == static_cast<pinocchio::JointIndex>(model->pncModel_.njoints))
             {
                 PRINT_ERROR("No joint with name '", jointName_, "' in model.");
                 returnCode = hresult_t::ERROR_GENERIC;
@@ -81,7 +78,7 @@ namespace jiminy
         if (returnCode == hresult_t::SUCCESS)
         {
             // Get the joint model
-            const pinocchio::JointModel & jointModel = model->pncModel_.joints[jointIdx_];
+            const pinocchio::JointModel & jointModel = model->pncModel_.joints[jointModelIdx_];
 
             // Initialize constraint jacobian, drift and multipliers
             jacobian_.setZero(jointModel.nv(), model->pncModel_.nv);
@@ -169,7 +166,7 @@ namespace jiminy
         auto model = model_.lock();
 
         // Get the joint model
-        const pinocchio::JointModel & jointModel = model->pncModel_.joints[jointIdx_];
+        const pinocchio::JointModel & jointModel = model->pncModel_.joints[jointModelIdx_];
 
         // Add Baumgarte stabilization drift
         const Eigen::VectorXd deltaPosition =
diff --git a/core/src/constraints/sphere_constraint.cc b/core/src/constraints/sphere_constraint.cc
index 673b3522a..6cdacdc42 100644
--- a/core/src/constraints/sphere_constraint.cc
+++ b/core/src/constraints/sphere_constraint.cc
@@ -9,37 +9,34 @@
 namespace jiminy
 {
     template<>
-    const std::string AbstractConstraintTpl<SphereConstraint>::type_("SphereConstraint");
+    const std::string AbstractConstraintTpl<SphereConstraint>::type_{"SphereConstraint"};
 
-    SphereConstraint::SphereConstraint(
-        const std::string & frameName, double sphereRadius, const Eigen::Vector3d & groundNormal) :
+    SphereConstraint::SphereConstraint(const std::string & frameName,
+                                       double sphereRadius,
+                                       const Eigen::Vector3d & groundNormal) noexcept :
     AbstractConstraintTpl(),
-    frameName_(frameName),
-    frameIdx_(0),
-    radius_(sphereRadius),
-    normal_(groundNormal.normalized()),
-    skewRadius_(pinocchio::alphaSkew(radius_, normal_)),
-    transformRef_(),
-    frameJacobian_()
+    frameName_{frameName},
+    radius_{sphereRadius},
+    normal_{groundNormal.normalized()}
     {
     }
 
-    const std::string & SphereConstraint::getFrameName() const
+    const std::string & SphereConstraint::getFrameName() const noexcept
     {
         return frameName_;
     }
 
-    pinocchio::FrameIndex SphereConstraint::getFrameIdx() const
+    pinocchio::FrameIndex SphereConstraint::getFrameIdx() const noexcept
     {
         return frameIdx_;
     }
 
-    void SphereConstraint::setReferenceTransform(const pinocchio::SE3 & transformRef)
+    void SphereConstraint::setReferenceTransform(const pinocchio::SE3 & transformRef) noexcept
     {
         transformRef_ = transformRef;
     }
 
-    const pinocchio::SE3 & SphereConstraint::getReferenceTransform() const
+    const pinocchio::SE3 & SphereConstraint::getReferenceTransform() const noexcept
     {
         return transformRef_;
     }
diff --git a/core/src/constraints/wheel_constraint.cc b/core/src/constraints/wheel_constraint.cc
index 2895e597a..17efa2ecb 100644
--- a/core/src/constraints/wheel_constraint.cc
+++ b/core/src/constraints/wheel_constraint.cc
@@ -14,36 +14,31 @@ namespace jiminy
     WheelConstraint::WheelConstraint(const std::string & frameName,
                                      double wheelRadius,
                                      const Eigen::Vector3d & groundNormal,
-                                     const Eigen::Vector3d & wheelAxis) :
+                                     const Eigen::Vector3d & wheelAxis) noexcept :
     AbstractConstraintTpl(),
-    frameName_(frameName),
-    frameIdx_(0),
-    radius_(wheelRadius),
-    normal_(groundNormal.normalized()),
-    axis_(wheelAxis.normalized()),
-    skewRadius_(),
-    dskewRadius_(),
-    transformRef_(),
-    frameJacobian_()
+    frameName_{frameName},
+    radius_{wheelRadius},
+    normal_{groundNormal.normalized()},
+    axis_{wheelAxis.normalized()}
     {
     }
 
-    const std::string & WheelConstraint::getFrameName() const
+    const std::string & WheelConstraint::getFrameName() const noexcept
     {
         return frameName_;
     }
 
-    pinocchio::FrameIndex WheelConstraint::getFrameIdx() const
+    pinocchio::FrameIndex WheelConstraint::getFrameIdx() const noexcept
     {
         return frameIdx_;
     }
 
-    void WheelConstraint::setReferenceTransform(const pinocchio::SE3 & transformRef)
+    void WheelConstraint::setReferenceTransform(const pinocchio::SE3 & transformRef) noexcept
     {
         transformRef_ = transformRef;
     }
 
-    const pinocchio::SE3 & WheelConstraint::getReferenceTransform() const
+    const pinocchio::SE3 & WheelConstraint::getReferenceTransform() const noexcept
     {
         return transformRef_;
     }
diff --git a/core/src/control/abstract_controller.cc b/core/src/control/abstract_controller.cc
index 573e5e7ef..eadf09d44 100644
--- a/core/src/control/abstract_controller.cc
+++ b/core/src/control/abstract_controller.cc
@@ -1,5 +1,6 @@
 #include "pinocchio/algorithm/joint-configuration.hpp"  // pinocchio::neutral
 
+#include "jiminy/core/telemetry/telemetry_sender.h"
 #include "jiminy/core/robot/robot.h"
 
 #include "jiminy/core/control/abstract_controller.h"
@@ -7,21 +8,15 @@
 
 namespace jiminy
 {
-    AbstractController::AbstractController() :
-    baseControllerOptions_(nullptr),
-    robot_(),
-    sensorsData_(),
-    isInitialized_(false),
-    isTelemetryConfigured_(false),
-    ctrlOptionsHolder_(),
-    telemetrySender_(),
-    registeredVariables_(),
-    registeredConstants_()
+    AbstractController::AbstractController() noexcept :
+    telemetrySender_{std::make_unique<TelemetrySender>()}
     {
         // Clarify that the base implementation is called
         AbstractController::setOptions(getDefaultControllerOptions());
     }
 
+    AbstractController::~AbstractController() = default;
+
     hresult_t AbstractController::initialize(std::weak_ptr<const Robot> robotIn)
     {
         /* Note that it is not possible to reinitialize a controller for a different robot, because
@@ -144,14 +139,14 @@ namespace jiminy
                     objectName = addCircumfix(
                         objectName, objectPrefixName, {}, TELEMETRY_FIELDNAME_DELIMITER);
                 }
-                telemetrySender_.configureObject(telemetryData, objectName);
+                telemetrySender_->configureObject(telemetryData, objectName);
                 for (const auto & [name, valuePtr] : registeredVariables_)
                 {
                     if (returnCode == hresult_t::SUCCESS)
                     {
                         // FIXME: Remove explicit `name` capture when moving to C++20
                         std::visit([&, &name = name](auto && arg)
-                                   { telemetrySender_.registerVariable(name, arg); },
+                                   { telemetrySender_->registerVariable(name, arg); },
                                    valuePtr);
                     }
                 }
@@ -159,7 +154,7 @@ namespace jiminy
                 {
                     if (returnCode == hresult_t::SUCCESS)
                     {
-                        returnCode = telemetrySender_.registerConstant(name, value);
+                        returnCode = telemetrySender_->registerConstant(name, value);
                     }
                 }
                 if (returnCode == hresult_t::SUCCESS)
@@ -236,11 +231,11 @@ namespace jiminy
     {
         if (isTelemetryConfigured_)
         {
-            telemetrySender_.updateValues();
+            telemetrySender_->updateValues();
         }
     }
 
-    GenericConfig AbstractController::getOptions() const
+    GenericConfig AbstractController::getOptions() const noexcept
     {
         return ctrlOptionsHolder_;
     }
diff --git a/core/src/engine/engine_multi_robot.cc b/core/src/engine/engine_multi_robot.cc
index 95df26d9d..17a1ec136 100644
--- a/core/src/engine/engine_multi_robot.cc
+++ b/core/src/engine/engine_multi_robot.cc
@@ -25,13 +25,15 @@
 #include "H5Cpp.h"
 #include "json/json.h"
 
-#include "jiminy/core/traits.h"
+#include "jiminy/core/telemetry/fwd.h"
+#include "jiminy/core/hardware/fwd.h"
 #include "jiminy/core/utilities/helpers.h"
 #include "jiminy/core/utilities/pinocchio.h"
 #include "jiminy/core/utilities/random.h"
 #include "jiminy/core/utilities/json.h"
 #include "jiminy/core/io/file_device.h"
 #include "jiminy/core/io/serialization.h"
+#include "jiminy/core/telemetry/telemetry_sender.h"
 #include "jiminy/core/telemetry/telemetry_data.h"
 #include "jiminy/core/telemetry/telemetry_recorder.h"
 #include "jiminy/core/constraints/abstract_constraint.h"
@@ -56,37 +58,13 @@ namespace jiminy
     inline constexpr uint32_t INIT_ITERATIONS{4U};
     inline constexpr uint32_t PGS_MAX_ITERATIONS{100U};
 
-    EngineMultiRobot::EngineMultiRobot() :
-    engineOptions_(nullptr),
-    systems_(),
-    isTelemetryConfigured_(false),
-    isSimulationRunning_(false),
-    engineOptionsHolder_(),
-    timer_(std::make_unique<Timer>()),
-    contactModel_(contactModel_t::UNSUPPORTED),
-    telemetrySender_(),
-    telemetryData_(nullptr),
-    telemetryRecorder_(nullptr),
-    stepper_(),
-    stepperUpdatePeriod_(INF),
-    stepperState_(),
-    systemsDataHolder_(),
-    forcesCoupling_(),
-    contactForcesPrev_(),
-    fPrev_(),
-    aPrev_(),
-    energy_(),
-    logData_(nullptr)
+    EngineMultiRobot::EngineMultiRobot() noexcept :
+    telemetrySender_{std::make_unique<TelemetrySender>()},
+    telemetryData_{std::make_shared<TelemetryData>()},
+    telemetryRecorder_{std::make_unique<TelemetryRecorder>()}
     {
         // Initialize the configuration options to the default.
         setOptions(getDefaultEngineOptions());
-
-        // Initialize the global telemetry data holder
-        telemetryData_ = std::make_shared<TelemetryData>();
-        telemetryData_->reset();
-
-        // Initialize the global telemetry recorder
-        telemetryRecorder_ = std::make_unique<TelemetryRecorder>();
     }
 
     // Cannot be default in the header since some types are incomplete at this point
@@ -142,7 +120,9 @@ namespace jiminy
         }
 
         // TODO: Check that the callback function is working as expected
-        systems_.emplace_back(systemName, robot, controller, std::move(callbackFct));
+        // FIXME: remove constructor call to `std::string` when moving to C++20
+        systems_.emplace_back(
+            systemHolder_t{systemName, robot, controller, std::move(callbackFct)});
         systemsDataHolder_.resize(systems_.size());
 
         return hresult_t::SUCCESS;
@@ -231,7 +211,7 @@ namespace jiminy
         if (returnCode == hresult_t::SUCCESS)
         {
             // Get the system index
-            int32_t systemIdx;
+            std::ptrdiff_t systemIdx;
             getSystemIdx(systemName, systemIdx);  // Cannot fail at this point
 
             // Update the systems' indices for the remaining coupling forces
@@ -330,13 +310,13 @@ namespace jiminy
         }
 
         // Get system and frame indices
-        int32_t systemIdx1;
+        std::ptrdiff_t systemIdx1;
         if (returnCode == hresult_t::SUCCESS)
         {
             returnCode = getSystemIdx(systemName1, systemIdx1);
         }
 
-        int32_t systemIdx2;
+        std::ptrdiff_t systemIdx2;
         if (returnCode == hresult_t::SUCCESS)
         {
             returnCode = getSystemIdx(systemName2, systemIdx2);
@@ -741,7 +721,7 @@ namespace jiminy
         if (!isTelemetryConfigured_)
         {
             // Initialize the engine-specific telemetry sender
-            telemetrySender_.configureObject(telemetryData_, ENGINE_TELEMETRY_NAMESPACE);
+            telemetrySender_->configureObject(telemetryData_, ENGINE_TELEMETRY_NAMESPACE);
 
             auto systemIt = systems_.begin();
             auto systemDataIt = systemsDataHolder_.begin();
@@ -787,7 +767,7 @@ namespace jiminy
                 {
                     if (engineOptions_->telemetry.enableConfiguration)
                     {
-                        returnCode = telemetrySender_.registerVariable(
+                        returnCode = telemetrySender_->registerVariable(
                             systemDataIt->logFieldnamesPosition, systemDataIt->state.q);
                     }
                 }
@@ -795,7 +775,7 @@ namespace jiminy
                 {
                     if (engineOptions_->telemetry.enableVelocity)
                     {
-                        returnCode = telemetrySender_.registerVariable(
+                        returnCode = telemetrySender_->registerVariable(
                             systemDataIt->logFieldnamesVelocity, systemDataIt->state.v);
                     }
                 }
@@ -803,7 +783,7 @@ namespace jiminy
                 {
                     if (engineOptions_->telemetry.enableAcceleration)
                     {
-                        returnCode = telemetrySender_.registerVariable(
+                        returnCode = telemetrySender_->registerVariable(
                             systemDataIt->logFieldnamesAcceleration, systemDataIt->state.a);
                     }
                 }
@@ -814,7 +794,7 @@ namespace jiminy
                         const auto & fext = systemDataIt->state.fExternal[i].toVector();
                         for (uint8_t j = 0; j < 6U; ++j)
                         {
-                            returnCode = telemetrySender_.registerVariable(
+                            returnCode = telemetrySender_->registerVariable(
                                 systemDataIt->logFieldnamesForceExternal[(i - 1) * 6U + j],
                                 &fext[j]);
                         }
@@ -824,7 +804,7 @@ namespace jiminy
                 {
                     if (engineOptions_->telemetry.enableCommand)
                     {
-                        returnCode = telemetrySender_.registerVariable(
+                        returnCode = telemetrySender_->registerVariable(
                             systemDataIt->logFieldnamesCommand, systemDataIt->state.command);
                     }
                 }
@@ -832,7 +812,7 @@ namespace jiminy
                 {
                     if (engineOptions_->telemetry.enableMotorEffort)
                     {
-                        returnCode = telemetrySender_.registerVariable(
+                        returnCode = telemetrySender_->registerVariable(
                             systemDataIt->logFieldnamesMotorEffort, systemDataIt->state.uMotor);
                     }
                 }
@@ -840,7 +820,7 @@ namespace jiminy
                 {
                     if (engineOptions_->telemetry.enableEnergy)
                     {
-                        returnCode = telemetrySender_.registerVariable(
+                        returnCode = telemetrySender_->registerVariable(
                             systemDataIt->logFieldnameEnergy, &(*energyIt));
                     }
                 }
@@ -885,7 +865,7 @@ namespace jiminy
         }
 
         // Update engine-specific telemetry variables
-        telemetrySender_.updateValues();
+        telemetrySender_->updateValues();
 
         // Flush the telemetry internal state
         telemetryRecorder_->flushDataSnapshot(stepperState_.t);
@@ -964,17 +944,18 @@ namespace jiminy
            that it is varying slowly enough to consider it constant during one integration step. */
         if (!system.robot->hasConstraints())
         {
-            for (int32_t i = 1; i < model.njoints; ++i)
+            for (int i = 1; i < model.njoints; ++i)
             {
                 data.Ycrb[i] = model.inertias[i];
             }
-            for (int32_t i = model.njoints - 1; i > 0; --i)
+            for (int i = model.njoints - 1; i > 0; --i)
             {
-                const pinocchio::JointIndex jointIdx = model.joints[i].id();
-                const pinocchio::JointIndex parentIdx = model.parents[jointIdx];
-                if (parentIdx > 0)
+                const pinocchio::JointIndex jointModelIdx = model.joints[i].id();
+                const pinocchio::JointIndex parentJointModelIdx = model.parents[jointModelIdx];
+                if (parentJointModelIdx > 0)
                 {
-                    data.Ycrb[parentIdx] += data.liMi[jointIdx].act(data.Ycrb[jointIdx]);
+                    data.Ycrb[parentJointModelIdx] +=
+                        data.liMi[jointModelIdx].act(data.Ycrb[jointModelIdx]);
                 }
             }
         }
@@ -988,21 +969,21 @@ namespace jiminy
         // Compute the spatial momenta and the sum of external forces acting on each body
         data.h[0].setZero();
         data.f[0].setZero();
-        for (int32_t i = 1; i < model.njoints; ++i)
+        for (int i = 1; i < model.njoints; ++i)
         {
             data.h[i] = model.inertias[i] * data.v[i];
             data.f[i] = model.inertias[i] * data.a[i] + data.v[i].cross(data.h[i]);
         }
-        for (int32_t i = model.njoints - 1; i > 0; --i)
+        for (int i = model.njoints - 1; i > 0; --i)
         {
-            const pinocchio::JointIndex parentIdx = model.parents[i];
-            data.h[parentIdx] += data.liMi[i].act(data.h[i]);
-            data.f[parentIdx] += data.liMi[i].act(data.f[i]);
+            const pinocchio::JointIndex parentJointModelIdx = model.parents[i];
+            data.h[parentJointModelIdx] += data.liMi[i].act(data.h[i]);
+            data.f[parentJointModelIdx] += data.liMi[i].act(data.f[i]);
         }
 
         /* Now that `data.Ycrb` and `data.h` are available, one can get directly the position and
            velocity of the center of mass of each subtrees. */
-        for (int32_t i = 0; i < model.njoints; ++i)
+        for (int i = 0; i < model.njoints; ++i)
         {
             data.com[i] = data.Ycrb[i].lever();
             data.vcom[i].noalias() = data.h[i].linear() / data.mass[i];
@@ -1038,7 +1019,7 @@ namespace jiminy
             contactForces[i] = system.robot->contactForces_[i];
         }
 
-        for (int32_t i = 0; i < system.robot->pncModel_.njoints; ++i)
+        for (int i = 0; i < system.robot->pncModel_.njoints; ++i)
         {
             f[i] = system.robot->pncData_.f[i];
             a[i] = system.robot->pncData_.a[i];
@@ -1583,7 +1564,7 @@ namespace jiminy
                     for (const auto & motor : systemIt->robot->getMotors())
                     {
                         const std::size_t motorIdx = motor->getIdx();
-                        const int32_t motorVelocityIdx = motor->getJointVelocityIdx();
+                        const Eigen::Index motorVelocityIdx = motor->getJointVelocityIdx();
                         u[motorVelocityIdx] += uMotor[motorIdx];
                     }
                 }
@@ -1626,13 +1607,13 @@ namespace jiminy
                 const std::string telemetryUrdfFile =
                     addCircumfix("urdf_file", system.name, {}, TELEMETRY_FIELDNAME_DELIMITER);
                 const std::string & urdfFileString = system.robot->getUrdfAsString();
-                telemetrySender_.registerConstant(telemetryUrdfFile, urdfFileString);
+                telemetrySender_->registerConstant(telemetryUrdfFile, urdfFileString);
 
                 // Backup 'has_freeflyer' option
                 const std::string telemetrHasFreeflyer =
                     addCircumfix("has_freeflyer", system.name, {}, TELEMETRY_FIELDNAME_DELIMITER);
-                telemetrySender_.registerConstant(telemetrHasFreeflyer,
-                                                  toString(system.robot->getHasFreeflyer()));
+                telemetrySender_->registerConstant(telemetrHasFreeflyer,
+                                                   toString(system.robot->getHasFreeflyer()));
 
                 // Backup mesh package lookup directories
                 const std::string telemetryMeshPackageDirs = addCircumfix(
@@ -1650,13 +1631,14 @@ namespace jiminy
                     meshPackageDirsString = meshPackageDirsStream.str();
                     meshPackageDirsString.pop_back();
                 }
-                telemetrySender_.registerConstant(telemetryMeshPackageDirs, meshPackageDirsString);
+                telemetrySender_->registerConstant(telemetryMeshPackageDirs,
+                                                   meshPackageDirsString);
 
                 // Backup the true and theoretical Pinocchio::Model
                 std::string key = addCircumfix(
                     "pinocchio_model", system.name, {}, TELEMETRY_FIELDNAME_DELIMITER);
                 std::string value = saveToBinary(system.robot->pncModel_);
-                telemetrySender_.registerConstant(key, value);
+                telemetrySender_->registerConstant(key, value);
 
                 /* Backup the Pinocchio GeometryModel for collisions and visuals.
                    It may fail because of missing serialization methods for convex, or because it
@@ -1669,12 +1651,12 @@ namespace jiminy
                         key = addCircumfix(
                             "collision_model", system.name, {}, TELEMETRY_FIELDNAME_DELIMITER);
                         value = saveToBinary(system.robot->collisionModel_);
-                        telemetrySender_.registerConstant(key, value);
+                        telemetrySender_->registerConstant(key, value);
 
                         key = addCircumfix(
                             "visual_model", system.name, {}, TELEMETRY_FIELDNAME_DELIMITER);
                         value = saveToBinary(system.robot->visualModel_);
-                        telemetrySender_.registerConstant(key, value);
+                        telemetrySender_->registerConstant(key, value);
                     }
                     catch (const std::exception & e)
                     {
@@ -1706,7 +1688,7 @@ namespace jiminy
             Json::StreamWriterBuilder jsonWriter;
             jsonWriter["indentation"] = "";
             const std::string allOptionsString = Json::writeString(jsonWriter, allOptionsJson);
-            telemetrySender_.registerConstant("options", allOptionsString);
+            telemetrySender_->registerConstant("options", allOptionsString);
 
             // Write the header: this locks the registration of new variables
             telemetryRecorder_->initialize(telemetryData_.get(), getTelemetryTimeUnit());
@@ -1808,11 +1790,11 @@ namespace jiminy
             double stepSize;
             if (std::isfinite(stepperUpdatePeriod_))
             {
-                stepSize = min(stepperUpdatePeriod_, tEnd - stepperState_.t);
+                stepSize = std::min(stepperUpdatePeriod_, tEnd - stepperState_.t);
             }
             else
             {
-                stepSize = min(engineOptions_->stepper.dtMax, tEnd - stepperState_.t);
+                stepSize = std::min(engineOptions_->stepper.dtMax, tEnd - stepperState_.t);
             }
             returnCode = step(stepSize);  // Automatic dt adjustment
         }
@@ -1989,7 +1971,7 @@ namespace jiminy
                 // Get the next breakpoint time if any
                 if (tBreakNextIt != systemData.forcesImpulseBreaks.end())
                 {
-                    tForceImpulseNext = min(tForceImpulseNext, *tBreakNextIt);
+                    tForceImpulseNext = std::min(tForceImpulseNext, *tBreakNextIt);
                 }
             }
 
@@ -2095,11 +2077,11 @@ namespace jiminy
                        Note that in this case, the sensors have already been updated in advance
                        during the previous loop. */
                     dtNextGlobal =
-                        min(dtNextUpdatePeriod + stepperUpdatePeriod_, tForceImpulseNext - t);
+                        std::min(dtNextUpdatePeriod + stepperUpdatePeriod_, tForceImpulseNext - t);
                 }
                 else
                 {
-                    dtNextGlobal = min(dtNextUpdatePeriod, tForceImpulseNext - t);
+                    dtNextGlobal = std::min(dtNextUpdatePeriod, tForceImpulseNext - t);
                 }
 
                 /* Check if the next dt to about equal to the time difference between the current
@@ -2134,7 +2116,7 @@ namespace jiminy
                     }
 
                     // Adjust stepsize to end up exactly at the next breakpoint
-                    dt = min(dt, tNext - t);
+                    dt = std::min(dt, tNext - t);
                     if (dtLargest > SIMULATION_MIN_TIMESTEP)
                     {
                         if (tNext - (t + dt) < SIMULATION_MIN_TIMESTEP)
@@ -2295,14 +2277,14 @@ namespace jiminy
                     }
 
                     // Initialize the next dt
-                    dt = min(dtLargest, engineOptions_->stepper.dtMax);
+                    dt = std::min(dtLargest, engineOptions_->stepper.dtMax);
                 }
             }
             else
             {
                 /* Make sure it ends exactly at the tEnd, never exceeds dtMax, and stop to apply
                    impulse forces. */
-                dt = min(dt, tEnd - t, tForceImpulseNext - t);
+                dt = std::min({dt, tEnd - t, tForceImpulseNext - t});
 
                 /* A breakpoint has been reached, because dt has been decreased wrt the largest
                    possible dt within integration tol. */
@@ -2404,7 +2386,7 @@ namespace jiminy
                     }
 
                     // Initialize the next dt
-                    dt = min(dtLargest, engineOptions_->stepper.dtMax);
+                    dt = std::min(dtLargest, engineOptions_->stepper.dtMax);
                 }
             }
 
@@ -2549,7 +2531,7 @@ namespace jiminy
             returnCode = hresult_t::ERROR_GENERIC;
         }
 
-        int32_t systemIdx;
+        std::ptrdiff_t systemIdx;
         if (returnCode == hresult_t::SUCCESS)
         {
             returnCode = getSystemIdx(systemName, systemIdx);
@@ -2610,7 +2592,7 @@ namespace jiminy
             returnCode = hresult_t::ERROR_GENERIC;
         }
 
-        int32_t systemIdx;
+        std::ptrdiff_t systemIdx;
         if (returnCode == hresult_t::SUCCESS)
         {
             returnCode = getSystemIdx(systemName, systemIdx);
@@ -2683,7 +2665,7 @@ namespace jiminy
             returnCode = hresult_t::ERROR_GENERIC;
         }
 
-        int32_t systemIdx;
+        std::ptrdiff_t systemIdx;
         if (returnCode == hresult_t::SUCCESS)
         {
             returnCode = getSystemIdx(systemName, systemIdx);
@@ -2728,7 +2710,7 @@ namespace jiminy
             return hresult_t::ERROR_GENERIC;
         }
 
-        int32_t systemIdx;
+        std::ptrdiff_t systemIdx;
         if (returnCode == hresult_t::SUCCESS)
         {
             returnCode = getSystemIdx(systemName, systemIdx);
@@ -2777,7 +2759,7 @@ namespace jiminy
 
         forcesImpulsePtr = &forcesImpuseDummy;
 
-        int32_t systemIdx;
+        std::ptrdiff_t systemIdx;
         returnCode = getSystemIdx(systemName, systemIdx);
 
         if (returnCode == hresult_t::SUCCESS)
@@ -2798,7 +2780,7 @@ namespace jiminy
 
         forcesProfilePtr = &forcesRegisterDummy;
 
-        int32_t systemIdx;
+        std::ptrdiff_t systemIdx;
         returnCode = getSystemIdx(systemName, systemIdx);
 
         if (returnCode == hresult_t::SUCCESS)
@@ -2810,7 +2792,7 @@ namespace jiminy
         return returnCode;
     }
 
-    GenericConfig EngineMultiRobot::getOptions() const
+    GenericConfig EngineMultiRobot::getOptions() const noexcept
     {
         return engineOptionsHolder_;
     }
@@ -2962,7 +2944,7 @@ namespace jiminy
     }
 
     hresult_t EngineMultiRobot::getSystemIdx(const std::string & systemName,
-                                             int32_t & systemIdx) const
+                                             std::ptrdiff_t & systemIdx) const
     {
         auto systemIt = std::find_if(systems_.begin(),
                                      systems_.end(),
@@ -2974,7 +2956,7 @@ namespace jiminy
             return hresult_t::ERROR_BAD_INPUT;
         }
 
-        systemIdx = static_cast<int32_t>(std::distance(systems_.begin(), systemIt));
+        systemIdx = std::distance(systems_.begin(), systemIt);
 
         return hresult_t::SUCCESS;
     }
@@ -3013,7 +2995,7 @@ namespace jiminy
 
         hresult_t returnCode = hresult_t::SUCCESS;
 
-        int32_t systemIdx;
+        std::ptrdiff_t systemIdx;
         returnCode = getSystemIdx(systemName, systemIdx);
         if (returnCode == hresult_t::SUCCESS)
         {
@@ -3111,16 +3093,16 @@ namespace jiminy
         pinocchio::forwardKinematics(model, data, q, v, a);
 
         // Update frame placements (avoiding redundant computations)
-        for (int32_t i = 1; i < model.nframes; ++i)
+        for (int jointModelIdx = 1; jointModelIdx < model.nframes; ++jointModelIdx)
         {
-            const pinocchio::Frame & frame = model.frames[i];
-            pinocchio::JointIndex parent = frame.parent;
+            const pinocchio::Frame & frame = model.frames[jointModelIdx];
+            pinocchio::JointIndex parentJointModelIdx = frame.parent;
             switch (frame.type)
             {
             case pinocchio::FrameType::JOINT:
                 /* If the frame is associated with an actual joint, no need to compute anything
                    new, since the frame transform is supposed to be identity. */
-                data.oMf[i] = data.oMi[parent];
+                data.oMf[jointModelIdx] = data.oMi[parentJointModelIdx];
                 break;
             case pinocchio::FrameType::BODY:
                 if (model.frames[frame.previousFrame].type == pinocchio::FrameType::FIXED_JOINT)
@@ -3129,13 +3111,13 @@ namespace jiminy
                        itself, so no need to compute them twice. Here we are doing the assumption
                        that the previous frame transform has already been updated since it is
                        closer to root in kinematic tree. */
-                    data.oMf[i] = data.oMf[frame.previousFrame];
+                    data.oMf[jointModelIdx] = data.oMf[frame.previousFrame];
                 }
                 else
                 {
                     /* BODYs connected via JOINT(s) have the identity transform, so copying parent
                        joint transform should be fine. */
-                    data.oMf[i] = data.oMi[parent];
+                    data.oMf[jointModelIdx] = data.oMi[parentJointModelIdx];
                 }
                 break;
             case pinocchio::FrameType::FIXED_JOINT:
@@ -3143,7 +3125,7 @@ namespace jiminy
             case pinocchio::FrameType::OP_FRAME:
             default:
                 // Nothing special, doing the actual computation
-                data.oMf[i] = data.oMi[parent] * frame.placement;
+                data.oMf[jointModelIdx] = data.oMi[parentJointModelIdx] * frame.placement;
             }
         }
 
@@ -3166,7 +3148,7 @@ namespace jiminy
         // Get the frame and joint indices
         const pinocchio::GeomIndex & geometryIdx =
             system.robot->collisionModel_.collisionPairs[collisionPairIdx].first;
-        pinocchio::JointIndex parentJointIdx =
+        pinocchio::JointIndex parentJointModelIdx =
             system.robot->collisionModel_.geometryObjects[geometryIdx].parentJoint;
 
         // Extract collision and distance results
@@ -3182,12 +3164,12 @@ namespace jiminy
             constraint->disable();
         }
 
-        for (uint32_t i = 0; i < collisionResult.numContacts(); ++i)
+        for (std::size_t contactIdx = 0; contactIdx < collisionResult.numContacts(); ++contactIdx)
         {
             /* Extract the contact information.
                Note that there is always a single contact point while computing the collision
                between two shape objects, for instance convex geometry and box primitive. */
-            const auto & contact = collisionResult.getContact(i);
+            const auto & contact = collisionResult.getContact(contactIdx);
             Eigen::Vector3d nGround = contact.normal.normalized();  // Ground normal in world frame
             double depth = contact.penetration_depth;  // Penetration depth (signed, negative)
             pinocchio::SE3 posContactInWorld = pinocchio::SE3::Identity();
@@ -3216,9 +3198,9 @@ namespace jiminy
             {
                 // Compute the linear velocity of the contact point in world frame
                 const pinocchio::Motion & motionJointLocal =
-                    system.robot->pncData_.v[parentJointIdx];
+                    system.robot->pncData_.v[parentJointModelIdx];
                 const pinocchio::SE3 & transformJointFrameInWorld =
-                    system.robot->pncData_.oMi[parentJointIdx];
+                    system.robot->pncData_.oMi[parentJointModelIdx];
                 const pinocchio::SE3 transformJointFrameInContact =
                     posContactInWorld.actInv(transformJointFrameInWorld);
                 const Eigen::Vector3d vContactInWorld =
@@ -3460,14 +3442,14 @@ namespace jiminy
              Eigen::VectorXd & u)
         {
             // Define some proxies for convenience
-            const pinocchio::JointIndex jointIdx = joint.id();
-            const uint32_t positionIdx = joint.idx_q();
-            const uint32_t velocityIdx = joint.idx_v();
+            const pinocchio::JointIndex jointModelIdx = joint.id();
+            const Eigen::Index positionIdx = joint.idx_q();
+            const Eigen::Index velocityIdx = joint.idx_v();
             const double qJoint = q[positionIdx];
             const double qJointMin = positionLimitMin[positionIdx];
             const double qJointMax = positionLimitMax[positionIdx];
             const double vJoint = v[velocityIdx];
-            const double Ia = getSubtreeInertiaProj(joint.derived(), pncData.Ycrb[jointIdx]);
+            const double Ia = getSubtreeInertiaProj(joint.derived(), pncData.Ycrb[jointModelIdx]);
             const double stiffness = engineOptions->joints.boundStiffness;
             const double damping = engineOptions->joints.boundDamping;
             const double transitionEps = engineOptions->contacts.transitionEps;
@@ -3589,12 +3571,12 @@ namespace jiminy
              Eigen::VectorXd & u)
         {
             // Define some proxies for convenience
-            const pinocchio::JointIndex jointIdx = joint.id();
-            const uint32_t velocityIdx = joint.idx_v();
+            const pinocchio::JointIndex jointModelIdx = joint.id();
+            const Eigen::Index velocityIdx = joint.idx_v();
             const double vJoint = v[velocityIdx];
             const double vJointMin = -velocityLimitMax[velocityIdx];
             const double vJointMax = velocityLimitMax[velocityIdx];
-            const double Ia = getSubtreeInertiaProj(joint.derived(), pncData.Ycrb[jointIdx]);
+            const double Ia = getSubtreeInertiaProj(joint.derived(), pncData.Ycrb[jointModelIdx]);
             const double damping = engineOptions->joints.boundDamping;
 
             // Check if out-of-bounds
@@ -3660,13 +3642,13 @@ namespace jiminy
         {
             const Eigen::VectorXd & positionLimitMin = system.robot->getPositionLimitMin();
             const Eigen::VectorXd & positionLimitMax = system.robot->getPositionLimitMax();
-            const std::vector<pinocchio::JointIndex> & rigidJointsIdx =
+            const std::vector<pinocchio::JointIndex> & rigidJointModelIndices =
                 system.robot->getRigidJointsModelIdx();
-            for (std::size_t i = 0; i < rigidJointsIdx.size(); ++i)
+            for (std::size_t i = 0; i < rigidJointModelIndices.size(); ++i)
             {
                 auto & constraint = systemData.constraintsHolder.boundJoints[i].second;
                 computePositionLimitsForcesAlgo::run(
-                    pncModel.joints[rigidJointsIdx[i]],
+                    pncModel.joints[rigidJointModelIndices[i]],
                     typename computePositionLimitsForcesAlgo::ArgsType(pncData,
                                                                        q,
                                                                        v,
@@ -3683,10 +3665,10 @@ namespace jiminy
         if (system.robot->mdlOptions_->joints.enableVelocityLimit)
         {
             const Eigen::VectorXd & velocityLimitMax = system.robot->getVelocityLimit();
-            for (pinocchio::JointIndex rigidIdx : system.robot->getRigidJointsModelIdx())
+            for (pinocchio::JointIndex rigidJointModelIdx : system.robot->getRigidJointsModelIdx())
             {
                 computeVelocityLimitsForcesAlgo::run(
-                    pncModel.joints[rigidIdx],
+                    pncModel.joints[rigidJointModelIdx],
                     typename computeVelocityLimitsForcesAlgo::ArgsType(
                         pncData, v, velocityLimitMax, engineOptions_, contactModel_, uInternal));
             }
@@ -3696,13 +3678,13 @@ namespace jiminy
         double angle;
         Eigen::Matrix3d rotJlog3;
         const Robot::dynamicsOptions_t & mdlDynOptions = system.robot->mdlOptions_->dynamics;
-        const std::vector<pinocchio::JointIndex> & flexibilityIdx =
+        const std::vector<pinocchio::JointIndex> & flexibilityJointModelIndices =
             system.robot->getFlexibleJointsModelIdx();
-        for (std::size_t i = 0; i < flexibilityIdx.size(); ++i)
+        for (std::size_t i = 0; i < flexibilityJointModelIndices.size(); ++i)
         {
-            const pinocchio::JointIndex jointIdx = flexibilityIdx[i];
-            const uint32_t positionIdx = pncModel.joints[jointIdx].idx_q();
-            const uint32_t velocityIdx = pncModel.joints[jointIdx].idx_v();
+            const pinocchio::JointIndex jointModelIdx = flexibilityJointModelIndices[i];
+            const Eigen::Index positionIdx = pncModel.joints[jointModelIdx].idx_q();
+            const Eigen::Index velocityIdx = pncModel.joints[jointModelIdx].idx_v();
             const Eigen::Vector3d & stiffness = mdlDynOptions.flexibilityConfig[i].stiffness;
             const Eigen::Vector3d & damping = mdlDynOptions.flexibilityConfig[i].damping;
 
@@ -3738,9 +3720,9 @@ namespace jiminy
             }
 
             // Apply the force at the origin of the parent joint frame, in local joint frame
-            const pinocchio::JointIndex parentJointIdx =
+            const pinocchio::JointIndex parentJointModelIdx =
                 system.robot->pncModel_.frames[frameIdx].parent;
-            fext[parentJointIdx] += fextLocal;
+            fext[parentJointModelIdx] += fextLocal;
 
             // Convert contact force from global frame to local frame to store it in contactForces_
             const pinocchio::SE3 & transformContactInJoint =
@@ -3758,7 +3740,7 @@ namespace jiminy
             /* Compute force at the given collision body.
                It returns the force applied at the origin of parent joint frame in global frame. */
             const pinocchio::FrameIndex frameIdx = collisionBodiesIdx[i];
-            const pinocchio::JointIndex parentJointIdx =
+            const pinocchio::JointIndex parentJointModelIdx =
                 system.robot->pncModel_.frames[frameIdx].parent;
             for (std::size_t j = 0; j < collisionPairsIdx[i].size(); ++j)
             {
@@ -3771,7 +3753,7 @@ namespace jiminy
                 }
 
                 // Apply the force at the origin of the parent joint frame, in local joint frame
-                fext[parentJointIdx] += fextLocal;
+                fext[parentJointModelIdx] += fextLocal;
             }
         }
     }
@@ -3794,11 +3776,11 @@ namespace jiminy
             if (*forcesImpulseActiveIt)
             {
                 const pinocchio::FrameIndex frameIdx = forcesImpulseIt->frameIdx;
-                const pinocchio::JointIndex parentJointIdx =
+                const pinocchio::JointIndex parentJointModelIdx =
                     system.robot->pncModel_.frames[frameIdx].parent;
                 const pinocchio::Force & F = forcesImpulseIt->F;
 
-                fext[parentJointIdx] += convertForceGlobalFrameToJoint(
+                fext[parentJointModelIdx] += convertForceGlobalFrameToJoint(
                     system.robot->pncModel_, system.robot->pncData_, frameIdx, F);
             }
         }
@@ -3807,13 +3789,13 @@ namespace jiminy
         for (auto & forceProfile : systemData.forcesProfile)
         {
             const pinocchio::FrameIndex frameIdx = forceProfile.frameIdx;
-            const pinocchio::JointIndex parentJointIdx =
+            const pinocchio::JointIndex parentJointModelIdx =
                 system.robot->pncModel_.frames[frameIdx].parent;
             if (forceProfile.updatePeriod < EPS)
             {
                 forceProfile.forcePrev = forceProfile.forceFct(t, q, v);
             }
-            fext[parentJointIdx] += convertForceGlobalFrameToJoint(
+            fext[parentJointModelIdx] += convertForceGlobalFrameToJoint(
                 system.robot->pncModel_, system.robot->pncData_, frameIdx, forceProfile.forcePrev);
         }
     }
@@ -3825,7 +3807,7 @@ namespace jiminy
         for (auto & forceCoupling : forcesCoupling_)
         {
             // Extract info about the first system involved
-            const int32_t systemIdx1 = forceCoupling.systemIdx1;
+            const std::ptrdiff_t systemIdx1 = forceCoupling.systemIdx1;
             const systemHolder_t & system1 = systems_[systemIdx1];
             const Eigen::VectorXd & q1 = qSplit[systemIdx1];
             const Eigen::VectorXd & v1 = vSplit[systemIdx1];
@@ -3833,7 +3815,7 @@ namespace jiminy
             ForceVector & fext1 = systemsDataHolder_[systemIdx1].state.fExternal;
 
             // Extract info about the second system involved
-            const int32_t systemIdx2 = forceCoupling.systemIdx2;
+            const std::ptrdiff_t systemIdx2 = forceCoupling.systemIdx2;
             const systemHolder_t & system2 = systems_[systemIdx2];
             const Eigen::VectorXd & q2 = qSplit[systemIdx2];
             const Eigen::VectorXd & v2 = vSplit[systemIdx2];
@@ -3842,19 +3824,19 @@ namespace jiminy
 
             // Compute the coupling force
             pinocchio::Force force = forceCoupling.forceFct(t, q1, v1, q2, v2);
-            const pinocchio::JointIndex parentJointIdx1 =
+            const pinocchio::JointIndex parentJointModelIdx1 =
                 system1.robot->pncModel_.frames[frameIdx1].parent;
-            fext1[parentJointIdx1] += convertForceGlobalFrameToJoint(
+            fext1[parentJointModelIdx1] += convertForceGlobalFrameToJoint(
                 system1.robot->pncModel_, system1.robot->pncData_, frameIdx1, force);
 
             // Move force from frame1 to frame2 to apply it to the second system
             force.toVector() *= -1;
-            const pinocchio::JointIndex parentJointIdx2 =
+            const pinocchio::JointIndex parentJointModelIdx2 =
                 system2.robot->pncModel_.frames[frameIdx2].parent;
             const Eigen::Vector3d offset = system2.robot->pncData_.oMf[frameIdx2].translation() -
                                            system1.robot->pncData_.oMf[frameIdx1].translation();
             force.angular() -= offset.cross(force.linear());
-            fext2[parentJointIdx2] += convertForceGlobalFrameToJoint(
+            fext2[parentJointModelIdx2] += convertForceGlobalFrameToJoint(
                 system2.robot->pncModel_, system2.robot->pncData_, frameIdx2, force);
         }
     }
@@ -4018,7 +4000,7 @@ namespace jiminy
             for (const auto & motor : systemIt->robot->getMotors())
             {
                 const std::size_t motorIdx = motor->getIdx();
-                const int32_t motorVelocityIdx = motor->getJointVelocityIdx();
+                const Eigen::Index motorVelocityIdx = motor->getJointVelocityIdx();
                 u[motorVelocityIdx] += uMotor[motorIdx];
             }
 
@@ -4055,7 +4037,7 @@ namespace jiminy
 
             // Project external forces from cartesian space to joint space.
             data.u = u;
-            for (int32_t i = 1; i < model.njoints; ++i)
+            for (int i = 1; i < model.njoints; ++i)
             {
                 /* Skip computation if force is zero for efficiency. It should be the case most
                    often. */
@@ -4107,7 +4089,7 @@ namespace jiminy
                     Eigen::VectorXd & uJoint = constraint->lambda_;
                     const auto & jointConstraint =
                         static_cast<JointConstraint const &>(*constraint.get());
-                    const auto & jointModel = joints[jointConstraint.getJointIdx()];
+                    const auto & jointModel = joints[jointConstraint.getJointModelIdx()];
                     jointModel.jointVelocitySelector(uInternal) += uJoint;
                     jointModel.jointVelocitySelector(u) += uJoint;
                 });
diff --git a/core/src/engine/system.cc b/core/src/engine/system.cc
index 07b5c95d0..3ba191dcc 100644
--- a/core/src/engine/system.cc
+++ b/core/src/engine/system.cc
@@ -18,12 +18,11 @@ namespace jiminy
     ForceProfile::ForceProfile(const std::string & frameNameIn,
                                pinocchio::FrameIndex frameIdxIn,
                                double updatePeriodIn,
-                               const ForceProfileFunctor & forceFctIn) :
-    frameName(frameNameIn),
-    frameIdx(frameIdxIn),
-    updatePeriod(updatePeriodIn),
-    forcePrev(pinocchio::Force::Zero()),
-    forceFct(forceFctIn)
+                               const ForceProfileFunctor & forceFctIn) noexcept :
+    frameName{frameNameIn},
+    frameIdx{frameIdxIn},
+    updatePeriod{updatePeriodIn},
+    forceFct{forceFctIn}
     {
     }
 
@@ -35,12 +34,12 @@ namespace jiminy
                                pinocchio::FrameIndex frameIdxIn,
                                double tIn,
                                double dtIn,
-                               const pinocchio::Force & FIn) :
-    frameName(frameNameIn),
-    frameIdx(frameIdxIn),
-    t(tIn),
-    dt(dtIn),
-    F(FIn)
+                               const pinocchio::Force & FIn) noexcept :
+    frameName{frameNameIn},
+    frameIdx{frameIdxIn},
+    t{tIn},
+    dt{dtIn},
+    F{FIn}
     {
     }
 
@@ -49,48 +48,23 @@ namespace jiminy
     // ====================================================
 
     ForceCoupling::ForceCoupling(const std::string & systemName1In,
-                                 int32_t systemIdx1In,
+                                 std::ptrdiff_t systemIdx1In,
                                  const std::string & systemName2In,
-                                 int32_t systemIdx2In,
+                                 std::ptrdiff_t systemIdx2In,
                                  const std::string & frameName1In,
                                  pinocchio::FrameIndex frameIdx1In,
                                  const std::string & frameName2In,
                                  pinocchio::FrameIndex frameIdx2In,
-                                 const ForceCouplingFunctor & forceFctIn) :
-    systemName1(systemName1In),
-    systemIdx1(systemIdx1In),
-    systemName2(systemName2In),
-    systemIdx2(systemIdx2In),
-    frameName1(frameName1In),
-    frameIdx1(frameIdx1In),
-    frameName2(frameName2In),
-    frameIdx2(frameIdx2In),
-    forceFct(forceFctIn)
-    {
-    }
-
-    // ====================================================
-    // ================== systemHolder_t ==================
-    // ====================================================
-
-    systemHolder_t::systemHolder_t(const std::string & systemNameIn,
-                                   std::shared_ptr<Robot> robotIn,
-                                   std::shared_ptr<AbstractController> controllerIn,
-                                   CallbackFunctor callbackFctIn) :
-    name(systemNameIn),
-    robot(robotIn),
-    controller(controllerIn),
-    callbackFct(std::move(callbackFctIn))
-    {
-    }
-
-    systemHolder_t::systemHolder_t() :
-    systemHolder_t("",
-                   nullptr,
-                   nullptr,
-                   [](double /* t */,
-                      const Eigen::VectorXd & /* q */,
-                      const Eigen::VectorXd & /* v */) -> bool { return false; })
+                                 const ForceCouplingFunctor & forceFctIn) noexcept :
+    systemName1{systemName1In},
+    systemIdx1{systemIdx1In},
+    systemName2{systemName2In},
+    systemIdx2{systemIdx2In},
+    frameName1{frameName1In},
+    frameIdx1{frameIdx1In},
+    frameName2{frameName2In},
+    frameIdx2{frameIdx2In},
+    forceFct{forceFctIn}
     {
     }
 
@@ -98,20 +72,6 @@ namespace jiminy
     // ================ systemState_t ================
     // ===============================================
 
-    systemState_t::systemState_t() :
-    q(),
-    v(),
-    a(),
-    command(),
-    u(),
-    uMotor(),
-    uInternal(),
-    uCustom(),
-    fExternal(),
-    isInitialized_(false)
-    {
-    }
-
     hresult_t systemState_t::initialize(const Robot & robot)
     {
         if (!robot.getIsInitialized())
@@ -120,15 +80,18 @@ namespace jiminy
             return hresult_t::ERROR_INIT_FAILED;
         }
 
+        Eigen::Index nv = robot.nv();
+        std::size_t nMotors = robot.nmotors();
+        std::size_t nJoints = robot.pncModel_.njoints;
         q = pinocchio::neutral(robot.pncModel_);
-        v.setZero(robot.nv());
-        a.setZero(robot.nv());
-        command.setZero(robot.getMotorsNames().size());
-        u.setZero(robot.nv());
-        uMotor.setZero(robot.getMotorsNames().size());
-        uInternal.setZero(robot.nv());
-        uCustom.setZero(robot.nv());
-        fExternal = ForceVector(robot.pncModel_.joints.size(), pinocchio::Force::Zero());
+        v.setZero(nv);
+        a.setZero(nv);
+        command.setZero(nMotors);
+        u.setZero(nv);
+        uMotor.setZero(nMotors);
+        uInternal.setZero(nv);
+        uCustom.setZero(nv);
+        fExternal = ForceVector(nJoints, pinocchio::Force::Zero());
         isInitialized_ = true;
 
         return hresult_t::SUCCESS;
diff --git a/core/src/hardware/abstract_motor.cc b/core/src/hardware/abstract_motor.cc
index 88a1e39cf..10f3b8606 100644
--- a/core/src/hardware/abstract_motor.cc
+++ b/core/src/hardware/abstract_motor.cc
@@ -6,23 +6,8 @@
 
 namespace jiminy
 {
-    AbstractMotorBase::AbstractMotorBase(const std::string & name) :
-    baseMotorOptions_(nullptr),
-    motorOptionsHolder_(),
-    isInitialized_(false),
-    isAttached_(false),
-    robot_(),
-    notifyRobot_(),
-    name_(name),
-    motorIdx_(0),
-    jointName_(),
-    jointModelIdx_(0),
-    jointType_(JointModelType::UNSUPPORTED),
-    jointPositionIdx_(-1),
-    jointVelocityIdx_(-1),
-    commandLimit_(0.0),
-    armature_(0.0),
-    sharedHolder_(nullptr)
+    AbstractMotorBase::AbstractMotorBase(const std::string & name) noexcept :
+    name_{name}
     {
         // Initialize the options
         setOptions(getDefaultMotorOptions());
@@ -197,7 +182,7 @@ namespace jiminy
         return hresult_t::SUCCESS;
     }
 
-    GenericConfig AbstractMotorBase::getOptions() const
+    GenericConfig AbstractMotorBase::getOptions() const noexcept
     {
         return motorOptionsHolder_;
     }
@@ -269,7 +254,7 @@ namespace jiminy
             // Get the motor effort limits from the URDF or the user options.
             if (baseMotorOptions_->commandLimitFromUrdf)
             {
-                int32_t jointVelocityOrigIdx;
+                Eigen::Index jointVelocityOrigIdx;
                 ::jiminy::getJointVelocityIdx(
                     robot->pncModelOrig_, jointName_, jointVelocityOrigIdx);
                 commandLimit_ = robot->pncModelOrig_.effortLimit[jointVelocityOrigIdx] /
@@ -372,12 +357,12 @@ namespace jiminy
         return jointType_;
     }
 
-    int32_t AbstractMotorBase::getJointPositionIdx() const
+    Eigen::Index AbstractMotorBase::getJointPositionIdx() const
     {
         return jointPositionIdx_;
     }
 
-    int32_t AbstractMotorBase::getJointVelocityIdx() const
+    Eigen::Index AbstractMotorBase::getJointVelocityIdx() const
     {
         return jointVelocityIdx_;
     }
diff --git a/core/src/hardware/abstract_sensor.cc b/core/src/hardware/abstract_sensor.cc
index 5c723068a..801a0e507 100644
--- a/core/src/hardware/abstract_sensor.cc
+++ b/core/src/hardware/abstract_sensor.cc
@@ -1,25 +1,22 @@
-#include "jiminy/core/robot/robot.h"
 #include "jiminy/core/utilities/random.h"
+#include "jiminy/core/telemetry/telemetry_sender.h"
+#include "jiminy/core/robot/robot.h"
 
 #include "jiminy/core/hardware/abstract_sensor.h"
 
 
 namespace jiminy
 {
-    AbstractSensorBase::AbstractSensorBase(const std::string & name) :
-    baseSensorOptions_(nullptr),
-    sensorOptionsHolder_(),
-    isInitialized_(false),
-    isAttached_(false),
-    isTelemetryConfigured_(false),
-    robot_(),
-    name_(name),
-    telemetrySender_()
+    AbstractSensorBase::AbstractSensorBase(const std::string & name) noexcept :
+    name_{name},
+    telemetrySender_{std::make_unique<TelemetrySender>()}
     {
         // Initialize the options
         setOptions(getDefaultSensorOptions());
     }
 
+    AbstractSensorBase::~AbstractSensorBase() = default;
+
     hresult_t AbstractSensorBase::configureTelemetry(std::shared_ptr<TelemetryData> telemetryData,
                                                      const std::string & objectPrefixName)
     {
@@ -43,8 +40,8 @@ namespace jiminy
                         objectName = addCircumfix(
                             objectName, objectPrefixName, {}, TELEMETRY_FIELDNAME_DELIMITER);
                     }
-                    telemetrySender_.configureObject(telemetryData, objectName);
-                    returnCode = telemetrySender_.registerVariable(getFieldnames(), get());
+                    telemetrySender_->configureObject(telemetryData, objectName);
+                    returnCode = telemetrySender_->registerVariable(getFieldnames(), get());
                     if (returnCode == hresult_t::SUCCESS)
                     {
                         isTelemetryConfigured_ = true;
@@ -65,7 +62,7 @@ namespace jiminy
     {
         if (isTelemetryConfigured_)
         {
-            telemetrySender_.updateValues();
+            telemetrySender_->updateValues();
         }
     }
 
@@ -91,7 +88,7 @@ namespace jiminy
         return hresult_t::SUCCESS;
     }
 
-    GenericConfig AbstractSensorBase::getOptions() const
+    GenericConfig AbstractSensorBase::getOptions() const noexcept
     {
         return sensorOptionsHolder_;
     }
diff --git a/core/src/hardware/basic_motors.cc b/core/src/hardware/basic_motors.cc
index 90d2e2d42..4e30bc67b 100644
--- a/core/src/hardware/basic_motors.cc
+++ b/core/src/hardware/basic_motors.cc
@@ -7,9 +7,8 @@
 
 namespace jiminy
 {
-    SimpleMotor::SimpleMotor(const std::string & name) :
-    AbstractMotorBase(name),
-    motorOptions_(nullptr)
+    SimpleMotor::SimpleMotor(const std::string & name) noexcept :
+    AbstractMotorBase(name)
     {
         /* AbstractMotorBase constructor calls the base implementations of the virtual methods
            since the derived class is not available at this point. Thus it must be called
diff --git a/core/src/hardware/basic_sensors.cc b/core/src/hardware/basic_sensors.cc
index 3912472ed..d9146d585 100644
--- a/core/src/hardware/basic_sensors.cc
+++ b/core/src/hardware/basic_sensors.cc
@@ -72,14 +72,6 @@ namespace jiminy
     template<>
     const bool AbstractSensorTpl<ImuSensor>::areFieldnamesGrouped_{false};
 
-    ImuSensor::ImuSensor(const std::string & name) :
-    AbstractSensorTpl(name),
-    frameName_(),
-    frameIdx_(0),
-    sensorRotationBiasInv_(Eigen::Matrix3d::Identity())
-    {
-    }
-
     hresult_t ImuSensor::initialize(const std::string & frameName)
     {
         hresult_t returnCode = hresult_t::SUCCESS;
@@ -224,13 +216,6 @@ namespace jiminy
     template<>
     const bool AbstractSensorTpl<ContactSensor>::areFieldnamesGrouped_{false};
 
-    ContactSensor::ContactSensor(const std::string & name) :
-    AbstractSensorTpl(name),
-    frameName_(),
-    frameIdx_(0)
-    {
-    }
-
     hresult_t ContactSensor::initialize(const std::string & frameName)
     {
         hresult_t returnCode = hresult_t::SUCCESS;
@@ -311,15 +296,6 @@ namespace jiminy
     template<>
     const bool AbstractSensorTpl<ForceSensor>::areFieldnamesGrouped_{false};
 
-    ForceSensor::ForceSensor(const std::string & name) :
-    AbstractSensorTpl(name),
-    frameName_(),
-    frameIdx_(0),
-    parentJointIdx_(0),
-    f_()
-    {
-    }
-
     hresult_t ForceSensor::initialize(const std::string & frameName)
     {
         hresult_t returnCode = hresult_t::SUCCESS;
@@ -351,7 +327,7 @@ namespace jiminy
         if (returnCode == hresult_t::SUCCESS)
         {
             // 'parent' returns the parent joint
-            parentJointIdx_ = robot->pncModel_.frames[frameIdx_].parent;
+            parentJointModelIdx_ = robot->pncModel_.frames[frameIdx_].parent;
         }
 
         return returnCode;
@@ -367,9 +343,9 @@ namespace jiminy
         return frameIdx_;
     }
 
-    pinocchio::JointIndex ForceSensor::getJointIdx() const
+    pinocchio::JointIndex ForceSensor::getJointModelIdx() const
     {
-        return parentJointIdx_;
+        return parentJointModelIdx_;
     }
 
     hresult_t ForceSensor::set(double /* t */,
@@ -384,7 +360,7 @@ namespace jiminy
         GET_ROBOT_IF_INITIALIZED()
 
         // Get the sum of external forces applied on parent joint
-        const pinocchio::JointIndex i = parentJointIdx_;
+        const pinocchio::JointIndex i = parentJointModelIdx_;
         const pinocchio::Force & fJoint = fExternal[i];
 
         // Transform the force from joint frame to sensor frame
@@ -404,14 +380,6 @@ namespace jiminy
     template<>
     const bool AbstractSensorTpl<EncoderSensor>::areFieldnamesGrouped_{true};
 
-    EncoderSensor::EncoderSensor(const std::string & name) :
-    AbstractSensorTpl(name),
-    jointName_(),
-    jointIdx_(0),
-    jointType_(JointModelType::UNSUPPORTED)
-    {
-    }
-
     hresult_t EncoderSensor::initialize(const std::string & jointName)
     {
         hresult_t returnCode = hresult_t::SUCCESS;
@@ -446,8 +414,8 @@ namespace jiminy
 
         if (returnCode == hresult_t::SUCCESS)
         {
-            jointIdx_ = robot->pncModel_.getJointId(jointName_);
-            getJointTypeFromIdx(robot->pncModel_, jointIdx_, jointType_);
+            jointModelIdx_ = robot->pncModel_.getJointId(jointName_);
+            getJointTypeFromIdx(robot->pncModel_, jointModelIdx_, jointType_);
 
             // Motors are only supported for linear and rotary joints
             if (jointType_ != JointModelType::LINEAR && jointType_ != JointModelType::ROTARY &&
@@ -467,9 +435,9 @@ namespace jiminy
         return jointName_;
     }
 
-    pinocchio::JointIndex EncoderSensor::getJointIdx() const
+    pinocchio::JointIndex EncoderSensor::getJointModelIdx() const
     {
-        return jointIdx_;
+        return jointModelIdx_;
     }
 
     JointModelType EncoderSensor::getJointType() const
@@ -486,9 +454,9 @@ namespace jiminy
     {
         GET_ROBOT_IF_INITIALIZED()
 
-        const auto & joint = robot->pncModel_.joints[jointIdx_];
-        const int32_t jointPositionIdx = joint.idx_q();
-        const int32_t jointVelocityIdx = joint.idx_v();
+        const auto & joint = robot->pncModel_.joints[jointModelIdx_];
+        const Eigen::Index jointPositionIdx = joint.idx_q();
+        const Eigen::Index jointVelocityIdx = joint.idx_v();
         if (jointType_ == JointModelType::ROTARY_UNBOUNDED)
         {
             const double cosTheta = q[jointPositionIdx];
@@ -513,13 +481,6 @@ namespace jiminy
     template<>
     const bool AbstractSensorTpl<EffortSensor>::areFieldnamesGrouped_{true};
 
-    EffortSensor::EffortSensor(const std::string & name) :
-    AbstractSensorTpl(name),
-    motorName_(),
-    motorIdx_(-1)
-    {
-    }
-
     hresult_t EffortSensor::initialize(const std::string & motorName)
     {
         hresult_t returnCode = hresult_t::SUCCESS;
diff --git a/core/src/io/abstract_io_device.cc b/core/src/io/abstract_io_device.cc
index fddf595ee..fa3359129 100644
--- a/core/src/io/abstract_io_device.cc
+++ b/core/src/io/abstract_io_device.cc
@@ -29,11 +29,8 @@ namespace jiminy
         return static_cast<openMode_t>(~static_cast<int32_t>(mode));
     }
 
-    AbstractIODevice::AbstractIODevice() :
-    modes_(openMode_t::NOT_OPEN),
-    supportedModes_(openMode_t::NOT_OPEN),
-    lastError_(hresult_t::SUCCESS),
-    io_(nullptr)
+    AbstractIODevice::AbstractIODevice(openMode_t supportedModes) noexcept :
+    supportedModes_(supportedModes)
     {
     }
 
@@ -51,7 +48,10 @@ namespace jiminy
         {
             if ((modes & supportedModes_) != modes)
             {
-                PRINT_ERROR("At least of the modes ", modes, " is not supported.");
+                std::cout << "supportedModes_: " << supportedModes_ << " | modes: " << modes
+                          << std::endl;
+                throw std::runtime_error("ERROR");
+                PRINT_ERROR("At least one of the selected modes is not supported.");
                 returnCode = lastError_ = hresult_t::ERROR_GENERIC;
             }
         }
@@ -121,31 +121,31 @@ namespace jiminy
         return false;
     }
 
-    int64_t AbstractIODevice::size()
+    std::size_t AbstractIODevice::size()
     {
         return bytesAvailable();
     }
 
-    hresult_t AbstractIODevice::resize(int64_t /* size */)
+    hresult_t AbstractIODevice::resize(std::size_t /* size */)
     {
         lastError_ = hresult_t::ERROR_GENERIC;
         PRINT_ERROR("This method is not available.");
         return lastError_;
     }
 
-    hresult_t AbstractIODevice::seek(int64_t /* pos */)
+    hresult_t AbstractIODevice::seek(std::ptrdiff_t /* pos */)
     {
         lastError_ = hresult_t::ERROR_GENERIC;
         PRINT_ERROR("This method is not available.");
         return lastError_;
     }
 
-    int64_t AbstractIODevice::pos()
+    std::ptrdiff_t AbstractIODevice::pos()
     {
-        return 0;
+        return -1;
     }
 
-    int64_t AbstractIODevice::bytesAvailable()
+    std::size_t AbstractIODevice::bytesAvailable()
     {
         return 0;
     }
@@ -155,14 +155,14 @@ namespace jiminy
         return lastError_;
     }
 
-    hresult_t AbstractIODevice::write(const void * data, int64_t dataSize)
+    hresult_t AbstractIODevice::write(const void * data, std::size_t dataSize)
     {
-        int64_t toWrite = dataSize;
+        std::size_t toWrite = dataSize;
         const uint8_t * bufferPos = static_cast<const uint8_t *>(data);
 
         while (toWrite > 0)
         {
-            int64_t writtenBytes = writeData(bufferPos + (dataSize - toWrite), toWrite);
+            std::ptrdiff_t writtenBytes = writeData(bufferPos + (dataSize - toWrite), toWrite);
             if (writtenBytes <= 0)
             {
                 lastError_ = hresult_t::ERROR_GENERIC;
@@ -175,14 +175,14 @@ namespace jiminy
         return hresult_t::SUCCESS;
     }
 
-    hresult_t AbstractIODevice::read(void * data, int64_t dataSize)
+    hresult_t AbstractIODevice::read(void * data, std::size_t dataSize)
     {
-        int64_t toRead = dataSize;
+        std::size_t toRead = dataSize;
         uint8_t * bufferPos = static_cast<uint8_t *>(data);
 
         while (toRead > 0)
         {
-            int64_t readBytes = readData(bufferPos + (dataSize - toRead), toRead);
+            std::ptrdiff_t readBytes = readData(bufferPos + (dataSize - toRead), toRead);
             if (readBytes <= 0)
             {
                 lastError_ = hresult_t::ERROR_GENERIC;
@@ -194,82 +194,4 @@ namespace jiminy
 
         return hresult_t::SUCCESS;
     }
-
-    hresult_t AbstractIODevice::setBlockingMode(bool /* shouldBlock */)
-    {
-        lastError_ = hresult_t::ERROR_GENERIC;
-        PRINT_ERROR("This methid is not available.");
-        return lastError_;
-    }
-
-    bool AbstractIODevice::isBackendValid()
-    {
-        return (io_.get() != nullptr);
-    }
-
-    void AbstractIODevice::setBackend(std::unique_ptr<AbstractIODevice> io)
-    {
-        io_ = std::move(io);
-        supportedModes_ = io_->supportedModes();
-    }
-
-    void AbstractIODevice::removeBackend()
-    {
-        io_.reset();
-        supportedModes_ = openMode_t::NOT_OPEN;
-    }
-
-    // Specific implementation - std::vector<uint8_t>
-    template<>
-    hresult_t AbstractIODevice::read<std::vector<uint8_t>>(std::vector<uint8_t> & v)
-    {
-        int64_t toRead = static_cast<int64_t>(v.size() * sizeof(uint8_t));
-        uint8_t * bufferPos = reinterpret_cast<uint8_t *>(v.data());
-        return read(bufferPos, toRead);
-    }
-
-    // Specific implementation - std::vector<char>
-    template<>
-    hresult_t AbstractIODevice::read<std::vector<char>>(std::vector<char> & v)
-    {
-        int64_t toRead = static_cast<int64_t>(v.size() * sizeof(char));
-        uint8_t * bufferPos = reinterpret_cast<uint8_t *>(v.data());
-        return read(bufferPos, toRead);
-    }
-
-    // Specific implementation - std::string
-    template<>
-    hresult_t AbstractIODevice::write<std::string_view>(const std::string_view & str)
-    {
-        int64_t toWrite = static_cast<int64_t>(str.size());
-        const uint8_t * bufferPos = reinterpret_cast<const uint8_t *>(str.data());
-        return write(bufferPos, toWrite);
-    }
-
-    // Specific implementation - std::vector<uint8_t>
-    template<>
-    hresult_t AbstractIODevice::write<std::vector<uint8_t>>(const std::vector<uint8_t> & v)
-    {
-        int64_t toWrite = static_cast<int64_t>(v.size() * sizeof(uint8_t));
-        const uint8_t * bufferPos = reinterpret_cast<const uint8_t *>(v.data());
-        return write(bufferPos, toWrite);
-    }
-
-    // Specific implementation - std::vector<char>
-    template<>
-    hresult_t AbstractIODevice::write<std::vector<char>>(const std::vector<char> & v)
-    {
-        int64_t toWrite = static_cast<int64_t>(v.size() * sizeof(char));
-        const uint8_t * bufferPos = reinterpret_cast<const uint8_t *>(v.data());
-        return write(bufferPos, toWrite);
-    }
-
-    // Specific implementation - std::vector<uint64_t>
-    template<>
-    hresult_t AbstractIODevice::write<std::vector<uint64_t>>(const std::vector<uint64_t> & v)
-    {
-        int64_t toWrite = static_cast<int64_t>(v.size() * sizeof(uint64_t));
-        const uint8_t * bufferPos = reinterpret_cast<const uint8_t *>(&v[0]);
-        return write(bufferPos, toWrite);
-    }
 }
diff --git a/core/src/io/file_device.cc b/core/src/io/file_device.cc
index 566169543..bffa286fb 100644
--- a/core/src/io/file_device.cc
+++ b/core/src/io/file_device.cc
@@ -2,7 +2,6 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 
-#include "jiminy/core/fwd.h"
 #include "jiminy/core/io/file_device.h"
 
 #ifndef _WIN32
@@ -36,16 +35,16 @@
 
 namespace jiminy
 {
-    FileDevice::FileDevice(const std::string & filename) :
-    filename_(filename),
-    fileDescriptor_(-1)
-    {
-        supportedModes_ = openMode_t::READ_ONLY | openMode_t::WRITE_ONLY | openMode_t::READ_WRITE |
-                          openMode_t::NON_BLOCKING | openMode_t::TRUNCATE | openMode_t::NEW_ONLY |
-                          openMode_t::EXISTING_ONLY | openMode_t::APPEND | openMode_t::SYNC;
+    FileDevice::FileDevice(const std::string & filename) noexcept :
+    AbstractIODevice(
 #ifndef _WIN32
-        supportedModes_ |= openMode_t::NON_BLOCKING | openMode_t::SYNC;
+        openMode_t::NON_BLOCKING | openMode_t::SYNC |
 #endif
+        openMode_t::READ_ONLY | openMode_t::WRITE_ONLY | openMode_t::READ_WRITE |
+        openMode_t::TRUNCATE | openMode_t::NEW_ONLY | openMode_t::EXISTING_ONLY |
+        openMode_t::APPEND),
+    filename_{filename}
+    {
     }
 
     FileDevice::~FileDevice()
@@ -62,51 +61,51 @@ namespace jiminy
 
     hresult_t FileDevice::doOpen(openMode_t mode)
     {
-        int32_t posixFLags = 0;
+        int32_t openFlags = 0;
         if (mode & openMode_t::READ_ONLY)
         {
-            posixFLags |= O_RDONLY;
+            openFlags |= O_RDONLY;
         }
         if (mode & openMode_t::WRITE_ONLY)
         {
-            posixFLags |= O_WRONLY;
-            posixFLags |= O_CREAT;
+            openFlags |= O_WRONLY;
+            openFlags |= O_CREAT;
         }
         if (mode & openMode_t::READ_WRITE)
         {
-            posixFLags |= O_RDWR;
+            openFlags |= O_RDWR;
         }
         if (mode & openMode_t::TRUNCATE)
         {
-            posixFLags |= O_TRUNC;
+            openFlags |= O_TRUNC;
         }
         if (mode & openMode_t::NEW_ONLY)
         {
-            posixFLags |= O_EXCL;
+            openFlags |= O_EXCL;
         }
         if (mode & openMode_t::EXISTING_ONLY)
         {
-            posixFLags &= ~O_CREAT;
+            openFlags &= ~O_CREAT;
         }
         if (mode & openMode_t::APPEND)
         {
-            posixFLags |= O_APPEND;
+            openFlags |= O_APPEND;
         }
 #ifndef _WIN32
         if (mode & openMode_t::NON_BLOCKING)
         {
-            posixFLags |= O_NONBLOCK;
+            openFlags |= O_NONBLOCK;
         }
         if (mode & openMode_t::SYNC)
         {
-            posixFLags |= O_SYNC;
+            openFlags |= O_SYNC;
         }
 #endif
 #ifdef _WIN32
-        posixFLags |= _O_BINARY;
+        openFlags |= _O_BINARY;
 #endif
 
-        const int32_t rc = ::open(filename_.c_str(), posixFLags, S_IRUSR | S_IWUSR);
+        const int32_t rc = ::open(filename_.c_str(), openFlags, S_IRUSR | S_IWUSR);
         if (rc < 0)
         {
             lastError_ = hresult_t::ERROR_GENERIC;
@@ -135,7 +134,7 @@ namespace jiminy
         return hresult_t::SUCCESS;
     }
 
-    hresult_t FileDevice::seek(int64_t pos)
+    hresult_t FileDevice::seek(std::ptrdiff_t pos)
     {
         const ssize_t rc = ::lseek(fileDescriptor_, pos, SEEK_SET);
         if (rc < 0)
@@ -148,7 +147,7 @@ namespace jiminy
         return hresult_t::SUCCESS;
     }
 
-    int64_t FileDevice::pos()
+    std::ptrdiff_t FileDevice::pos()
     {
         const ssize_t pos_cur = ::lseek(fileDescriptor_, 0, SEEK_CUR);
         if (pos_cur < 0)
@@ -160,7 +159,7 @@ namespace jiminy
         return pos_cur;
     }
 
-    int64_t FileDevice::size()
+    std::size_t FileDevice::size()
     {
         struct stat st;
         int32_t rc = ::fstat(fileDescriptor_, &st);
@@ -172,30 +171,30 @@ namespace jiminy
         return st.st_size;
     }
 
-    int64_t FileDevice::bytesAvailable()
+    std::size_t FileDevice::bytesAvailable()
     {
         if (!isReadable())
         {
             return 0;
         }
-        return size() - pos();
+        return size() - static_cast<std::size_t>(pos());
     }
 
-    int64_t FileDevice::readData(void * data, int64_t dataSize)
+    std::ptrdiff_t FileDevice::readData(void * data, std::size_t dataSize)
     {
-        const ssize_t readBytes = ::read(fileDescriptor_, data, static_cast<size_t>(dataSize));
+        const ssize_t readBytes = ::read(fileDescriptor_, data, dataSize);
         if (readBytes < 0)
         {
             lastError_ = hresult_t::ERROR_GENERIC;
             PRINT_ERROR(
                 "The file is not open, or data buffer is outside accessible address space.");
         }
-        return readBytes;
+        return static_cast<std::ptrdiff_t>(readBytes);
     }
 
-    int64_t FileDevice::writeData(const void * data, int64_t dataSize)
+    std::ptrdiff_t FileDevice::writeData(const void * data, std::size_t dataSize)
     {
-        const ssize_t writtenBytes = ::write(fileDescriptor_, data, static_cast<size_t>(dataSize));
+        const ssize_t writtenBytes = ::write(fileDescriptor_, data, dataSize);
         if (writtenBytes < 0)
         {
             lastError_ = hresult_t::ERROR_GENERIC;
@@ -210,9 +209,9 @@ namespace jiminy
         return filename_;
     }
 
-    hresult_t FileDevice::resize(int64_t sizeIn)
+    hresult_t FileDevice::resize(std::size_t size)
     {
-        const int32_t rc = ::ftruncate(fileDescriptor_, sizeIn);
+        const int rc = ::ftruncate(fileDescriptor_, size);
         if (rc < 0)
         {
             lastError_ = hresult_t::ERROR_GENERIC;
diff --git a/core/src/io/json_loader.cc b/core/src/io/json_loader.cc
index c9565fda5..6fd3fc738 100644
--- a/core/src/io/json_loader.cc
+++ b/core/src/io/json_loader.cc
@@ -6,10 +6,8 @@
 
 namespace jiminy
 {
-    JsonLoader::JsonLoader(std::shared_ptr<AbstractIODevice> device) :
-    rootJson_(std::make_unique<Json::Value>()),
-    payload_(),
-    device_(device)
+    JsonLoader::JsonLoader(const std::shared_ptr<AbstractIODevice> & device) noexcept :
+    device_{device}
     {
     }
 
@@ -22,8 +20,8 @@ namespace jiminy
         if (returnCode == hresult_t::SUCCESS)
         {
             auto size = device_->bytesAvailable();
-            payload_.resize(static_cast<std::size_t>(size));
-            returnCode = device_->read(payload_.data(), size);
+            payload_.resize(size);
+            returnCode = device_->read(payload_);
         }
 
         if (returnCode == hresult_t::SUCCESS)
diff --git a/core/src/io/json_writer.cc b/core/src/io/json_writer.cc
index a04b028d1..74ad373af 100644
--- a/core/src/io/json_writer.cc
+++ b/core/src/io/json_writer.cc
@@ -8,8 +8,8 @@
 
 namespace jiminy
 {
-    JsonWriter::JsonWriter(std::shared_ptr<AbstractIODevice> device) :
-    device_(std::move(device))
+    JsonWriter::JsonWriter(const std::shared_ptr<AbstractIODevice> & device) noexcept :
+    device_{device}
     {
     }
 
diff --git a/core/src/io/memory_device.cc b/core/src/io/memory_device.cc
index 9b5b2e914..46b69b6b1 100644
--- a/core/src/io/memory_device.cc
+++ b/core/src/io/memory_device.cc
@@ -4,39 +4,36 @@
 
 namespace jiminy
 {
-    MemoryDevice::MemoryDevice(uint64_t size) :
-    buffer_(static_cast<std::size_t>(size)),
-    currentPos_(0)
+    MemoryDevice::MemoryDevice(std::size_t sizeIn) noexcept :
+    AbstractIODevice(
+#ifndef _WIN32
+        openMode_t::NON_BLOCKING |
+#endif
+        openMode_t::READ_ONLY | openMode_t::WRITE_ONLY | openMode_t::READ_WRITE |
+        openMode_t::APPEND),
+    buffer_(sizeIn),
+    currentPos_{0}
     {
-        supportedModes_ = openMode_t::READ_ONLY | openMode_t::WRITE_ONLY | openMode_t::READ_WRITE |
-                          openMode_t::NON_BLOCKING | openMode_t::APPEND;
     }
 
-
-    MemoryDevice::MemoryDevice(const MemoryDevice & other) :
-    buffer_(other.buffer_),
-    currentPos_(other.currentPos_)
-    {
-        supportedModes_ = other.supportedModes_;
-        modes_ = other.modes_;
-    }
-
-
     MemoryDevice::MemoryDevice(MemoryDevice && other) :
+    AbstractIODevice(std::move(static_cast<AbstractIODevice &&>(other))),
     buffer_(std::move(other.buffer_)),
-    currentPos_(other.currentPos_)
+    currentPos_{other.currentPos_}
     {
-        supportedModes_ = other.supportedModes_;
-        modes_ = other.modes_;
         other.close();
     }
 
-    MemoryDevice::MemoryDevice(std::vector<uint8_t> && initBuffer) :
+    MemoryDevice::MemoryDevice(std::vector<uint8_t> && initBuffer) noexcept :
+    AbstractIODevice(
+#ifndef _WIN32
+        openMode_t::NON_BLOCKING |
+#endif
+        openMode_t::READ_ONLY | openMode_t::WRITE_ONLY | openMode_t::READ_WRITE |
+        openMode_t::APPEND),
     buffer_(std::move(initBuffer)),
-    currentPos_(0)
+    currentPos_{0}
     {
-        supportedModes_ = openMode_t::READ_ONLY | openMode_t::WRITE_ONLY | openMode_t::READ_WRITE |
-                          openMode_t::NON_BLOCKING | openMode_t::APPEND;
     }
 
     MemoryDevice::~MemoryDevice()
@@ -44,63 +41,36 @@ namespace jiminy
         close();
     }
 
-    MemoryDevice & MemoryDevice::operator=(const MemoryDevice & other)
+    hresult_t MemoryDevice::seek(std::ptrdiff_t pos)
     {
-        buffer_ = other.buffer_;
-        currentPos_ = other.currentPos_;
-        modes_ = other.modes_;
-
-        return *this;
-    }
-
-    MemoryDevice & MemoryDevice::operator=(MemoryDevice && other)
-    {
-        buffer_ = std::move(other.buffer_);
-        currentPos_ = other.currentPos_;
-        modes_ = other.modes_;
-        other.close();
-
-        return *this;
-    }
-
-    hresult_t MemoryDevice::seek(int64_t pos)
-    {
-        if ((pos < 0) || pos > size())
+        if ((pos < 0) || static_cast<std::size_t>(pos) > size())
         {
             lastError_ = hresult_t::ERROR_GENERIC;
             PRINT_ERROR("The requested position '", pos, "' is out of scope.");
             return lastError_;
         }
-
         currentPos_ = pos;
         return hresult_t::SUCCESS;
     }
 
-    int64_t MemoryDevice::readData(void * data, int64_t dataSize)
+    std::ptrdiff_t MemoryDevice::readData(void * data, std::size_t dataSize)
     {
         // Read no more than available bytes
-        int64_t toRead = std::min(dataSize, bytesAvailable());
-        std::memcpy(data, buffer_.data() + currentPos_, static_cast<std::size_t>(toRead));
+        std::size_t toRead = std::min(dataSize, bytesAvailable());
+        std::memcpy(data, buffer_.data() + currentPos_, toRead);
         currentPos_ += toRead;
         return toRead;
     }
 
-    int64_t MemoryDevice::writeData(const void * data, int64_t dataSize)
+    std::ptrdiff_t MemoryDevice::writeData(const void * data, std::size_t dataSize)
     {
         // Write no more than available bytes
-        int64_t toWrite = std::min(dataSize, bytesAvailable());
-        std::memcpy(buffer_.data() + currentPos_, data, static_cast<std::size_t>(toWrite));
+        std::size_t toWrite = std::min(dataSize, bytesAvailable());
+        std::memcpy(buffer_.data() + currentPos_, data, toWrite);
         currentPos_ += toWrite;
         return toWrite;
     }
 
-    hresult_t MemoryDevice::setBlockingMode(bool)
-    {
-        // Since this is a memory device, it can't block when doing its job,
-        // thus we don't care about blocking mode and answer 'OK no problem'.
-        return hresult_t::SUCCESS;
-    }
-
     hresult_t MemoryDevice::doOpen(openMode_t modes)
     {
         if (!(modes & openMode_t::APPEND))
@@ -116,9 +86,9 @@ namespace jiminy
         return hresult_t::SUCCESS;
     }
 
-    hresult_t MemoryDevice::resize(int64_t size)
+    hresult_t MemoryDevice::resize(std::size_t size)
     {
-        buffer_.resize(static_cast<std::size_t>(size));
+        buffer_.resize(size);
         return hresult_t::SUCCESS;
     }
 }
diff --git a/core/src/robot/model.cc b/core/src/robot/model.cc
index 40465c3c5..08c902a34 100644
--- a/core/src/robot/model.cc
+++ b/core/src/robot/model.cc
@@ -40,7 +40,7 @@
 
 namespace jiminy
 {
-    void constraintsHolder_t::clear()
+    void constraintsHolder_t::clear() noexcept
     {
         boundJoints.clear();
         contactFrames.clear();
@@ -60,7 +60,7 @@ namespace jiminy
         const std::string & key, constraintsHolderType_t holderType)
     {
         // Pointers are NOT initialized to nullptr by default
-        constraintsMap_t * constraintsMapPtr = nullptr;
+        constraintsMap_t * constraintsMapPtr{nullptr};
         constraintsMap_t::iterator constraintIt;
         if (holderType == constraintsHolderType_t::COLLISION_BODIES)
         {
@@ -172,49 +172,7 @@ namespace jiminy
         return constraintsMapPtr->end();
     }
 
-    Model::Model() :
-    pncModelOrig_(),
-    pncModel_(),
-    collisionModelOrig_(),
-    collisionModel_(),
-    visualModelOrig_(),
-    visualModel_(),
-    pncDataOrig_(),
-    pncData_(),
-    collisionData_(),
-    visualData_(),
-    mdlOptions_(nullptr),
-    contactForces_(),
-    isInitialized_(false),
-    urdfPath_(),
-    urdfData_(),
-    meshPackageDirs_(),
-    hasFreeflyer_(false),
-    mdlOptionsHolder_(),
-    collisionBodiesNames_(),
-    contactFramesNames_(),
-    collisionBodiesIdx_(),
-    collisionPairsIdx_(),
-    contactFramesIdx_(),
-    rigidJointsNames_(),
-    rigidJointsModelIdx_(),
-    rigidJointsPositionIdx_(),
-    rigidJointsVelocityIdx_(),
-    flexibleJointsNames_(),
-    flexibleJointsModelIdx_(),
-    constraintsHolder_(),
-    positionLimitMin_(),
-    positionLimitMax_(),
-    velocityLimit_(),
-    logFieldnamesPosition_(),
-    logFieldnamesVelocity_(),
-    logFieldnamesAcceleration_(),
-    logFieldnamesForceExternal_(),
-    pncModelFlexibleOrig_(),
-    jointsAcceleration_(),
-    nq_(0),
-    nv_(0),
-    nx_(0)
+    Model::Model() noexcept
     {
         setOptions(getDefaultModelOptions());
     }
@@ -414,13 +372,14 @@ namespace jiminy
         {
             // Add the frame to the the original rigid model
             {
-                pinocchio::JointIndex parentJointId = pncModelOrig_.frames[parentFrameId].parent;
+                pinocchio::JointIndex parentJointModelId =
+                    pncModelOrig_.frames[parentFrameId].parent;
                 const pinocchio::SE3 & parentFramePlacement =
                     pncModelOrig_.frames[parentFrameId].placement;
                 const pinocchio::SE3 jointFramePlacement =
                     parentFramePlacement.act(framePlacement);
                 const pinocchio::Frame frame(
-                    frameName, parentJointId, parentFrameId, jointFramePlacement, frameType);
+                    frameName, parentJointModelId, parentFrameId, jointFramePlacement, frameType);
                 pncModelOrig_.addFrame(frame);
                 pncDataOrig_ = pinocchio::Data(pncModelOrig_);
             }
@@ -430,14 +389,14 @@ namespace jiminy
                 getFrameIdx(pncModelFlexibleOrig_,
                             parentBodyName,
                             parentFrameId);  // Cannot fail at this point
-                pinocchio::JointIndex parentJointId =
+                pinocchio::JointIndex parentJointModelId =
                     pncModelFlexibleOrig_.frames[parentFrameId].parent;
                 const pinocchio::SE3 & parentFramePlacement =
                     pncModelFlexibleOrig_.frames[parentFrameId].placement;
                 const pinocchio::SE3 jointFramePlacement =
                     parentFramePlacement.act(framePlacement);
                 const pinocchio::Frame frame(
-                    frameName, parentJointId, parentFrameId, jointFramePlacement, frameType);
+                    frameName, parentJointModelId, parentFrameId, jointFramePlacement, frameType);
                 pncModelFlexibleOrig_.addFrame(frame);
             }
 
@@ -508,8 +467,8 @@ namespace jiminy
                 getFrameIdx(pncModelFlexibleOrig_,
                             frameName,
                             frameIdx);  // Cannot fail at this point
-                pncModelFlexibleOrig_.frames.erase(std::next(pncModelFlexibleOrig_.frames.begin(),
-                                                             static_cast<uint32_t>(frameIdx)));
+                pncModelFlexibleOrig_.frames.erase(
+                    std::next(pncModelFlexibleOrig_.frames.begin(), frameIdx));
                 pncModelFlexibleOrig_.nframes--;
             }
 
@@ -1372,7 +1331,7 @@ namespace jiminy
             {
                 if (mdlOptions_->joints.positionLimitFromUrdf)
                 {
-                    for (int32_t & positionIdx : rigidJointsPositionIdx_)
+                    for (Eigen::Index positionIdx : rigidJointsPositionIdx_)
                     {
                         positionLimitMin_[positionIdx] = pncModel_.lowerPositionLimit[positionIdx];
                         positionLimitMax_[positionIdx] = pncModel_.upperPositionLimit[positionIdx];
@@ -1382,7 +1341,7 @@ namespace jiminy
                 {
                     for (std::size_t i = 0; i < rigidJointsPositionIdx_.size(); ++i)
                     {
-                        uint32_t positionIdx = rigidJointsPositionIdx_[i];
+                        Eigen::Index positionIdx = rigidJointsPositionIdx_[i];
                         positionLimitMin_[positionIdx] = mdlOptions_->joints.positionLimitMin[i];
                         positionLimitMax_[positionIdx] = mdlOptions_->joints.positionLimitMax[i];
                     }
@@ -1393,7 +1352,7 @@ namespace jiminy
                normalization and cos/sin representation. */
             for (const auto & joint : pncModel_.joints)
             {
-                uint32_t positionIdx, positionNq;
+                Eigen::Index positionIdx, positionNq;
                 switch (getJointType(joint))
                 {
                 case JointModelType::ROTARY_UNBOUNDED:
@@ -1422,7 +1381,7 @@ namespace jiminy
             {
                 if (mdlOptions_->joints.velocityLimitFromUrdf)
                 {
-                    for (int32_t & velocityIdx : rigidJointsVelocityIdx_)
+                    for (Eigen::Index & velocityIdx : rigidJointsVelocityIdx_)
                     {
                         velocityLimit_[velocityIdx] = pncModel_.velocityLimit[velocityIdx];
                     }
@@ -1431,7 +1390,7 @@ namespace jiminy
                 {
                     for (std::size_t i = 0; i < rigidJointsVelocityIdx_.size(); ++i)
                     {
-                        uint32_t velocityIdx = rigidJointsVelocityIdx_[i];
+                        Eigen::Index velocityIdx = rigidJointsVelocityIdx_[i];
                         velocityLimit_[velocityIdx] = mdlOptions_->joints.velocityLimit[i];
                     }
                 }
@@ -1489,16 +1448,17 @@ namespace jiminy
                                     frameOrig.name,
                                     frameIdx);  // Cannot fail at this point
                         const pinocchio::Frame & frame = pncModel_.frames[frameIdx];
-                        const pinocchio::JointIndex newParentIdx = frame.parent;
-                        const pinocchio::JointIndex oldParentIdx =
+                        const pinocchio::JointIndex newParentModelIdx = frame.parent;
+                        const pinocchio::JointIndex oldParentModelIdx =
                             pncModel_.getJointId(parentJointName);
                         geom.parentFrame = frameIdx;
-                        geom.parentJoint = newParentIdx;
+                        geom.parentJoint = newParentModelIdx;
 
                         /* Compute the relative displacement between the new and old joint
                            placement wrt their common parent joint. */
                         pinocchio::SE3 geomPlacementRef = pinocchio::SE3::Identity();
-                        for (pinocchio::JointIndex i = newParentIdx; i > oldParentIdx && i > 0;
+                        for (pinocchio::JointIndex i = newParentModelIdx;
+                             i > std::max(oldParentModelIdx, pinocchio::JointIndex{0});
                              i = pncModel_.parents[i])
                         {
                             geomPlacementRef = pncModel_.jointPlacements[i] * geomPlacementRef;
@@ -1812,7 +1772,7 @@ namespace jiminy
         return hresult_t::SUCCESS;
     }
 
-    GenericConfig Model::getOptions() const
+    GenericConfig Model::getOptions() const noexcept
     {
         return mdlOptionsHolder_;
     }
@@ -1851,7 +1811,7 @@ namespace jiminy
                                                        Eigen::VectorXd & qFlex) const
     {
         // Define some proxies
-        uint32_t nqRigid = pncModelOrig_.nq;
+        int nqRigid = pncModelOrig_.nq;
 
         // Check the size of the input state
         if (qRigid.size() != nqRigid)
@@ -1864,8 +1824,8 @@ namespace jiminy
         qFlex = pinocchio::neutral(pncModelFlexibleOrig_);
 
         // Compute the flexible state based on the rigid state
-        int32_t idxRigid = 0;
-        int32_t idxFlex = 0;
+        int idxRigid = 0;
+        int idxFlex = 0;
         for (; idxRigid < pncModelOrig_.njoints; ++idxFlex)
         {
             const std::string & jointRigidName = pncModelOrig_.names[idxRigid];
@@ -2078,12 +2038,12 @@ namespace jiminy
         return rigidJointsModelIdx_;
     }
 
-    const std::vector<int32_t> & Model::getRigidJointsPositionIdx() const
+    const std::vector<Eigen::Index> & Model::getRigidJointsPositionIdx() const
     {
         return rigidJointsPositionIdx_;
     }
 
-    const std::vector<int32_t> & Model::getRigidJointsVelocityIdx() const
+    const std::vector<Eigen::Index> & Model::getRigidJointsVelocityIdx() const
     {
         return rigidJointsVelocityIdx_;
     }
@@ -2132,17 +2092,17 @@ namespace jiminy
         return hasConstraintsEnabled;
     }
 
-    int32_t Model::nq() const
+    Eigen::Index Model::nq() const
     {
         return nq_;
     }
 
-    int32_t Model::nv() const
+    Eigen::Index Model::nv() const
     {
         return nv_;
     }
 
-    int32_t Model::nx() const
+    Eigen::Index Model::nx() const
     {
         return nx_;
     }
diff --git a/core/src/robot/robot.cc b/core/src/robot/robot.cc
index 5bd57bff1..4bb96c9b8 100644
--- a/core/src/robot/robot.cc
+++ b/core/src/robot/robot.cc
@@ -1,33 +1,21 @@
 #include <fstream>
 #include <exception>
 
-#include "jiminy/core/io/file_device.h"
-#include "jiminy/core/hardware/abstract_motor.h"
-#include "jiminy/core/hardware/abstract_sensor.h"
 #include "jiminy/core/utilities/helpers.h"
 #include "jiminy/core/utilities/pinocchio.h"
 #include "jiminy/core/utilities/json.h"
+#include "jiminy/core/io/file_device.h"
+#include "jiminy/core/hardware/abstract_motor.h"
+#include "jiminy/core/hardware/abstract_sensor.h"
 
 #include "jiminy/core/robot/robot.h"
 
 
 namespace jiminy
 {
-    Robot::Robot() :
-    Model(),
-    isTelemetryConfigured_(false),
-    telemetryData_(nullptr),
-    motorsHolder_(),
-    sensorsGroupHolder_(),
-    sensorTelemetryOptions_(),
-    motorsNames_(),
-    sensorsNames_(),
-    logFieldnamesCommand_(),
-    logFieldnamesMotorEffort_(),
-    nmotors_(0U),
-    mutexLocal_(std::make_unique<MutexLocal>()),
-    motorsSharedHolder_(std::make_shared<MotorSharedDataHolder_t>()),
-    sensorsSharedHolder_()
+
+    Robot::Robot() noexcept :
+    motorsSharedHolder_{std::make_shared<MotorSharedDataHolder_t>()}
     {
     }
 
@@ -185,7 +173,7 @@ namespace jiminy
                 }
 
                 // Update rotor inertia and effort limit of pinocchio model
-                int32_t jointVelocityOrigIdx;
+                Eigen::Index jointVelocityOrigIdx;
                 ::jiminy::getJointVelocityIdx(
                     robot->pncModelOrig_, motorIn.getJointName(), jointVelocityOrigIdx);
                 robot->pncModel_.rotorInertia[motorIn.getJointVelocityIdx()] =
@@ -240,7 +228,7 @@ namespace jiminy
 
         // Reset effortLimit and rotorInertia
         const std::shared_ptr<AbstractMotorBase> & motor = *motorIt;
-        int32_t jointVelocityOrigIdx;
+        Eigen::Index jointVelocityOrigIdx;
         ::jiminy::getJointVelocityIdx(pncModelOrig_, motor->getJointName(), jointVelocityOrigIdx);
         pncModel_.rotorInertia[motor->getJointVelocityIdx()] =
             pncModelOrig_.rotorInertia[jointVelocityOrigIdx];
@@ -774,7 +762,7 @@ namespace jiminy
         return returnCode;
     }
 
-    GenericConfig Robot::getOptions() const
+    GenericConfig Robot::getOptions() const noexcept
     {
         GenericConfig robotOptions;
         robotOptions["model"] = getModelOptions();
@@ -1303,16 +1291,16 @@ namespace jiminy
         return motorsModelIdx;
     }
 
-    std::vector<std::vector<int32_t>> Robot::getMotorsPositionIdx() const
+    std::vector<std::vector<Eigen::Index>> Robot::getMotorsPositionIdx() const
     {
-        std::vector<std::vector<int32_t>> motorsPositionIdx;
+        std::vector<std::vector<Eigen::Index>> motorsPositionIdx;
         motorsPositionIdx.reserve(nmotors_);
         std::transform(motorsHolder_.begin(),
                        motorsHolder_.end(),
                        std::back_inserter(motorsPositionIdx),
-                       [](const auto & elem) -> std::vector<int32_t>
+                       [](const auto & elem) -> std::vector<Eigen::Index>
                        {
-                           int32_t const & jointPositionIdx = elem->getJointPositionIdx();
+                           Eigen::Index const & jointPositionIdx = elem->getJointPositionIdx();
                            if (elem->getJointType() == JointModelType::ROTARY_UNBOUNDED)
                            {
                                return {jointPositionIdx, jointPositionIdx + 1};
@@ -1325,14 +1313,15 @@ namespace jiminy
         return motorsPositionIdx;
     }
 
-    std::vector<int32_t> Robot::getMotorsVelocityIdx() const
+    std::vector<Eigen::Index> Robot::getMotorsVelocityIdx() const
     {
-        std::vector<int32_t> motorsVelocityIdx;
+        std::vector<Eigen::Index> motorsVelocityIdx;
         motorsVelocityIdx.reserve(nmotors_);
         std::transform(motorsHolder_.begin(),
                        motorsHolder_.end(),
                        std::back_inserter(motorsVelocityIdx),
-                       [](const auto & elem) -> int32_t { return elem->getJointVelocityIdx(); });
+                       [](const auto & elem) -> Eigen::Index
+                       { return elem->getJointVelocityIdx(); });
         return motorsVelocityIdx;
     }
 
diff --git a/core/src/solver/constraint_solvers.cc b/core/src/solver/constraint_solvers.cc
index 1bebe169b..28d777f3d 100644
--- a/core/src/solver/constraint_solvers.cc
+++ b/core/src/solver/constraint_solvers.cc
@@ -21,20 +21,12 @@ namespace jiminy
                          double torsion,
                          double tolAbs,
                          double tolRel,
-                         uint32_t maxIter) :
-    model_(model),
-    data_(data),
-    maxIter_(maxIter),
-    tolAbs_(tolAbs),
-    tolRel_(tolRel),
-    J_(),
-    gamma_(),
-    lambda_(),
-    constraintsData_(),
-    b_(),
-    y_(),
-    yPrev_(),
-    isLcpFullyUpToDate_(false)
+                         uint32_t maxIter) noexcept :
+    model_{model},
+    data_{data},
+    maxIter_{maxIter},
+    tolAbs_{tolAbs},
+    tolRel_{tolRel}
     {
         Eigen::Index constraintsRowsMax = 0U;
         constraintsHolder->foreach(
diff --git a/core/src/stepper/abstract_runge_kutta_stepper.cc b/core/src/stepper/abstract_runge_kutta_stepper.cc
index 715f80c1c..460307432 100644
--- a/core/src/stepper/abstract_runge_kutta_stepper.cc
+++ b/core/src/stepper/abstract_runge_kutta_stepper.cc
@@ -9,26 +9,27 @@ namespace jiminy
                                                          const Eigen::MatrixXd & RungeKuttaMatrix,
                                                          const Eigen::VectorXd & bWeights,
                                                          const Eigen::VectorXd & cNodes,
-                                                         bool isFSAL) :
+                                                         bool isFSAL) noexcept :
     AbstractStepper(f, robots),
-    A_(RungeKuttaMatrix),
-    b_(bWeights),
-    c_(cNodes),
-    isFSAL_(isFSAL),
+    A_{RungeKuttaMatrix},
+    b_{bWeights},
+    c_{cNodes},
+    isFSAL_{isFSAL},
     ki_(cNodes.size(), stateDerivative_t(robots)),
     stateIncrement_(robots),
     stateBuffer_(robots),
     candidateSolution_(robots)
-
     {
-        assert(A_.rows() == A_.cols());
-        assert(c_.size() == A_.rows());
-        assert(b_.size() == b_.rows());
     }
 
     bool AbstractRungeKuttaStepper::tryStepImpl(
         state_t & state, stateDerivative_t & stateDerivative, double t, double & dt)
     {
+        // Make sure that the provided matrices are consistent with each other
+        assert(A_.rows() == A_.cols());
+        assert(c_.size() == A_.rows());
+        assert(b_.size() == b_.rows());
+
         // First ki is simply the provided stateDerivative
         ki_[0] = stateDerivative;
 
diff --git a/core/src/stepper/abstract_stepper.cc b/core/src/stepper/abstract_stepper.cc
index 02108a9c0..056362b1e 100644
--- a/core/src/stepper/abstract_stepper.cc
+++ b/core/src/stepper/abstract_stepper.cc
@@ -4,9 +4,9 @@
 namespace jiminy
 {
     AbstractStepper::AbstractStepper(const systemDynamics & f,
-                                     const std::vector<const Robot *> & robots) :
-    f_(f),
-    robots_(robots),
+                                     const std::vector<const Robot *> & robots) noexcept :
+    f_{f},
+    robots_{robots},
     state_(robots),
     stateDerivative_(robots),
     fOutput_(robots)
diff --git a/core/src/stepper/euler_explicit_stepper.cc b/core/src/stepper/euler_explicit_stepper.cc
index f10a4fe48..4123e1236 100644
--- a/core/src/stepper/euler_explicit_stepper.cc
+++ b/core/src/stepper/euler_explicit_stepper.cc
@@ -3,12 +3,6 @@
 
 namespace jiminy
 {
-    EulerExplicitStepper::EulerExplicitStepper(const systemDynamics & f,
-                                               const std::vector<const Robot *> & robots) :
-    AbstractStepper(f, robots)
-    {
-    }
-
     bool EulerExplicitStepper::tryStepImpl(
         state_t & state, stateDerivative_t & stateDerivative, double t, double & dt)
     {
diff --git a/core/src/stepper/runge_kutta4_stepper.cc b/core/src/stepper/runge_kutta4_stepper.cc
index bf613345a..b7407f27e 100644
--- a/core/src/stepper/runge_kutta4_stepper.cc
+++ b/core/src/stepper/runge_kutta4_stepper.cc
@@ -3,7 +3,7 @@
 namespace jiminy
 {
     RungeKutta4Stepper::RungeKutta4Stepper(const systemDynamics & f,
-                                           const std::vector<const Robot *> & robots) :
+                                           const std::vector<const Robot *> & robots) noexcept :
     AbstractRungeKuttaStepper(f, robots, RK4::A, RK4::b, RK4::c, false)
     {
     }
diff --git a/core/src/stepper/runge_kutta_dopri_stepper.cc b/core/src/stepper/runge_kutta_dopri_stepper.cc
index 827914f6a..a26b2f2a8 100644
--- a/core/src/stepper/runge_kutta_dopri_stepper.cc
+++ b/core/src/stepper/runge_kutta_dopri_stepper.cc
@@ -5,10 +5,10 @@ namespace jiminy
     RungeKuttaDOPRIStepper::RungeKuttaDOPRIStepper(const systemDynamics & f,
                                                    const std::vector<const Robot *> & robots,
                                                    double tolRel,
-                                                   double tolAbs) :
+                                                   double tolAbs) noexcept :
     AbstractRungeKuttaStepper(f, robots, DOPRI::A, DOPRI::b, DOPRI::c, true),
-    tolRel_(tolRel),
-    tolAbs_(tolAbs),
+    tolRel_{tolRel},
+    tolAbs_{tolAbs},
     scale_(robots),
     otherSolution_(robots),
     error_(robots)
diff --git a/core/src/telemetry/telemetry_data.cc b/core/src/telemetry/telemetry_data.cc
index 6fd9afcb7..fd0cdea41 100644
--- a/core/src/telemetry/telemetry_data.cc
+++ b/core/src/telemetry/telemetry_data.cc
@@ -1,19 +1,11 @@
 
+#include "jiminy/core/telemetry/fwd.h"
 #include "jiminy/core/telemetry/telemetry_data.h"
 
 
 namespace jiminy
 {
-    TelemetryData::TelemetryData() :
-    constantsRegistry_(),
-    integersRegistry_(),
-    floatsRegistry_(),
-    isRegisteringAvailable_(false)
-    {
-        reset();
-    }
-
-    void TelemetryData::reset()
+    void TelemetryData::reset() noexcept
     {
         constantsRegistry_.clear();
         integersRegistry_.clear();
diff --git a/core/src/telemetry/telemetry_recorder.cc b/core/src/telemetry/telemetry_recorder.cc
index 373ff2582..e2a8273b6 100644
--- a/core/src/telemetry/telemetry_recorder.cc
+++ b/core/src/telemetry/telemetry_recorder.cc
@@ -3,6 +3,7 @@
 #include <iomanip>
 #include <fstream>
 
+#include "jiminy/core/telemetry/fwd.h"
 #include "jiminy/core/io/file_device.h"
 #include "jiminy/core/telemetry/telemetry_data.h"
 
@@ -11,7 +12,7 @@
 
 namespace jiminy
 {
-    inline constexpr int64_t TELEMETRY_MIN_BUFFER_SIZE{256U * 1024U};  // 256Ko
+    inline constexpr std::size_t TELEMETRY_MIN_BUFFER_SIZE{256U * 1024U};  // 256Ko
 
     TelemetryRecorder::~TelemetryRecorder()
     {
@@ -49,14 +50,13 @@ namespace jiminy
             integerSectionSize_ = sizeof(int64_t) * integersRegistry_->size();
             floatsRegistry_ = telemetryData->getRegistry<double>();
             floatSectionSize_ = sizeof(double) * floatsRegistry_->size();
-            recordedBytesDataLine_ =
-                integerSectionSize_ + floatSectionSize_ +
-                static_cast<int64_t>(START_LINE_TOKEN.size() +
-                                     sizeof(int64_t));  // int64_t for Global.Time
+            recordedBytesDataLine_ = integerSectionSize_ + floatSectionSize_ +
+                                     START_LINE_TOKEN.size() +
+                                     sizeof(int64_t);  // int64_t for Global.Time
 
             // Get the header
             telemetryData->formatHeader(header);
-            headerSize_ = static_cast<int64_t>(header.size());
+            headerSize_ = header.size();
 
             // Create a new MemoryDevice and open it
             returnCode = createNewChunk();
@@ -108,7 +108,8 @@ namespace jiminy
         hresult_t returnCode = hresult_t::SUCCESS;
 
         // Close the current MemoryDevice, if any and if it was opened
-        if (!flows_.empty())
+        const bool hasHeader = !flows_.empty();
+        if (hasHeader)
         {
             flows_.back().close();
         }
@@ -117,12 +118,14 @@ namespace jiminy
            The size of the first chunk is chosen to be large enough to contain the whole header,
            including constants. This does not really affect the performances since it is written
            only once per simulation. The optimized buffer size is used for the log data. */
-        int64_t isHeaderThere = flows_.empty();
-        int64_t maxBufferSize = std::max(TELEMETRY_MIN_BUFFER_SIZE, isHeaderThere * headerSize_);
-        int64_t maxRecordedDataLines =
-            (maxBufferSize - isHeaderThere * headerSize_) / recordedBytesDataLine_;
-        recordedBytesLimits_ =
-            isHeaderThere * headerSize_ + maxRecordedDataLines * recordedBytesDataLine_;
+        std::size_t headerSize = 0;
+        if (!hasHeader)
+        {
+            headerSize = headerSize_;
+        }
+        std::size_t maxBufferSize = std::max(TELEMETRY_MIN_BUFFER_SIZE, headerSize);
+        std::size_t maxRecordedDataLines = (maxBufferSize - headerSize) / recordedBytesDataLine_;
+        recordedBytesLimits_ = headerSize + maxRecordedDataLines * recordedBytesDataLine_;
         flows_.emplace_back(recordedBytesLimits_);
         returnCode = flows_.back().open(openMode_t::READ_WRITE);
 
@@ -178,7 +181,7 @@ namespace jiminy
         {
             for (auto & flow : flows_)
             {
-                const int64_t pos_old = flow.pos();
+                const std::ptrdiff_t pos_old = flow.pos();
                 flow.seek(0);
 
                 std::vector<uint8_t> bufferChunk;
@@ -232,7 +235,7 @@ namespace jiminy
                 {
                     // Read version flag and check if valid
                     int32_t version;
-                    flow->readData(&version, sizeof(int32_t));
+                    flow->read(version);
                     if (version != TELEMETRY_VERSION)
                     {
                         PRINT_ERROR(
@@ -335,7 +338,7 @@ namespace jiminy
                 {
                     /* Check if actual data are still available.
                        It is necessary because a pre-allocated memory may not be full. */
-                    flow->readData(&startLineTokenBuffer, 1);
+                    flow->read(startLineTokenBuffer);
                     if (startLineTokenBuffer != START_LINE_TOKEN[0])
                     {
                         break;
@@ -343,9 +346,9 @@ namespace jiminy
                     flow->seek(flow->pos() + startLineTokenSize - 1);
 
                     // Read data line
-                    flow->readData(&logData.times[timeIdx], sizeof(int64_t));
-                    flow->readData(logData.integerValues.col(timeIdx).data(), integerSectionSize);
-                    flow->readData(logData.floatValues.col(timeIdx).data(), floatSectionSize);
+                    flow->read(logData.times[timeIdx]);
+                    flow->read(logData.integerValues.col(timeIdx));
+                    flow->read(logData.floatValues.col(timeIdx));
 
                     // Increment timestamp counter
                     ++timeIdx;
diff --git a/core/src/telemetry/telemetry_sender.cc b/core/src/telemetry/telemetry_sender.cc
index 9ace19b48..34b162108 100644
--- a/core/src/telemetry/telemetry_sender.cc
+++ b/core/src/telemetry/telemetry_sender.cc
@@ -3,13 +3,6 @@
 
 namespace jiminy
 {
-    TelemetrySender::TelemetrySender() :
-    objectName_(DEFAULT_TELEMETRY_NAMESPACE),
-    telemetryData_(nullptr),
-    bufferPosition_()
-    {
-    }
-
     void TelemetrySender::configureObject(std::shared_ptr<TelemetryData> telemetryDataInstance,
                                           const std::string_view & objectName)
     {
diff --git a/core/src/utilities/helpers.cc b/core/src/utilities/helpers.cc
index 1260495e9..603e11762 100644
--- a/core/src/utilities/helpers.cc
+++ b/core/src/utilities/helpers.cc
@@ -7,7 +7,7 @@
 #    include <stdio.h>
 #endif
 
-#include "jiminy/core/telemetry/telemetry_recorder.h"  // `LogData`
+#include "jiminy/core/telemetry/fwd.h"  // `LogData`
 #include "jiminy/core/utilities/helpers.h"
 
 
@@ -15,23 +15,18 @@ namespace jiminy
 {
     // *************** Local Mutex/Lock mechanism ******************
 
-    MutexLocal::MutexLocal() :
-    isLocked_(new bool{false})
-    {
-    }
-
     MutexLocal::~MutexLocal()
     {
         *isLocked_ = false;
     }
 
-    bool MutexLocal::isLocked() const
+    bool MutexLocal::isLocked() const noexcept
     {
         return *isLocked_;
     }
 
-    LockGuardLocal::LockGuardLocal(MutexLocal & mutexLocal) :
-    mutexFlag_(mutexLocal.isLocked_)
+    LockGuardLocal::LockGuardLocal(MutexLocal & mutexLocal) noexcept :
+    mutexFlag_{mutexLocal.isLocked_}
     {
         *mutexFlag_ = true;
     }
@@ -43,21 +38,18 @@ namespace jiminy
 
     // ************************* Timer **************************
 
-    Timer::Timer() :
-    t0(),
-    tf(),
-    dt(0.0)
+    Timer::Timer() noexcept
     {
         tic();
     }
 
-    void Timer::tic()
+    void Timer::tic() noexcept
     {
         t0 = Time::now();
         dt = 0.0;
     }
 
-    void Timer::toc()
+    void Timer::toc() noexcept
     {
         tf = Time::now();
         std::chrono::duration<double> timeDiff = tf - t0;
diff --git a/core/src/utilities/json.cc b/core/src/utilities/json.cc
index 89fef0bd6..7cb264bb0 100644
--- a/core/src/utilities/json.cc
+++ b/core/src/utilities/json.cc
@@ -31,9 +31,8 @@ namespace jiminy
     class AppendBoostVariantToJson : public boost::static_visitor<>
     {
     public:
-        explicit AppendBoostVariantToJson(Json::Value & root) :
-        root_(root),
-        field_()
+        explicit AppendBoostVariantToJson(Json::Value & root) noexcept :
+        root_{root}
         {
         }
 
@@ -47,7 +46,7 @@ namespace jiminy
 
     public:
         Json::Value & root_;
-        std::string field_;
+        std::string field_{};
     };
 
     template<>
@@ -122,7 +121,7 @@ namespace jiminy
     template<>
     Eigen::VectorXd convertFromJson<Eigen::VectorXd>(const Json::Value & value)
     {
-        Eigen::VectorXd vec;
+        Eigen::VectorXd vec{};
         if (value.size() > 0)
         {
             vec.resize(value.size());
@@ -137,7 +136,7 @@ namespace jiminy
     template<>
     Eigen::MatrixXd convertFromJson<Eigen::MatrixXd>(const Json::Value & value)
     {
-        Eigen::MatrixXd mat;
+        Eigen::MatrixXd mat{};
         if (value.size() > 0)
         {
             auto it = value.begin();
@@ -174,7 +173,7 @@ namespace jiminy
     template<>
     GenericConfig convertFromJson<GenericConfig>(const Json::Value & value)
     {
-        GenericConfig config;
+        GenericConfig config{};
         for (auto root = value.begin(); root != value.end(); ++root)
         {
             GenericConfig::mapped_type field;
diff --git a/core/src/utilities/pinocchio.cc b/core/src/utilities/pinocchio.cc
index 34b1bede3..23f25cba5 100644
--- a/core/src/utilities/pinocchio.cc
+++ b/core/src/utilities/pinocchio.cc
@@ -5,7 +5,6 @@
 #include "pinocchio/spatial/force.hpp"       // `pinocchio::Force`
 #include "pinocchio/spatial/inertia.hpp"     // `pinocchio::Inertia`
 #include "pinocchio/multibody/model.hpp"     // `pinocchio::Model`
-#include "pinocchio/multibody/fcl.hpp"       // `pinocchio::GeometryType`
 #include "pinocchio/multibody/geometry.hpp"  // `pinocchio::GeometryModel`
 #include "pinocchio/multibody/data.hpp"      // `pinocchio::Data`
 #include "pinocchio/multibody/visitor.hpp"   // `pinocchio::fusion::JointUnaryVisitorBase`
@@ -15,8 +14,7 @@
 #include "hpp/fcl/mesh_loader/loader.h"
 #include "hpp/fcl/BVH/BVH_model.h"
 
-#include "jiminy/core/traits.h"
-#include "jiminy/core/telemetry/telemetry_recorder.h"  // `LogData`
+#include "jiminy/core/telemetry/fwd.h"  // `LogData`
 #include "jiminy/core/utilities/helpers.h"
 #include "jiminy/core/utilities/pinocchio.h"
 
@@ -26,15 +24,15 @@ namespace jiminy
     hresult_t getJointNameFromPositionIdx(
         const pinocchio::Model & model, int32_t idx, std::string & jointNameOut)
     {
-        // Iterate over all joints.
+        // Iterate over all joints
         for (pinocchio::JointIndex i = 0; i < static_cast<pinocchio::JointIndex>(model.njoints);
              ++i)
         {
-            // Get joint starting and ending index in position vector.
+            // Get joint starting and ending index in position vector
             const int32_t startIdx = model.joints[i].idx_q();
             const int32_t endIdx = startIdx + model.joints[i].nq();
 
-            // If idx is between start and end, we found the joint we were looking for.
+            // If idx is between start and end, we found the joint we were looking for
             if (startIdx <= idx && idx < endIdx)
             {
                 jointNameOut = model.names[i];
@@ -49,11 +47,11 @@ namespace jiminy
     hresult_t getJointNameFromVelocityIdx(
         const pinocchio::Model & model, int32_t idx, std::string & jointNameOut)
     {
-        // Iterate over all joints.
+        // Iterate over all joints
         for (pinocchio::JointIndex i = 0; i < static_cast<pinocchio::JointIndex>(model.njoints);
              ++i)
         {
-            // Get joint starting and ending index in velocity vector.
+            // Get joint starting and ending index in velocity vector
             const int32_t startIdx = model.joints[i].idx_v();
             const int32_t endIdx = startIdx + model.joints[i].nv();
 
@@ -147,7 +145,7 @@ namespace jiminy
         }
     };
 
-    JointModelType getJointType(const pinocchio::JointModel & jointModel)
+    JointModelType getJointType(const pinocchio::JointModel & jointModel) noexcept
     {
         JointModelType jointTypeOut{JointModelType::UNSUPPORTED};
         getJointTypeAlgo::run(jointModel, typename getJointTypeAlgo::ArgsType(jointTypeOut));
@@ -280,7 +278,7 @@ namespace jiminy
 
     hresult_t getJointPositionIdx(const pinocchio::Model & model,
                                   const std::string & jointName,
-                                  std::vector<int32_t> & jointPositionIdx)
+                                  std::vector<Eigen::Index> & jointPositionIdx)
     {
         // It returns all the indices if the joint has multiple degrees of freedom
 
@@ -291,8 +289,8 @@ namespace jiminy
         }
 
         const pinocchio::JointIndex jointModelIdx = model.getJointId(jointName);
-        const int32_t jointPositionFirstIdx = model.joints[jointModelIdx].idx_q();
-        const int32_t jointNq = model.joints[jointModelIdx].nq();
+        const Eigen::Index jointPositionFirstIdx = model.joints[jointModelIdx].idx_q();
+        const Eigen::Index jointNq = model.joints[jointModelIdx].nq();
         jointPositionIdx.resize(static_cast<std::size_t>(jointNq));
         std::iota(jointPositionIdx.begin(), jointPositionIdx.end(), jointPositionFirstIdx);
 
@@ -301,7 +299,7 @@ namespace jiminy
 
     hresult_t getJointPositionIdx(const pinocchio::Model & model,
                                   const std::string & jointName,
-                                  int32_t & jointPositionFirstIdx)
+                                  Eigen::Index & jointPositionFirstIdx)
     {
         // It returns the first index even if the joint has multiple degrees of freedom
 
@@ -319,7 +317,7 @@ namespace jiminy
 
     hresult_t getJointsPositionIdx(const pinocchio::Model & model,
                                    const std::vector<std::string> & jointsNames,
-                                   std::vector<int32_t> & jointsPositionIdx,
+                                   std::vector<Eigen::Index> & jointsPositionIdx,
                                    bool firstJointIdxOnly)
     {
         hresult_t returnCode = hresult_t::SUCCESS;
@@ -327,7 +325,7 @@ namespace jiminy
         jointsPositionIdx.clear();
         if (!firstJointIdxOnly)
         {
-            std::vector<int32_t> jointPositionIdx;
+            std::vector<Eigen::Index> jointPositionIdx;
             for (const std::string & jointName : jointsNames)
             {
                 if (returnCode == hresult_t::SUCCESS)
@@ -343,7 +341,7 @@ namespace jiminy
         }
         else
         {
-            int32_t jointPositionIdx;
+            Eigen::Index jointPositionIdx;
             for (const std::string & jointName : jointsNames)
             {
                 if (returnCode == hresult_t::SUCCESS)
@@ -402,7 +400,7 @@ namespace jiminy
 
     hresult_t getJointVelocityIdx(const pinocchio::Model & model,
                                   const std::string & jointName,
-                                  std::vector<int32_t> & jointVelocityIdx)
+                                  std::vector<Eigen::Index> & jointVelocityIdx)
     {
         // It returns all the indices if the joint has multiple degrees of freedom
 
@@ -413,8 +411,8 @@ namespace jiminy
         }
 
         const pinocchio::JointIndex jointModelIdx = model.getJointId(jointName);
-        const int32_t jointVelocityFirstIdx = model.joints[jointModelIdx].idx_v();
-        const int32_t jointNv = model.joints[jointModelIdx].nv();
+        const Eigen::Index jointVelocityFirstIdx = model.joints[jointModelIdx].idx_v();
+        const Eigen::Index jointNv = model.joints[jointModelIdx].nv();
         jointVelocityIdx.resize(static_cast<std::size_t>(jointNv));
         std::iota(jointVelocityIdx.begin(), jointVelocityIdx.end(), jointVelocityFirstIdx);
 
@@ -423,7 +421,7 @@ namespace jiminy
 
     hresult_t getJointVelocityIdx(const pinocchio::Model & model,
                                   const std::string & jointName,
-                                  int32_t & jointVelocityFirstIdx)
+                                  Eigen::Index & jointVelocityFirstIdx)
     {
         // It returns the first index even if the joint has multiple degrees of freedom
 
@@ -441,7 +439,7 @@ namespace jiminy
 
     hresult_t getJointsVelocityIdx(const pinocchio::Model & model,
                                    const std::vector<std::string> & jointsNames,
-                                   std::vector<int32_t> & jointsVelocityIdx,
+                                   std::vector<Eigen::Index> & jointsVelocityIdx,
                                    bool firstJointIdxOnly)
     {
         hresult_t returnCode = hresult_t::SUCCESS;
@@ -449,7 +447,7 @@ namespace jiminy
         jointsVelocityIdx.clear();
         if (!firstJointIdxOnly)
         {
-            std::vector<int32_t> jointVelocityIdx;
+            std::vector<Eigen::Index> jointVelocityIdx;
             for (const std::string & jointName : jointsNames)
             {
                 if (returnCode == hresult_t::SUCCESS)
@@ -465,7 +463,7 @@ namespace jiminy
         }
         else
         {
-            int32_t jointVelocityIdx;
+            Eigen::Index jointVelocityIdx;
             for (const std::string & jointName : jointsNames)
             {
                 if (returnCode == hresult_t::SUCCESS)
diff --git a/core/src/utilities/random.cc b/core/src/utilities/random.cc
index 995589e08..8b337d24b 100644
--- a/core/src/utilities/random.cc
+++ b/core/src/utilities/random.cc
@@ -1,7 +1,5 @@
 #include <numeric>
 
-#include "jiminy/core/traits.h"
-
 #include "jiminy/core/utilities/random.h"
 
 
@@ -15,16 +13,16 @@ namespace jiminy
     // Based on Ziggurat generator by Marsaglia and Tsang (JSS, 2000):
     // https://people.sc.fsu.edu/~jburkardt/cpp_src/ziggurat/ziggurat.html
 
-    std::mt19937 generator_;
+    std::mt19937 generator_{};
     std::uniform_real_distribution<float> distUniform_(0.0, 1.0);
-    bool isInitialized_ = false;
-    uint32_t seed_ = 0U;
+    bool isInitialized_{false};
+    uint32_t seed_{0U};
 
     uint32_t kn[128];
     float fn[128];
     float wn[128];
 
-    void r4_nor_setup()
+    void r4_nor_setup() noexcept
     {
         const double m1 = 2147483648.0;
         const double vn = 9.91256303526217e-03;
@@ -116,7 +114,7 @@ namespace jiminy
         }
     }
 
-    void resetRandomGenerators(const std::optional<uint32_t> & seed)
+    void resetRandomGenerators(const std::optional<uint32_t> & seed) noexcept
     {
         uint32_t newSeed = seed.value_or(seed_);
         srand(newSeed);  // Eigen relies on srand for generating random numbers
@@ -158,9 +156,7 @@ namespace jiminy
         if (std > 0.0)
         {
             return Eigen::VectorXd::NullaryExpr(
-                size,
-                [&mean, &std](const Eigen::VectorXd::Index &) -> double
-                { return randNormal(mean, std); });
+                size, [mean, std](Eigen::Index) -> double { return randNormal(mean, std); });
         }
         return Eigen::VectorXd::Constant(size, mean);
     }
@@ -172,17 +168,15 @@ namespace jiminy
 
     Eigen::VectorXd randVectorNormal(const Eigen::VectorXd & mean, const Eigen::VectorXd & std)
     {
-        return Eigen::VectorXd::NullaryExpr(
-            std.size(),
-            [&mean, &std](const Eigen::VectorXd::Index & i) -> double
-            { return randNormal(mean[i], std[i]); });
+        return Eigen::VectorXd::NullaryExpr(std.size(),
+                                            [&mean, &std](Eigen::Index i) -> double
+                                            { return randNormal(mean[i], std[i]); });
     }
 
     Eigen::VectorXd randVectorNormal(const Eigen::VectorXd & std)
     {
-        return Eigen::VectorXd::NullaryExpr(std.size(),
-                                            [&std](const Eigen::VectorXd::Index & i) -> double
-                                            { return randNormal(0, std[i]); });
+        return Eigen::VectorXd::NullaryExpr(
+            std.size(), [&std](Eigen::Index i) -> double { return randNormal(0, std[i]); });
     }
 
     void shuffleIndices(std::vector<uint32_t> & vector)
@@ -195,12 +189,12 @@ namespace jiminy
     // domain. The author hereby disclaims copyright to this source code:
     // https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp
 
-    inline uint32_t rotl32(uint32_t x, int8_t r)
+    inline uint32_t rotl32(uint32_t x, int8_t r) noexcept
     {
         return (x << r) | (x >> (32 - r));
     }
 
-    uint32_t MurmurHash3(const void * key, int32_t len, uint32_t seed)
+    uint32_t MurmurHash3(const void * key, int32_t len, uint32_t seed) noexcept
     {
         // Define some internal constants
         const uint32_t c1 = 0xcc9e2d51;
@@ -263,80 +257,19 @@ namespace jiminy
 
     //-----------------------------------------------------------------------------
 
-    /// \brief Lower Cholesky factor of a Toeplitz positive semi-definite matrix.
-    ///
-    /// \details In practice, it is advisable to combine this algorithm with Tikhonov
-    ///          regularization of relative magnitude 1e-9 to avoid numerical instabilities because
-    ///          of double machine precision.
-    ///
-    /// \see Michael Stewart, Cholesky factorization of semi-definite Toeplitz matrices. Linear
-    ///      Algebra and its Applications, Volume 254, pages 497-525, 1997.
-    ///
-    /// \see https://people.sc.fsu.edu/~jburkardt/cpp_src/toeplitz_cholesky/toeplitz_cholesky.html
-    ///
-    /// \param[in] a Toeplitz matrix to decompose.
-    /// \param[out] l Lower Cholesky factor.
-    template<typename DerivedType1, typename DerivedType2>
-    void toeplitzCholeskyLower(const Eigen::MatrixBase<DerivedType1> & a,
-                               Eigen::MatrixBase<DerivedType2> & l)
-    {
-        using Scalar = typename DerivedType1::Scalar;
-
-        // Initialize lower Cholesky factor. Resizing is enough, no need to initialize it.
-        const Eigen::Index n = a.rows();
-        l.resize(n, n);
-
-        /* Compute compressed representation of the matrix.
-           It coincides with the Schur generator for Toepliz matrices.
-           TODO: Investigate if row-major is more efficient than column-major storage order.
-           TODO: Avoid 'eval' by reversing columns and thereby shifting data to the right. */
-        Eigen::Matrix<Scalar, 2, Eigen::Dynamic> g(2, n);
-        g.row(0) = a.row(0);
-        g.row(1).tail(n - 1) = a.col(0).tail(n - 1);
-        g(1, 0) = 0.0;
-
-        // Run progressive Schur algorithm, adapted to Toepliz matrices
-        l.col(0) = g.row(0);
-        g.row(0).tail(n - 1) = g.row(0).head(n - 1).eval();
-        g(0, 0) = 0.0;
-        Eigen::Matrix<Scalar, 2, 2> H = Eigen::Matrix<Scalar, 2, 2>::Ones();
-        for (Eigen::Index i = 1; i < n; ++i)
-        {
-            const double rho = -g(1, i) / g(0, i);
-            // H << 1.0, rho,
-            //      rho, 1.0;
-            Eigen::Map<Eigen::Matrix<Scalar, 4, 1>>(H.data()).template segment<2>(1).fill(rho);
-            g.rightCols(n - i + 1) = H * g.rightCols(n - i + 1) / std::sqrt(1.0 - rho * rho);
-            l.col(i).tail(n - i + 1) = g.row(0).tail(n - i + 1);
-            g.row(0).tail(n - i) = g.row(0).segment(i - 1, n - i).eval();
-            g(0, i - 1) = 0.0;
-        }
-    }
-
     PeriodicGaussianProcess::PeriodicGaussianProcess(
-        double wavelength, double period, double scale) :
-    wavelength_(wavelength),
-    period_(period),
-    scale_(scale),
-    dt_(0.02 * wavelength_),
-    numTimes_(static_cast<int32_t>(std::ceil(period_ / dt_))),
-    isInitialized_(false),
-    values_(numTimes_),
-    covSqrtRoot_(numTimes_, numTimes_)
+        double wavelength, double period, double scale) noexcept :
+    wavelength_{wavelength},
+    period_{period},
+    scale_{scale}
     {
     }
 
     void PeriodicGaussianProcess::reset()
     {
-        // Initialize the process if not already done
-        if (!isInitialized_)
-        {
-            initialize();
-        }
-
         // Sample normal vector
         const Eigen::VectorXd normalVec =
-            Eigen::VectorXd::NullaryExpr(numTimes_, [](double) { return randNormal(); });
+            Eigen::VectorXd::NullaryExpr(numTimes_, [](Eigen::Index) { return randNormal(); });
 
         // Compute discrete periodic gaussian process values
         values_.noalias() = covSqrtRoot_.triangularView<Eigen::Lower>() * normalVec;
@@ -344,12 +277,6 @@ namespace jiminy
 
     double PeriodicGaussianProcess::operator()(const float & t)
     {
-        // Reset the process if not initialized
-        if (!isInitialized_)
-        {
-            reset();
-        }
-
         // Wrap requested time in gaussian process period
         double tWrap = std::fmod(t, period_);
         if (tWrap < 0)
@@ -381,63 +308,21 @@ namespace jiminy
         return dt_;
     }
 
-    void PeriodicGaussianProcess::initialize()
-    {
-        // Compute distance matrix
-        Eigen::MatrixXd distMat(numTimes_, numTimes_);
-        for (int32_t i = 0; i < numTimes_; ++i)
-        {
-            distMat.diagonal(i).setConstant(dt_ * i);
-        }
-        distMat.triangularView<Eigen::StrictlyLower>() = distMat.transpose();
-
-        // Compute covariance matrix
-        Eigen::MatrixXd cov(numTimes_, numTimes_);
-        cov = distMat.array().abs().unaryExpr(
-            [period = period_, wavelength = wavelength_](double dist)
-            { return std::exp(-2.0 * std::pow(std::sin(M_PI / period * dist) / wavelength, 2)); });
-
-        /* Perform Square-Root-Free Cholesky decomposition (LDLT).
-           All decompositions are equivalent as the covariance matrix is symmetric, namely Eigen
-           Value, Singular Value, Square-Root-Free Cholesky and Schur decompositions. Cholesky
-           is by far the most efficient one (https://math.stackexchange.com/q/22825/375496).
-           Moreover, the covariance is positive semi-definite toepliz matrix, so computational
-           complexity can be reduced even further using optimized Cholesky algorithm. */
-        toeplitzCholeskyLower(cov + 1.0e-9 * Eigen::MatrixXd::Identity(numTimes_, numTimes_),
-                              covSqrtRoot_);
-
-        // At this point, it is fully initialized
-        isInitialized_ = true;
-    }
-
     PeriodicFourierProcess::PeriodicFourierProcess(
-        double wavelength, double period, double scale) :
-    wavelength_(wavelength),
-    period_(period),
-    scale_(scale),
-    dt_(0.02 * wavelength_),
-    numTimes_(static_cast<int32_t>(std::ceil(period_ / dt_))),
-    numHarmonics_(static_cast<int32_t>(std::ceil(period_ / wavelength_))),
-    isInitialized_(false),
-    values_(numTimes_),
-    cosMat_(numTimes_, numHarmonics_),
-    sinMat_(numTimes_, numHarmonics_)
+        double wavelength, double period, double scale) noexcept :
+    wavelength_{wavelength},
+    period_{period},
+    scale_{scale}
     {
     }
 
     void PeriodicFourierProcess::reset()
     {
-        // Initialize the process if not already done
-        if (!isInitialized_)
-        {
-            initialize();
-        }
-
         // Sample normal vectors
         Eigen::VectorXd normalVec1 =
-            Eigen::VectorXd::NullaryExpr(numHarmonics_, [](double) { return randNormal(); });
+            Eigen::VectorXd::NullaryExpr(numHarmonics_, [](Eigen::Index) { return randNormal(); });
         Eigen::VectorXd normalVec2 =
-            Eigen::VectorXd::NullaryExpr(numHarmonics_, [](double) { return randNormal(); });
+            Eigen::VectorXd::NullaryExpr(numHarmonics_, [](Eigen::Index) { return randNormal(); });
 
         // Compute discrete periodic gaussian process values
         values_ = M_SQRT2 / std::sqrt(2 * numHarmonics_ + 1) *
@@ -446,12 +331,6 @@ namespace jiminy
 
     double PeriodicFourierProcess::operator()(const float & t)
     {
-        // Reset the process if not initialized
-        if (!isInitialized_)
-        {
-            reset();
-        }
-
         // Wrap requested time in guassian process period
         double tWrap = std::fmod(t, period_);
         if (tWrap < 0)
@@ -488,29 +367,10 @@ namespace jiminy
         return dt_;
     }
 
-    void PeriodicFourierProcess::initialize()
-    {
-        // Compute exponential base at given time
-        for (int32_t colIdx = 0; colIdx < numHarmonics_; ++colIdx)
-        {
-            for (int32_t rowIdx = 0; rowIdx < numTimes_; ++rowIdx)
-            {
-                const double freq = colIdx / period_;
-                const double t = dt_ * rowIdx;
-                const double phase = 2 * M_PI * freq * t;
-                cosMat_(rowIdx, colIdx) = std::cos(phase);
-                sinMat_(rowIdx, colIdx) = std::sin(phase);
-            }
-        }
-
-        // At this point, it is fully initialized
-        isInitialized_ = true;
-    }
-
-    AbstractPerlinNoiseOctave::AbstractPerlinNoiseOctave(double wavelength, double scale) :
-    wavelength_(wavelength),
-    scale_(scale),
-    shift_(0.0)
+    AbstractPerlinNoiseOctave::AbstractPerlinNoiseOctave(double wavelength, double scale) noexcept
+    :
+    wavelength_{wavelength},
+    scale_{scale}
     {
     }
 
@@ -564,12 +424,6 @@ namespace jiminy
         return yLeft + ratio * (yRight - yLeft);
     }
 
-    RandomPerlinNoiseOctave::RandomPerlinNoiseOctave(double wavelength, double scale) :
-    AbstractPerlinNoiseOctave(wavelength, scale),
-    seed_(0)
-    {
-    }
-
     void RandomPerlinNoiseOctave::reset()
     {
         // Call base implementation
@@ -598,8 +452,7 @@ namespace jiminy
     PeriodicPerlinNoiseOctave::PeriodicPerlinNoiseOctave(
         double wavelength, double period, double scale) :
     AbstractPerlinNoiseOctave(wavelength, scale),
-    period_(period),
-    perm_(256)
+    period_{period}
     {
         // Make sure the wavelength is multiple of the period
         assert(std::abs(period_ - period) < 1e-6);
@@ -633,24 +486,15 @@ namespace jiminy
     }
 
     AbstractPerlinProcess::AbstractPerlinProcess(
-        double wavelength, double scale, uint32_t numOctaves) :
-    wavelength_(wavelength),
-    numOctaves_(numOctaves),
-    scale_(scale),
-    isInitialized_(false),
-    octaves_(),
-    amplitude_(0.0)
+        double scale, std::vector<std::unique_ptr<AbstractPerlinNoiseOctave>> && octaves) noexcept
+    :
+    scale_{scale},
+    octaves_(std::move(octaves))
     {
     }
 
     void AbstractPerlinProcess::reset()
     {
-        // Initialize the process if not already done
-        if (!isInitialized_)
-        {
-            initialize();
-        }
-
         // Reset every octave successively
         for (auto & octave : octaves_)
         {
@@ -668,12 +512,6 @@ namespace jiminy
 
     double AbstractPerlinProcess::operator()(const float & t)
     {
-        // Reset the process if not initialized
-        if (!isInitialized_)
-        {
-            reset();
-        }
-
         // Compute sum of octaves' values
         double value = 0.0;
         for (const auto & octave : octaves_)
@@ -685,86 +523,74 @@ namespace jiminy
         return value / amplitude_;
     }
 
-    double AbstractPerlinProcess::getWavelength() const
+    double AbstractPerlinProcess::getWavelength() const noexcept
     {
-        return wavelength_;
+        return std::transform_reduce(
+            octaves_.cbegin(),
+            octaves_.cend(),
+            INF,
+            std::less<double>(),
+            [](const std::unique_ptr<AbstractPerlinNoiseOctave> & octave) -> double
+            { return octave->getWavelength(); });
     }
 
-    uint32_t AbstractPerlinProcess::getNumOctaves() const
+    std::size_t AbstractPerlinProcess::getNumOctaves() const noexcept
     {
-        return numOctaves_;
+        return octaves_.size();
     }
 
-    double AbstractPerlinProcess::getScale() const
+    double AbstractPerlinProcess::getScale() const noexcept
     {
         return scale_;
     }
 
-    RandomPerlinProcess::RandomPerlinProcess(
-        double wavelength, double scale, uint32_t numOctaves) :
-    AbstractPerlinProcess(wavelength, scale, numOctaves)
+    std::vector<std::unique_ptr<AbstractPerlinNoiseOctave>> buildPerlinNoiseOctaves(
+        double wavelength,
+        std::size_t numOctaves,
+        std::function<std::unique_ptr<AbstractPerlinNoiseOctave>(double, double)> factory)
     {
-    }
-
-    void RandomPerlinProcess::initialize()
-    {
-        // Add desired perlin noise octaves
-        octaves_.clear();
-        octaves_.reserve(numOctaves_);
-        double octaveWavelength = wavelength_;
-        double octaveScale = 1.0;
-        for (uint32_t i = 0; i < numOctaves_; ++i)
+        std::vector<std::unique_ptr<AbstractPerlinNoiseOctave>> octaves_;
+        octaves_.reserve(numOctaves);
+        double scale = 1.0;
+        for (std::size_t i = 0; i < numOctaves; ++i)
         {
-            octaves_.emplace_back(
-                std::make_unique<RandomPerlinNoiseOctave>(octaveWavelength, octaveScale));
-            octaveScale *= PERLIN_NOISE_PERSISTENCE;
-            octaveWavelength *= PERLIN_NOISE_LACUNARITY;
+            octaves_.push_back(factory(wavelength, scale));
+            wavelength *= PERLIN_NOISE_LACUNARITY;
+            scale *= PERLIN_NOISE_PERSISTENCE;
         }
+        return octaves_;
+    }
 
-        // At this point, it is fully initialized
-        isInitialized_ = true;
+    RandomPerlinProcess::RandomPerlinProcess(
+        double wavelength, double scale, std::size_t numOctaves) :
+    AbstractPerlinProcess(
+        scale,
+        buildPerlinNoiseOctaves(
+            wavelength,
+            numOctaves,
+            [](double wavelengthIn, double scaleIn) -> std::unique_ptr<AbstractPerlinNoiseOctave>
+            { return std::make_unique<RandomPerlinNoiseOctave>(wavelengthIn, scaleIn); }))
+    {
     }
 
     PeriodicPerlinProcess::PeriodicPerlinProcess(
-        double wavelength, double period, double scale, uint32_t numOctaves) :
-    AbstractPerlinProcess(wavelength, scale, numOctaves),
-    period_(period)
+        double wavelength, double period, double scale, std::size_t numOctaves) :
+    AbstractPerlinProcess(
+        scale,
+        buildPerlinNoiseOctaves(
+            wavelength,
+            numOctaves,
+            [period](double wavelengthIn,
+                     double scaleIn) -> std::unique_ptr<AbstractPerlinNoiseOctave> {
+                return std::make_unique<PeriodicPerlinNoiseOctave>(wavelengthIn, period, scaleIn);
+            })),
+    period_{period}
     {
         // Make sure the period is larger than the wavelength
         assert(period_ >= wavelength && "Period must be larger than wavelength.");
     }
 
-    void PeriodicPerlinProcess::initialize()
-    {
-        // Add desired perlin noise octaves
-        octaves_.clear();
-        octaves_.reserve(numOctaves_);
-        double octaveWavelength = wavelength_;
-        double octaveScale = 1.0;
-        for (uint32_t i = 0; i < numOctaves_; ++i)
-        {
-            // Make sure the octave wavelength is divisor of the period
-            if (octaveWavelength > period_)
-            {
-                // Do not add more octoves if current wavelength is larger than the period
-                break;
-            }
-            octaveWavelength = period_ / std::floor(period_ / octaveWavelength);
-
-            // Instantiate and add octave
-            octaves_.emplace_back(std::make_unique<PeriodicPerlinNoiseOctave>(
-                octaveWavelength, period_, octaveScale));
-
-            // Update scale and wavelength for next octave
-            octaveScale *= PERLIN_NOISE_PERSISTENCE;
-            octaveWavelength *= PERLIN_NOISE_LACUNARITY;
-        }
-
-        // At this point, it is fully initialized
-        isInitialized_ = true;
-    }
-
-    double PeriodicPerlinProcess::getPeriod() const
+    double PeriodicPerlinProcess::getPeriod() const noexcept
     {
         return period_;
     }
@@ -785,7 +611,7 @@ namespace jiminy
         return 0.0;
     }
 
-    std::pair<double, double> tile2dInterp1d(Eigen::Matrix<int32_t, 2, 1> & posIdx,
+    std::pair<double, double> tile2dInterp1d(Eigen::Vector2i & posIdx,
                                              const Eigen::Vector2d & posRel,
                                              uint32_t dim,
                                              const Eigen::Vector2d & size,
@@ -842,9 +668,9 @@ namespace jiminy
         interpThreshold.array() /= size.array();
 
         const Eigen::Vector2d offset = Eigen::Vector2d::NullaryExpr(
-            [&size, &seed](const Eigen::VectorXd::Index & i) -> double
+            [&size, seed](Eigen::Index i) -> double
             {
-                Eigen::Matrix<Eigen::VectorXd::Index, 1, 1> key;
+                Eigen::Matrix<Eigen::Index, 1, 1> key;
                 key[0] = i;
                 return randomDouble(key, 1, size[i], seed);
             });
@@ -858,7 +684,7 @@ namespace jiminy
             // Compute the tile index and relative coordinate
             Eigen::Vector2d pos = rotationMat * (pos3.head<2>() + offset);
             Eigen::Vector2d posRel = pos.array() / size.array();
-            Eigen::Matrix<int32_t, 2, 1> posIdx = posRel.array().floor().cast<int32_t>();
+            Eigen::Vector2i posIdx = posRel.array().floor().cast<int32_t>();
             posRel -= posIdx.cast<double>();
 
             // Interpolate height based on nearby tiles if necessary
diff --git a/core/unit/engine_sanity_check.cc b/core/unit/engine_sanity_check.cc
index 459711840..b24128e28 100755
--- a/core/unit/engine_sanity_check.cc
+++ b/core/unit/engine_sanity_check.cc
@@ -7,6 +7,8 @@
 #define EIGEN_RUNTIME_NO_MALLOC
 
 #include "jiminy/core/fwd.h"
+#include "jiminy/core/telemetry/fwd.h"
+#include "jiminy/core/hardware/fwd.h"
 #include "jiminy/core/utilities/helpers.h"
 #include "jiminy/core/hardware/abstract_sensor.h"
 #include "jiminy/core/hardware/basic_motors.h"
diff --git a/python/jiminy_py/src/jiminy_py/viewer/meshcat/wrapper.py b/python/jiminy_py/src/jiminy_py/viewer/meshcat/wrapper.py
index 9924562b7..d84044b67 100644
--- a/python/jiminy_py/src/jiminy_py/viewer/meshcat/wrapper.py
+++ b/python/jiminy_py/src/jiminy_py/viewer/meshcat/wrapper.py
@@ -5,7 +5,6 @@
 import sys
 import urllib
 import base64
-import atexit
 import asyncio
 import logging
 import pathlib
@@ -341,9 +340,6 @@ def __init__(self,
 
             self.comm_manager = CommManager(comm_url)
 
-        # Make sure the server is properly closed
-        atexit.register(self.close)
-
     def __del__(self) -> None:
         self.close()
 
diff --git a/python/jiminy_py/src/jiminy_py/viewer/viewer.py b/python/jiminy_py/src/jiminy_py/viewer/viewer.py
index 468317c69..06e2333d7 100644
--- a/python/jiminy_py/src/jiminy_py/viewer/viewer.py
+++ b/python/jiminy_py/src/jiminy_py/viewer/viewer.py
@@ -10,7 +10,6 @@
 import math
 import shutil
 import base64
-import atexit
 import logging
 import pathlib
 import tempfile
@@ -306,19 +305,17 @@ def fun_safe(*args: Any, **kwargs: Any) -> Any:
 class _ProcessWrapper:
     """Wrap `multiprocessing.process.BaseProcess`, `subprocess.Popen`, and
     `psutil.Process` in the same object to provide the same user interface.
-
-    It also makes sure that the process is properly terminated at Python exits,
-    and without zombies left behind.
     """
     def __init__(self,
                  proc: Union[
                      multiprocessing.process.BaseProcess, subprocess.Popen,
-                     Panda3dApp, Process],
-                 kill_at_exit: bool = False):
+                     Panda3dApp, Process]):
         self._proc = proc
-        # Make sure the process is killed at Python exit
-        if kill_at_exit:
-            atexit.register(self.kill)
+
+    def __del__(self) -> None:
+        """Automatically kill process at garbage collection.
+        """
+        self.kill()
 
     def is_parent(self) -> bool:
         """ TODO: Write documentation.
@@ -657,10 +654,7 @@ def __init__(self,  # pylint: disable=unused-argument
             force_update_visual=True, force_update_collision=True, wait=True)
 
     def __del__(self) -> None:
-        """Destructor.
-
-        .. note::
-            It automatically close the viewer before being garbage collected.
+        """Automatically close the viewer at garbage collection.
         """
         self.close()
 
@@ -1108,7 +1102,6 @@ def close(self: Optional[Union["Viewer", Type["Viewer"]]] = None) -> None:
                         pass
                 Viewer._backend_proc.wait(0.2)
                 Viewer._backend_proc.kill()
-            atexit.unregister(Viewer.close)
             Viewer.backend = None
             Viewer._backend_obj = None
             Viewer._backend_proc = None
@@ -1164,8 +1157,7 @@ def close(self: Optional[Union["Viewer", Type["Viewer"]]] = None) -> None:
 
     @staticmethod
     @_with_lock
-    def connect_backend(backend: Optional[str] = None,
-                        close_at_exit: bool = True) -> None:
+    def connect_backend(backend: Optional[str] = None) -> None:
         """Get the running process of backend client.
 
         This method can be used to open a new process if necessary.
@@ -1174,8 +1166,6 @@ def connect_backend(backend: Optional[str] = None,
                         'panda3d', 'panda3d-qt', 'meshcat'.
                         Optional: The default is hardware and environment
                         dependent. See `viewer.default_backend` for details.
-        :param close_at_exit: Terminate backend server at Python exit.
-                              Optional: True by default
 
         :returns: Pointer to the running backend Client and its PID.
         """
@@ -1211,14 +1201,14 @@ def connect_backend(backend: Optional[str] = None,
                 if backend == 'panda3d-qt':
                     from .panda3d.panda3d_widget import Panda3dQWidget
                     client = Panda3dQWidget()
-                    proc = _ProcessWrapper(client, close_at_exit)
+                    proc = _ProcessWrapper(client)
                 elif backend == 'panda3d-sync':
                     client = Panda3dApp()
-                    proc = _ProcessWrapper(client, close_at_exit)
+                    proc = _ProcessWrapper(client)
                 else:
                     client = Panda3dViewer(window_type='onscreen',
                                            window_title=Viewer.window_name)
-                    proc = _ProcessWrapper(client._app, close_at_exit)
+                    proc = _ProcessWrapper(client._app)
             except RuntimeError as e:
                 raise RuntimeError(
                     "Something went wrong. Impossible to instantiate viewer "
@@ -1294,7 +1284,7 @@ def connect_backend(backend: Optional[str] = None,
                 server_proc = Process(conn.pid)
             else:
                 server_proc = client.server_proc
-            proc = _ProcessWrapper(server_proc, close_at_exit)
+            proc = _ProcessWrapper(server_proc)
 
         # Make sure the backend process is alive
         assert proc.is_alive(), (
@@ -1305,10 +1295,6 @@ def connect_backend(backend: Optional[str] = None,
         Viewer._backend_obj = client
         Viewer._backend_proc = proc
 
-        # Make sure to close cleanly the viewer at exit
-        if close_at_exit:
-            atexit.register(Viewer.close)
-
     @staticmethod
     @_with_lock
     @_must_be_open
diff --git a/python/jiminy_pywrap/include/jiminy/python/functors.h b/python/jiminy_pywrap/include/jiminy/python/functors.h
index f89393e8d..983cf0821 100644
--- a/python/jiminy_pywrap/include/jiminy/python/functors.h
+++ b/python/jiminy_pywrap/include/jiminy/python/functors.h
@@ -2,7 +2,7 @@
 #define FUNCTORS_WRAPPERS_PYTHON_H
 
 #include "jiminy/core/fwd.h"
-#include "jiminy/core/traits.h"
+#include "jiminy/core/telemetry/fwd.h"
 #include "jiminy/core/hardware/abstract_sensor.h"
 
 #include "pinocchio/bindings/python/fwd.hpp"
@@ -85,10 +85,10 @@ namespace jiminy::python
 
     public:
         FctPyWrapper(const bp::object & objPy) :
-        funcPyPtr_(objPy),
+        funcPyPtr_{objPy},
         outPtr_(createInternalBuffer<OutputArg>()),
         outData_(setDataInternalBuffer(outPtr_)),
-        outPyPtr_(nullptr)
+        outPyPtr_{nullptr}
         {
             outPyPtr_ = getNumpyReference(outData_);
         }
@@ -98,7 +98,7 @@ namespace jiminy::python
         funcPyPtr_(other.funcPyPtr_),
         outPtr_(createInternalBuffer<OutputArg>()),
         outData_(setDataInternalBuffer(outPtr_)),
-        outPyPtr_(nullptr)
+        outPyPtr_{nullptr}
         {
             *outPtr_ = *(other.outPtr_);
             outPyPtr_ = getNumpyReference(outData_);
@@ -107,9 +107,9 @@ namespace jiminy::python
         // Move constructor, takes a rvalue reference &&
         FctPyWrapper(FctPyWrapper && other) :
         funcPyPtr_(other.funcPyPtr_),
-        outPtr_(nullptr),
+        outPtr_{nullptr},
         outData_(other.outData_),
-        outPyPtr_(nullptr)
+        outPyPtr_{nullptr}
         {
             // Steal the resource from "other"
             outPtr_ = other.outPtr_;
@@ -177,7 +177,7 @@ namespace jiminy::python
     {
     public:
         FctInOutPyWrapper(const bp::object & objPy) :
-        funcPyPtr_(objPy)
+        funcPyPtr_{objPy}
         {
         }
         void operator()(const InputArgs &... argsIn, Eigen::VectorXd & argOut)
@@ -218,12 +218,12 @@ namespace jiminy::python
 
     public:
         HeightmapFunctorPyWrapper(const bp::object & objPy, heightmapType_t objType) :
-        heightmapType_(objType),
-        handlePyPtr_(objPy),
+        heightmapType_{objType},
+        handlePyPtr_{objPy},
         out1Ptr_(new double),
         out2Ptr_(new Eigen::Vector3d),
-        out1PyPtr_(),
-        out2PyPtr_()
+        out1PyPtr_{},
+        out2PyPtr_{}
         {
             if (heightmapType_ == heightmapType_t::CONSTANT)
             {
@@ -248,8 +248,8 @@ namespace jiminy::python
         handlePyPtr_(other.handlePyPtr_),
         out1Ptr_(new double),
         out2Ptr_(new Eigen::Vector3d),
-        out1PyPtr_(),
-        out2PyPtr_()
+        out1PyPtr_{},
+        out2PyPtr_{}
         {
             *out1Ptr_ = *(other.out1Ptr_);
             *out2Ptr_ = *(other.out2Ptr_);
@@ -261,10 +261,10 @@ namespace jiminy::python
         HeightmapFunctorPyWrapper(HeightmapFunctorPyWrapper && other) :
         heightmapType_(other.heightmapType_),
         handlePyPtr_(other.handlePyPtr_),
-        out1Ptr_(nullptr),
-        out2Ptr_(nullptr),
-        out1PyPtr_(nullptr),
-        out2PyPtr_(nullptr)
+        out1Ptr_{nullptr},
+        out2Ptr_{nullptr},
+        out1PyPtr_{nullptr},
+        out2PyPtr_{nullptr}
         {
             // Steal the resource from "other"
             out1Ptr_ = other.out1Ptr_;
diff --git a/python/jiminy_pywrap/include/jiminy/python/utilities.h b/python/jiminy_pywrap/include/jiminy/python/utilities.h
index 899ded7d3..0f740df92 100644
--- a/python/jiminy_pywrap/include/jiminy/python/utilities.h
+++ b/python/jiminy_pywrap/include/jiminy/python/utilities.h
@@ -6,12 +6,13 @@
 #include <functional>
 
 #include "jiminy/core/fwd.h"
-#include "jiminy/core/traits.h"
+#include "jiminy/core/telemetry/fwd.h"
 #include "jiminy/core/hardware/abstract_sensor.h"
 
 #include <boost/mpl/vector.hpp>
 
 #include "pinocchio/bindings/python/fwd.hpp"
+
 #include <boost/python/numpy.hpp>
 #include <boost/python/signature.hpp>
 #include <boost/python/object/function_doc_signature.hpp>
@@ -622,13 +623,17 @@ namespace jiminy::python
         }
     }
 
-    /// Convert most C++ objects into Python objects by value.
-
+    /// Convert most C++ objects into Python objects by value
     template<typename T>
-    std::enable_if_t<!is_vector_v<T> && !is_eigen_v<T> && !std::is_arithmetic_v<T> &&
-                         !std::is_integral_v<T>,
-                     bp::object>
-    convertToPython(const T & data, const bool & copy = true)
+    std::enable_if_t<
+        !is_vector_v<T> && !is_array_v<T> && !is_eigen_v<T> &&
+            !std::is_arithmetic_v<std::decay_t<T>> &&
+            !std::is_same_v<std::decay_t<T>, GenericConfig> &&
+            !std::is_same_v<std::decay_t<T>, std::pair<const std::string, SensorDataTypeMap>> &&
+            !std::is_same_v<std::decay_t<T>, std::string_view> &&
+            !std::is_same_v<std::decay_t<T>, FlexibleJointData>,
+        bp::object>
+    convertToPython(T && data, const bool & copy = true)
     {
         if (copy)
         {
@@ -639,8 +644,8 @@ namespace jiminy::python
     }
 
     template<typename T>
-    std::enable_if_t<std::is_arithmetic_v<T> || std::is_integral_v<T>, bp::object>
-    convertToPython(T & data, const bool & copy = true)
+    std::enable_if_t<std::is_arithmetic_v<std::decay_t<T>>, bp::object>
+    convertToPython(T && data, const bool & copy = true)
     {
         if (copy)
         {
@@ -650,7 +655,8 @@ namespace jiminy::python
     }
 
     template<typename T>
-    std::enable_if_t<is_eigen_v<T>, bp::object> convertToPython(T & data, const bool & copy = true)
+    std::enable_if_t<is_eigen_v<T>, bp::object> convertToPython(T && data,
+                                                                const bool & copy = true)
     {
         PyObject * vecPyPtr = getNumpyReference(data);
         if (copy)
@@ -663,107 +669,106 @@ namespace jiminy::python
     }
 
     template<typename T>
-    std::enable_if_t<is_vector_v<T>, bp::object> convertToPython(T & data,
-                                                                 const bool & copy = true)
+    std::enable_if_t<std::is_same_v<std::decay_t<T>, std::string_view>, bp::object>
+    convertToPython(T && data, const bool & copy = true)
     {
-        bp::list dataPy;
-        for (auto & val : data)
+        if (copy)
         {
-            dataPy.append(convertToPython(val, copy));
+            return bp::object(data);
         }
-        return std::move(dataPy);
+        return bp::object(bp::handle<>(PyUnicode_FromStringAndSize(data.data(), data.size())));
     }
 
     template<typename T>
-    std::enable_if_t<is_vector_v<T>, bp::object> convertToPython(const T & data,
-                                                                 const bool & copy = true)
-    {
-        bp::list dataPy;
-        for (const auto & val : data)
-        {
-            dataPy.append(convertToPython(val, copy));
-        }
-        return std::move(dataPy);
-    }
-
-    template<>
-    inline bp::object convertToPython(const std::string & data, const bool & copy)
+    std::enable_if_t<std::is_same_v<std::decay_t<T>, FlexibleJointData>, bp::object>
+    convertToPython(T && flexibleJointData, const bool & copy)
     {
-        if (copy)
+        if (!copy)
         {
-            return bp::object(data);
+            throw std::runtime_error(
+                "Passing 'FlexibleJointData' object to python by reference is not supported.");
         }
-        return bp::object(bp::handle<>(PyUnicode_FromStringAndSize(data.c_str(), data.size())));
-    }
-
-    template<>
-    inline bp::object convertToPython<FlexibleJointData>(
-        const FlexibleJointData & flexibleJointData, const bool & /* copy */)
-    {
         bp::dict flexibilityJointDataPy;
         flexibilityJointDataPy["frameName"] = flexibleJointData.frameName;
         flexibilityJointDataPy["stiffness"] = flexibleJointData.stiffness;
         flexibilityJointDataPy["damping"] = flexibleJointData.damping;
         flexibilityJointDataPy["inertia"] = flexibleJointData.inertia;
+        // FIXME: Remove explicit and redundant move when moving to C++20
         return std::move(flexibilityJointDataPy);
     }
 
-    template<>
-    inline bp::object convertToPython<std::pair<const std::string, SensorDataTypeMap>>(
-        const std::pair<const std::string, SensorDataTypeMap> & sensorDataTypeItem,
-        const bool & copy)
+    template<typename T>
+    std::enable_if_t<
+        std::is_same_v<std::decay_t<T>, std::pair<const std::string, SensorDataTypeMap>>,
+        bp::object>
+    convertToPython(T && sensorDataTypeItem, const bool & copy)
     {
-        const auto & [sensorGroupName, sensorDataType] = sensorDataTypeItem;
+        auto & [sensorGroupName, sensorDataType] = sensorDataTypeItem;
         return bp::make_tuple(sensorGroupName, convertToPython(sensorDataType.getAll(), copy));
     }
 
+    template<typename T>
+    std::enable_if_t<is_vector_v<T> || is_array_v<T>, bp::object>
+    convertToPython(T && data, const bool & copy = true)
+    {
+        bp::list dataPy;
+        for (auto & val : data)
+        {
+            dataPy.append(convertToPython(val, copy));
+        }
+        // FIXME: Remove explicit and redundant move when moving to C++20
+        return std::move(dataPy);
+    }
+
+    template<typename T>
+    std::enable_if_t<std::is_same_v<std::decay_t<T>, GenericConfig>, bp::object>
+    convertToPython(T && config, const bool & copy = true);
+
     class AppendBoostVariantToPython : public boost::static_visitor<bp::object>
     {
     public:
-        AppendBoostVariantToPython(const bool & copy) :
-        copy_(copy)
+        AppendBoostVariantToPython(bool copy) :
+        copy_{copy}
         {
         }
 
         template<typename T>
-        auto operator()(const T & value) const
+        bp::object operator()(T && value) const
         {
-            return convertToPython(value, copy_);
+            return convertToPython<T>(value, copy_);
         }
 
     public:
-        bool copy_;
+        const bool copy_;
     };
 
-    template<>
-    inline bp::object convertToPython<GenericConfig>(const GenericConfig & config,
-                                                     const bool & copy)
+    template<typename T>
+    std::enable_if_t<std::is_same_v<std::decay_t<T>, GenericConfig>, bp::object>
+    convertToPython(T && config, const bool & copy)
     {
         bp::dict configPyDict;
         AppendBoostVariantToPython visitor(copy);
-        for (const auto & [key, value] : config)
+        for (auto & [key, value] : config)
         {
             configPyDict[key] = boost::apply_visitor(visitor, value);
         }
+        // FIXME: Remove explicit and redundant move when moving to C++20
         return std::move(configPyDict);
     }
 
     template<typename T, bool copy = true>
     struct converterToPython
     {
-        static PyObject * convert(const T & data)
-        {
-            return bp::incref(convertToPython<T>(data, copy).ptr());
-        }
+        static PyObject * convert(T data) { return bp::incref(convertToPython(data, copy).ptr()); }
 
         static const PyTypeObject * get_pytype()
         {
-            if constexpr (is_vector_v<T>)
+            if constexpr (is_vector_v<T> || is_array_v<T>)
             {
                 return &PyList_Type;
             }
-            else if constexpr (std::is_same_v<T, GenericConfig> ||
-                               std::is_same_v<T, FlexibleJointData>)
+            else if constexpr (std::is_same_v<std::decay_t<T>, GenericConfig> ||
+                               std::is_same_v<std::decay_t<T>, FlexibleJointData>)
             {
                 return &PyDict_Type;
             }
@@ -781,16 +786,14 @@ namespace jiminy::python
         {
             struct type
             {
-                typedef typename std::remove_reference_t<T> value_type;
-
-                PyObject * operator()(T x) const
+                PyObject * operator()(T && x) const
                 {
-                    return bp::incref(convertToPython<value_type>(x, copy).ptr());
+                    return bp::incref(convertToPython(std::forward<T>(x), copy).ptr());
                 }
 
                 const PyTypeObject * get_pytype() const
                 {
-                    return converterToPython<value_type, copy>::get_pytype();
+                    return converterToPython<T, copy>::get_pytype();
                 }
             };
         };
@@ -835,7 +838,7 @@ namespace jiminy::python
     // Convert most Python objects in C++ objects by value.
 
     template<typename T>
-    std::enable_if_t<!is_vector_v<T> && !is_map_v<T> && !is_eigen_v<T> &&
+    std::enable_if_t<!is_vector_v<T> && !is_array_v<T> && !is_map_v<T> && !is_eigen_v<T> &&
                          !std::is_same_v<T, SensorsDataMap>,
                      T>
     convertFromPython(const bp::object & dataPy)
@@ -943,17 +946,40 @@ namespace jiminy::python
     template<typename T>
     std::enable_if_t<is_vector_v<T>, T> convertFromPython(const bp::object & dataPy)
     {
-        T vec;
         const bp::list listPy = bp::extract<bp::list>(dataPy);
+        T vec;
         vec.reserve(bp::len(listPy));
         for (bp::ssize_t i = 0; i < bp::len(listPy); ++i)
         {
-            const bp::object itemPy = listPy[i];
-            vec.push_back(std::move(convertFromPython<typename T::value_type>(itemPy)));
+            vec.push_back(convertFromPython<typename T::value_type>(listPy[i]));
         }
         return vec;
     }
 
+    namespace details
+    {
+        template<typename T, size_t... Is, typename F>
+        std::array<T, sizeof...(Is)> BuildArrayFromCallable(std::index_sequence<Is...>, F fun)
+        {
+            return {fun(Is)...};
+        }
+    }
+
+    template<typename T>
+    std::enable_if_t<is_array_v<T>, T> convertFromPython(const bp::object & dataPy)
+    {
+        constexpr std::size_t N = std::tuple_size_v<T>;
+        const bp::list listPy = bp::extract<bp::list>(dataPy);
+        if (bp::len(listPy) != N)
+        {
+            throw std::runtime_error("Consistent number of elements");
+        }
+        return details::BuildArrayFromCallable<typename T::value_type>(
+            std::make_index_sequence<N>{},
+            [&listPy](std::size_t i)
+            { return convertFromPython<typename T::value_type>(listPy[i]); });
+    }
+
     template<typename T>
     std::enable_if_t<std::is_same_v<T, SensorsDataMap>, T>
     convertFromPython(const bp::object & dataPy)
diff --git a/python/jiminy_pywrap/src/constraints.cc b/python/jiminy_pywrap/src/constraints.cc
index 0b330bd7a..8822d2396 100644
--- a/python/jiminy_pywrap/src/constraints.cc
+++ b/python/jiminy_pywrap/src/constraints.cc
@@ -160,7 +160,7 @@ namespace jiminy::python
                                               &JointConstraint::getJointName,
                                               bp::return_value_policy<bp::return_by_value>())
                 .ADD_PROPERTY_GET_WITH_POLICY("joint_idx",
-                                              &JointConstraint::getJointIdx,
+                                              &JointConstraint::getJointModelIdx,
                                               bp::return_value_policy<bp::return_by_value>())
                 .ADD_PROPERTY_GET_SET_WITH_POLICY("reference_configuration",
                                                   &JointConstraint::getReferenceConfiguration,
diff --git a/python/jiminy_pywrap/src/controllers.cc b/python/jiminy_pywrap/src/controllers.cc
index c9568c4d0..374e9cec0 100644
--- a/python/jiminy_pywrap/src/controllers.cc
+++ b/python/jiminy_pywrap/src/controllers.cc
@@ -1,3 +1,4 @@
+#include "jiminy/core/telemetry/fwd.h"
 #include "jiminy/core/hardware/abstract_sensor.h"
 #include "jiminy/core/control/abstract_controller.h"
 #include "jiminy/core/control/controller_functor.h"
@@ -98,7 +99,8 @@ namespace jiminy::python
                 .def("register_constants", &PyAbstractControllerVisitor::registerConstant,
                                            (bp::arg("self"), "fieldnames", "values"))
                 .def("remove_entries", &AbstractController::removeEntries)
-                .def("set_options", &PyAbstractControllerVisitor::setOptions)
+                .def("set_options", &PyAbstractControllerVisitor::setOptions,
+                                    (bp::arg("self"), "options"))
                 .def("get_options", &AbstractController::getOptions)
                 .ADD_PROPERTY_GET("robot", &PyAbstractControllerVisitor::getRobot)
                 .DEF_READONLY("sensors_data", &AbstractController::sensorsData_)
diff --git a/python/jiminy_pywrap/src/engine.cc b/python/jiminy_pywrap/src/engine.cc
index cdde1a32b..98bfa95ea 100644
--- a/python/jiminy_pywrap/src/engine.cc
+++ b/python/jiminy_pywrap/src/engine.cc
@@ -1,14 +1,12 @@
+#include "jiminy/core/utilities/helpers.h"
+#include "jiminy/core/utilities/json.h"
 #include "jiminy/core/io/serialization.h"
+#include "jiminy/core/io/abstract_io_device.h"
+#include "jiminy/core/io/memory_device.h"
 #include "jiminy/core/control/abstract_controller.h"
 #include "jiminy/core/robot/robot.h"
 #include "jiminy/core/engine/engine.h"
 #include "jiminy/core/engine/engine_multi_robot.h"
-#include "jiminy/core/telemetry/telemetry_data.h"
-#include "jiminy/core/telemetry/telemetry_recorder.h"
-#include "jiminy/core/utilities/json.h"
-#include "jiminy/core/utilities/helpers.h"
-
-#include <boost/optional.hpp>
 
 #include "pinocchio/bindings/python/fwd.hpp"
 #include <boost/python/raw_function.hpp>
diff --git a/python/jiminy_pywrap/src/functors.cc b/python/jiminy_pywrap/src/functors.cc
index 6e94c0615..4dfac928d 100644
--- a/python/jiminy_pywrap/src/functors.cc
+++ b/python/jiminy_pywrap/src/functors.cc
@@ -1,7 +1,5 @@
 #include "pinocchio/spatial/force.hpp"  // `Pinocchio::Force`
 
-#include "jiminy/core/traits.h"
-
 #include "jiminy/python/functors.h"
 
 
diff --git a/python/jiminy_pywrap/src/helpers.cc b/python/jiminy_pywrap/src/helpers.cc
index ecc2888c5..23152aa80 100644
--- a/python/jiminy_pywrap/src/helpers.cc
+++ b/python/jiminy_pywrap/src/helpers.cc
@@ -27,16 +27,16 @@ namespace jiminy::python
         return seed;
     }
 
-    JointModelType getJointTypeFromIdx(const pinocchio::Model & model, int32_t idIn)
+    JointModelType getJointTypeFromIdx(const pinocchio::Model & model, std::size_t jointIdx)
     {
         JointModelType jointType = JointModelType::UNSUPPORTED;
-        ::jiminy::getJointTypeFromIdx(model, idIn, jointType);
+        ::jiminy::getJointTypeFromIdx(model, jointIdx, jointType);
         return jointType;
     }
 
-    int32_t getJointPositionIdx(const pinocchio::Model & model, const std::string & name)
+    Eigen::Index getJointPositionIdx(const pinocchio::Model & model, const std::string & name)
     {
-        int32_t jointPositionFirstIdx = model.nq;
+        Eigen::Index jointPositionFirstIdx = model.nq;
         ::jiminy::getJointPositionIdx(model, name, jointPositionFirstIdx);
         return jointPositionFirstIdx;
     }
diff --git a/python/jiminy_pywrap/src/module.cc b/python/jiminy_pywrap/src/module.cc
index 970f2588d..340e84d22 100755
--- a/python/jiminy_pywrap/src/module.cc
+++ b/python/jiminy_pywrap/src/module.cc
@@ -5,7 +5,6 @@
 #include "pinocchio/spatial/force.hpp"  // `Pinocchio::Force`
 
 #include "jiminy/core/utilities/random.h"
-#include "jiminy/core/traits.h"
 
 /* Eigenpy must be imported first, since it sets pre-processor definitions used by Boost Python
    to configure Python C API. */
@@ -41,14 +40,14 @@ namespace jiminy::python
              bp::return_value_policy<bp::return_by_value>(),                                  \
              (bp::arg("self"), "t", "q", "v"));
 
-#define REGISTER_CONVERTER(Type, Copy)                                                  \
-    {                                                                                   \
-        bp::type_info info = bp::type_id<Type>();                                       \
-        const bp::converter::registration * reg = bp::converter::registry::query(info); \
-        if (reg == nullptr || *reg->m_to_python == nullptr)                             \
-        {                                                                               \
-            bp::to_python_converter<Type, converterToPython<Type, Copy>, true>();       \
-        }                                                                               \
+#define REGISTER_TO_PYTHON_BY_VALUE_CONVERTER(Type)                                       \
+    {                                                                                     \
+        bp::type_info info = bp::type_id<Type>();                                         \
+        const bp::converter::registration * reg = bp::converter::registry::query(info);   \
+        if (reg == nullptr || *reg->m_to_python == nullptr)                               \
+        {                                                                                 \
+            bp::to_python_converter<Type, converterToPython<const Type &, true>, true>(); \
+        }                                                                                 \
     }
 
     BOOST_PYTHON_MODULE(PYTHON_LIBRARY_NAME)
@@ -103,12 +102,13 @@ namespace jiminy::python
 
         /* Enable some automatic C++ to Python converters.
            By default, conversion is by-value unless specified explicitly via a call policy. */
-        REGISTER_CONVERTER(std::vector<std::vector<int32_t>>, true);
-        REGISTER_CONVERTER(std::vector<uint32_t>, true);
-        REGISTER_CONVERTER(std::vector<int32_t>, true);
-        REGISTER_CONVERTER(std::vector<Eigen::VectorXd>, true);
-        REGISTER_CONVERTER(std::vector<Eigen::MatrixXd>, true);
-        REGISTER_CONVERTER(GenericConfig, true);
+        REGISTER_TO_PYTHON_BY_VALUE_CONVERTER(std::vector<pinocchio::Index>);
+        REGISTER_TO_PYTHON_BY_VALUE_CONVERTER(std::vector<std::vector<pinocchio::Index>>);
+        REGISTER_TO_PYTHON_BY_VALUE_CONVERTER(std::vector<Eigen::Index>);
+        REGISTER_TO_PYTHON_BY_VALUE_CONVERTER(std::vector<std::vector<Eigen::Index>>);
+        REGISTER_TO_PYTHON_BY_VALUE_CONVERTER(std::vector<Eigen::VectorXd>);
+        REGISTER_TO_PYTHON_BY_VALUE_CONVERTER(std::vector<Eigen::MatrixXd>);
+        REGISTER_TO_PYTHON_BY_VALUE_CONVERTER(GenericConfig);
 
         // Expose functors
         TIME_STATE_FCT_EXPOSE(Bool, bool)
diff --git a/python/jiminy_pywrap/src/robot.cc b/python/jiminy_pywrap/src/robot.cc
index d5a0c11b0..eeb4789d8 100644
--- a/python/jiminy_pywrap/src/robot.cc
+++ b/python/jiminy_pywrap/src/robot.cc
@@ -1,3 +1,5 @@
+#include "jiminy/core/telemetry/fwd.h"
+#include "jiminy/core/hardware/fwd.h"
 #include "jiminy/core/hardware/abstract_sensor.h"
 #include "jiminy/core/hardware/abstract_motor.h"
 #include "jiminy/core/constraints/abstract_constraint.h"
diff --git a/python/jiminy_pywrap/src/sensors.cc b/python/jiminy_pywrap/src/sensors.cc
index 957d71bff..237c09e32 100644
--- a/python/jiminy_pywrap/src/sensors.cc
+++ b/python/jiminy_pywrap/src/sensors.cc
@@ -1,4 +1,4 @@
-#include "jiminy/core/traits.h"
+#include "jiminy/core/telemetry/fwd.h"
 #include "jiminy/core/hardware/abstract_sensor.h"
 #include "jiminy/core/hardware/basic_sensors.h"
 
@@ -363,7 +363,7 @@ namespace jiminy::python
                                               &ForceSensor::getFrameIdx,
                                               bp::return_value_policy<bp::return_by_value>())
                 .ADD_PROPERTY_GET_WITH_POLICY("joint_idx",
-                                              &ForceSensor::getJointIdx,
+                                              &ForceSensor::getJointModelIdx,
                                               bp::return_value_policy<bp::return_by_value>())
                 ;
             // clang-format on
@@ -381,7 +381,7 @@ namespace jiminy::python
                                               &EncoderSensor::getJointName,
                                               bp::return_value_policy<bp::return_by_value>())
                 .ADD_PROPERTY_GET_WITH_POLICY("joint_idx",
-                                              &EncoderSensor::getJointIdx,
+                                              &EncoderSensor::getJointModelIdx,
                                               bp::return_value_policy<bp::return_by_value>())
                 .ADD_PROPERTY_GET_WITH_POLICY("joint_type",
                                               &EncoderSensor::getJointType,
diff --git a/soup/hdf5/CMakeLists.txt b/soup/hdf5/CMakeLists.txt
index 3ceafa5d5..efbae747f 100644
--- a/soup/hdf5/CMakeLists.txt
+++ b/soup/hdf5/CMakeLists.txt
@@ -35,8 +35,10 @@ externalproject_add(zlib_external
           -DSKIP_INSTALL_LIBRARIES=ON  # Install manually to skip shared libs
           -DCMAKE_DEBUG_POSTFIX=d  # Force adding "d" suffix for config debug
           -DBUILD_SHARED_LIBS=OFF
-          -DCMAKE_POSITION_INDEPENDENT_CODE=ON
+          -DCMAKE_C_COMPILER:STRING=${CMAKE_C_COMPILER}
+          -DCMAKE_CXX_COMPILER:STRING=${CMAKE_CXX_COMPILER}
           -DCMAKE_MSVC_RUNTIME_LIBRARY:STRING=${CMAKE_MSVC_RUNTIME_LIBRARY}
+          -DCMAKE_POSITION_INDEPENDENT_CODE=ON
           -DCMAKE_TOOLCHAIN_FILE:PATH=${CMAKE_TOOLCHAIN_FILE}
           -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_INSTALL_PREFIX}
           -DCMAKE_C_FLAGS:STRING=${CMAKE_CXX_FLAGS_EXTERNAL}
@@ -125,11 +127,13 @@ externalproject_add(${PROJECT_NAME}
           -DBUILD_STATIC_LIBS=ON
           -DBUILD_SHARED_LIBS=OFF
           -DBUILD_STATIC_EXECS=OFF
-          -DCMAKE_POSITION_INDEPENDENT_CODE=ON
           -DZLIB_PACKAGE_NAME:STRING=NOT_FOUND  # Hack to avoid finding some zlib config files already installed and not skipping find_package(ZLIB)
           -DZLIB_ROOT:PATH=${CMAKE_INSTALL_PREFIX}
           -DZLIB_LIBRARY:PATH=${zlib_INSTALL_LIB_PATH}  # Using ZLIB_ROOT only is finding the shared lib instead of the static one
+          -DCMAKE_C_COMPILER:STRING=${CMAKE_C_COMPILER}
+          -DCMAKE_CXX_COMPILER:STRING=${CMAKE_CXX_COMPILER}
           -DCMAKE_MSVC_RUNTIME_LIBRARY:STRING=${CMAKE_MSVC_RUNTIME_LIBRARY}
+          -DCMAKE_POSITION_INDEPENDENT_CODE=ON
           -DCMAKE_TOOLCHAIN_FILE:PATH=${CMAKE_TOOLCHAIN_FILE}
           -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_INSTALL_PREFIX}
           -DCMAKE_C_FLAGS:STRING=${CMAKE_CXX_FLAGS_EXTERNAL}
diff --git a/soup/jsoncpp/CMakeLists.txt b/soup/jsoncpp/CMakeLists.txt
index 2fa6ade0d..d10d29edc 100644
--- a/soup/jsoncpp/CMakeLists.txt
+++ b/soup/jsoncpp/CMakeLists.txt
@@ -25,11 +25,15 @@ externalproject_add(${PROJECT_NAME}
      CMAKE_ARGS
           -DJSONCPP_WITH_TESTS=OFF
           -DJSONCPP_WITH_POST_BUILD_UNITTEST=OFF
+          -DJSONCPP_WITH_EXAMPLE=OFF
           -DJSONCPP_WITH_PKGCONFIG_SUPPORT=OFF
           -DJSONCPP_WITH_CMAKE_PACKAGE=ON
           -DBUILD_SHARED_LIBS=OFF
-          -DCMAKE_POSITION_INDEPENDENT_CODE=ON
+          -DBUILD_OBJECT_LIBS=OFF
+          -DCMAKE_C_COMPILER:STRING=${CMAKE_C_COMPILER}
+          -DCMAKE_CXX_COMPILER:STRING=${CMAKE_CXX_COMPILER}
           -DCMAKE_MSVC_RUNTIME_LIBRARY:STRING=${CMAKE_MSVC_RUNTIME_LIBRARY}
+          -DCMAKE_POSITION_INDEPENDENT_CODE=ON
           -DCMAKE_TOOLCHAIN_FILE:PATH=${CMAKE_TOOLCHAIN_FILE}
           -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_INSTALL_PREFIX}
           -DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS_EXTERNAL}


Test patch:
diff --git a/soup/gtest/CMakeLists.txt b/soup/gtest/CMakeLists.txt
index 68c8eab0a..d7b2dfd53 100644
--- a/soup/gtest/CMakeLists.txt
+++ b/soup/gtest/CMakeLists.txt
@@ -31,8 +31,10 @@ externalproject_add(${PROJECT_NAME}
      GIT_CONFIG        advice.detachedHead=false;${GIT_CREDENTIAL_EXTERNAL}
 
      CMAKE_ARGS
-          -DCMAKE_POSITION_INDEPENDENT_CODE=ON
+          -DCMAKE_C_COMPILER:STRING=${CMAKE_C_COMPILER}
+          -DCMAKE_CXX_COMPILER:STRING=${CMAKE_CXX_COMPILER}
           -DCMAKE_MSVC_RUNTIME_LIBRARY:STRING=${CMAKE_MSVC_RUNTIME_LIBRARY}
+          -DCMAKE_POSITION_INDEPENDENT_CODE=ON
           -DCMAKE_TOOLCHAIN_FILE:PATH=${CMAKE_TOOLCHAIN_FILE}
           -DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS_EXTERNAL}
           -DCMAKE_CXX_FLAGS_DEBUG:STRING=${CMAKE_CXX_FLAGS_DEBUG_EXTERNAL}