diff --git a/CMakeLists.txt b/CMakeLists.txt
index 95b1272a426438513d2d6482dd7f66752d141b6a..d7da026cedd0be42059b94cf469521ed25071c55 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.18)
 project(fortran-support VERSION 0.1.0 LANGUAGES Fortran C)
 
 option(BUILD_SHARED_LIBS "Build shared libraries" ON)
-option(BUILD_TESTING "Generate build files for unit tests" ON)
+option(BUILD_TESTING "Build tests" ON)
 
 if(NOT CMAKE_BUILD_TYPE)
   set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING
@@ -12,10 +12,17 @@ if(NOT CMAKE_BUILD_TYPE)
 endif(NOT CMAKE_BUILD_TYPE)
 
 include(GNUInstallDirs)
-include(CTest)
 
 add_subdirectory(src)
 
+include(CTest)
+if(BUILD_TESTING)
+  add_subdirectory(test)
+else()
+  # Allow for 'make test' even if the tests are disabled:
+  enable_testing()
+endif()
+
 export(
   EXPORT "${PROJECT_NAME}-targets"
   FILE "${PROJECT_BINARY_DIR}/${PROJECT_NAME}-targets.cmake"
@@ -49,11 +56,6 @@ install(
     "${CMAKE_INSTALL_LIBDIR}/${PROJECT_NAME}/cmake"
 )
 
-if(BUILD_TESTING)
-  enable_testing()
-  add_subdirectory(test)
-endif()
-
 include("${PROJECT_SOURCE_DIR}/cmake/list_sources.cmake")
 
 # Collect source files of the project:
diff --git a/cmake/gtest_helper.cmake b/cmake/gtest_helper.cmake
index bcd4c731560128ff6731901eae89d4fc8cc18e45..f5a061d669923234af1fb20341a348776258e74b 100644
--- a/cmake/gtest_helper.cmake
+++ b/cmake/gtest_helper.cmake
@@ -1,26 +1,7 @@
 macro(add_icon_c_test test_name file_name)
     add_executable("CTest_${test_name}" ${file_name})
-    target_include_directories("CTest_${test_name}" SYSTEM AFTER PUBLIC "${PROJECT_SOURCE_DIR}/test/googletest/include" ${CMAKE_CURRENT_BINARY_DIR})
-    target_link_libraries("CTest_${test_name}" PRIVATE fortran-support gtest gtest_main)
+    target_link_libraries("CTest_${test_name}" PRIVATE fortran-support::fortran-support GTest::gtest_main)
     add_test(NAME "CTest_${test_name}" COMMAND "CTest_${test_name}")
-    set_property(TEST "CTest_${test_name}"
-        PROPERTY LABELS C)
-endmacro()
-
-macro(add_icon_fortran_test test_name file_name)
-    add_executable("FTest_${test_name}" ${file_name})
-    target_include_directories("FTest_${test_name}" SYSTEM AFTER PUBLIC "${PROJECT_SOURCE_DIR}/test/googletest/include")
-    target_link_libraries("FTest_${test_name}" PRIVATE FTest_C_Binding fortran-support gtest gtest_main)
-    add_test(NAME "FTest_${test_name}" COMMAND "FTest_${test_name}")
-    set_property(TEST "FTest_${test_name}"
-        PROPERTY LABELS Fortran)
-endmacro()
-
-macro(add_icon_fortran_c_test test_name file_name)
-    add_executable("FCTest_${test_name}" ${file_name})
-    target_include_directories("FCTest_${test_name}" SYSTEM AFTER PUBLIC "${PROJECT_SOURCE_DIR}/test/googletest/include")
-    target_link_libraries("FCTest_${test_name}" PRIVATE FTest_C_Binding fortran-support gtest gtest_main)
-    add_test(NAME "FCTest_${test_name}" COMMAND "FCTest_${test_name}")
-    set_property(TEST "FCTest_${test_name}"
-        PROPERTY LABELS Fortran/C)
+    set_property(TEST "CTest_${test_name}" PROPERTY LABELS C)
+    set_target_properties("CTest_${test_name}" PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED ON)
 endmacro()
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index d7db70a34a31b0e56ac4a2402869d15aab2a4e6b..bedf0a39f2dd76e09c5bc7ca79e363f3a1672a8d 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -31,16 +31,18 @@ add_library(fortran-support
   mo_util_timer.f90
   nml_annotate.c
   util_arithmetic_expr.c
+  util_arithmetic_expr.h
   util_backtrace.c
+  util_backtrace.h
   util_file.c
+  util_file.h
   util_hash.c
+  util_hash.h
   util_stride.c
   util_string_parse.c
   util_sysinfo.c
   util_system.c
   util_timer.c
-  util_arithmetic_expr.hpp
-  util_backtrace.h
   ${CMAKE_CURRENT_BINARY_DIR}/config.h
 )
 
@@ -60,12 +62,15 @@ set_target_properties(fortran-support
 
 target_include_directories(fortran-support
   PUBLIC
-    $<BUILD_INTERFACE:${Fortran_MODULE_DIRECTORY}>
-    $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+# Path to the Fortran modules:
+    $<BUILD_INTERFACE:$<$<COMPILE_LANGUAGE:Fortran>:${Fortran_MODULE_DIRECTORY}>>
+    $<INSTALL_INTERFACE:$<$<COMPILE_LANGUAGE:Fortran>:${CMAKE_INSTALL_INCLUDEDIR}>>
   INTERFACE
-    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+# Path to the internal C/C++ headers (for testing):
+    $<BUILD_INTERFACE:$<$<COMPILE_LANGUAGE:C,CXX>:${CMAKE_CURRENT_SOURCE_DIR}>>
   PRIVATE
-    $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
+# Path to config.h (for C and C++ only):
+    $<BUILD_INTERFACE:$<$<COMPILE_LANGUAGE:C,CXX>:${CMAKE_CURRENT_BINARY_DIR}>>
 )
 
 install(
diff --git a/src/util_arithmetic_expr.c b/src/util_arithmetic_expr.c
index 01cf6b5752418ee60a921fe4b779ad61e1fae067..411944b213ea80949372e7d30b97906dd81468e0 100644
--- a/src/util_arithmetic_expr.c
+++ b/src/util_arithmetic_expr.c
@@ -13,7 +13,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <math.h>
-#include "util_arithmetic_expr.hpp"
+#include "util_arithmetic_expr.h"
 
 const char*  const priorities = "(;><;+-;/*;^";
 const char*  const left_assoc = "><+-/*^";
diff --git a/src/util_arithmetic_expr.hpp b/src/util_arithmetic_expr.h
similarity index 71%
rename from src/util_arithmetic_expr.hpp
rename to src/util_arithmetic_expr.h
index 8aa75f7f0fc0249d1c8a55f681ff435bef756562..b3f3f5df9c976827c7682e8e0f240229ab7e8cf0 100644
--- a/src/util_arithmetic_expr.hpp
+++ b/src/util_arithmetic_expr.h
@@ -1,5 +1,9 @@
-#ifndef UTIL_ARITHMETIC_EXPR_HPP
-#define UTIL_ARITHMETIC_EXPR_HPP
+#ifndef UTIL_ARITHMETIC_EXPR_H
+#define UTIL_ARITHMETIC_EXPR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
 
 #define MAX_BUF_LEN 1024
 #define NUM_FCT 9
@@ -42,4 +46,14 @@ int do_parse_infix(const char *in_parse_line, struct t_list *data);
 /* accessor function for function name strings */
 int get_fctname(const int id, char *string);
 
-#endif /* UTIL_ARITHMETIC_EXPR_HPP */
+/* returns the priority of an arithmetic operator */
+int priority(char op);
+
+/* returns the associativity of an arithmetic operator */
+int left_associative(char op);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // UTIL_ARITHMETIC_EXPR_H
diff --git a/src/util_arithmetic_expr.rl b/src/util_arithmetic_expr.rl
index 7bbfe6a3130cc9e33879cf4db165793046650118..d2c8db6a3ce962788f6b0044b74188a5f7e8a418 100644
--- a/src/util_arithmetic_expr.rl
+++ b/src/util_arithmetic_expr.rl
@@ -11,7 +11,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <math.h>
-#include "util_arithmetic_expr.hpp"
+#include "util_arithmetic_expr.h"
 
 const char*  const priorities = "(;><;+-;/*;^";
 const char*  const left_assoc = "><+-/*^";
diff --git a/src/util_file.h b/src/util_file.h
new file mode 100644
index 0000000000000000000000000000000000000000..669a60330787b5073c7af2e226cad286702a8229
--- /dev/null
+++ b/src/util_file.h
@@ -0,0 +1,16 @@
+#ifndef UTIL_FILE_H
+#define UTIL_FILE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int util_islink(char *path);
+long int util_filesize(char *filename);
+int util_file_is_writable(char *filename);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // UTIL_FILE_H
diff --git a/src/util_hash.c b/src/util_hash.c
index 503723ee83269797041612fd15dc3d6da2d29247..3105a8a4d3e0d97081b79f1f59818172da30cc73 100644
--- a/src/util_hash.c
+++ b/src/util_hash.c
@@ -88,6 +88,8 @@
 
 #endif
 
+#include "util_hash.h"
+
 #define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))
 
 /*
diff --git a/src/util_hash.h b/src/util_hash.h
new file mode 100644
index 0000000000000000000000000000000000000000..f99f18bc7c3a03d49f6b29bb6bb5066dc0819edb
--- /dev/null
+++ b/src/util_hash.h
@@ -0,0 +1,16 @@
+#ifndef UTIL_HASH_H
+#define UTIL_HASH_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+uint32_t util_hashword(const void *key, size_t length, uint32_t initval);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // UTIL_HASH_H
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index b66ae958db5daa4fd502e4ac3de4832ad49437ee..6eadca6d5084a5409b41d22361a5c3f0e96bf31e 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -1,20 +1,2 @@
-include(FetchContent)
-enable_language(CXX)
-
-find_package(GTest 1.13)
-if(NOT GTest_FOUND)
-  message(CHECK_START "Fetching external GTest")
-  if ("${CMAKE_VERSION}" VERSION_GREATER_EQUAL "3.24")
-    cmake_policy(SET CMP0135 NEW)
-  endif()
-  FetchContent_Declare(googletest
-    URL https://github.com/google/googletest/archive/refs/tags/v1.13.0.tar.gz
-    URL_HASH MD5=95b29f0038ec84a611df951d74d99897
-  )
-  set(INSTALL_GTEST OFF CACHE BOOL "Do not install GTest")
-  FetchContent_MakeAvailable(googletest)
-  message(CHECK_PASS "done")
-endif()
-
 add_subdirectory(fortran)
-add_subdirectory(googletest)
+add_subdirectory(c)
diff --git a/test/c/CMakeLists.txt b/test/c/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..953f455c2f61eaa7df56038f062cad5fecb1fdf0
--- /dev/null
+++ b/test/c/CMakeLists.txt
@@ -0,0 +1,39 @@
+enable_language(CXX)
+
+find_package(GTest 1.13)
+if(NOT GTest_FOUND)
+  message(CHECK_START "Fetching external GTest")
+  if ("${CMAKE_VERSION}" VERSION_GREATER_EQUAL "3.24")
+    cmake_policy(SET CMP0135 NEW)
+  endif()
+  include(FetchContent)
+  FetchContent_Declare(googletest
+    URL https://github.com/google/googletest/archive/refs/tags/v1.13.0.tar.gz
+    URL_HASH MD5=95b29f0038ec84a611df951d74d99897
+  )
+  set(INSTALL_GTEST OFF CACHE BOOL "Disable installation of googletest")
+  set(BUILD_GMOCK OFF CACHE BOOL "Do not build the googlemock subproject")
+  FetchContent_MakeAvailable(googletest)
+  # GTest puts its build artifacts to ${CMAKE_BINARY_DIR} instead of
+  # ${CMAKE_CURRENT_BINARY_DIR}. Therefore, we have to make it behave:
+  set_target_properties(gtest_main gtest
+    PROPERTIES
+      RUNTIME_OUTPUT_DIRECTORY "${googletest_BINARY_DIR}/bin"
+      LIBRARY_OUTPUT_DIRECTORY "${googletest_BINARY_DIR}/lib"
+      ARCHIVE_OUTPUT_DIRECTORY "${googletest_BINARY_DIR}/lib"
+      PDB_OUTPUT_DIRECTORY "${googletest_BINARY_DIR}/bin"
+      COMPILE_PDB_OUTPUT_DIRECTORY "${googletest_BINARY_DIR}/lib"
+  )
+  message(CHECK_PASS "done")
+endif()
+
+include("${PROJECT_SOURCE_DIR}/cmake/gtest_helper.cmake")
+
+add_icon_c_test(UtilArithmeticExprTest ctest_util_arithmetic_expr.cpp)
+add_icon_c_test(UtilHashTest ctest_util_hash.cpp)
+
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/util_file_test.txt"
+     "This is a test file for unit tests for util_file.c\n")
+file(CREATE_LINK "${CMAKE_CURRENT_SOURCE_DIR}/util_file_test.txt"
+     "${CMAKE_CURRENT_BINARY_DIR}/util_file_link.txt" SYMBOLIC)
+add_icon_c_test(UtilFileTest ctest_util_file.cpp)
diff --git a/test/googletest/c_test/ctest_util_arithmetic_expr.cpp b/test/c/ctest_util_arithmetic_expr.cpp
similarity index 97%
rename from test/googletest/c_test/ctest_util_arithmetic_expr.cpp
rename to test/c/ctest_util_arithmetic_expr.cpp
index 922104d94d0aaf5a61a2ab1b291e9700202278f5..401c815256f0b97c8ef773bda7ac092cc95c5e0e 100644
--- a/test/googletest/c_test/ctest_util_arithmetic_expr.cpp
+++ b/test/c/ctest_util_arithmetic_expr.cpp
@@ -1,7 +1,6 @@
 #include <gtest/gtest.h>
 
-#include <external_function.hpp>
-#include <util_arithmetic_expr.hpp>
+#include <util_arithmetic_expr.h>
 
 class UtilArithmeticExprTest : public ::testing::Test {
   protected:
diff --git a/test/googletest/c_test/ctest_util_file.cpp b/test/c/ctest_util_file.cpp
similarity index 52%
rename from test/googletest/c_test/ctest_util_file.cpp
rename to test/c/ctest_util_file.cpp
index 65ad9e2bd209f80ae52dc678f9e58097986104bf..6cef0e3a6c8bcc4c896b21519c2a8bd6c850e4ac 100644
--- a/test/googletest/c_test/ctest_util_file.cpp
+++ b/test/c/ctest_util_file.cpp
@@ -1,18 +1,18 @@
 #include <gtest/gtest.h>
 
 #include <string>
-#include <path_config.h>
-#include <external_function.hpp>
 
+#include <util_file.h>
+
+static std::string working_dir = ".";
 
 class UtilFileTest : public ::testing::Test {};
 
 
 TEST_F(UtilFileTest, FileIsLink) {
-    std::string path = current_path;
-    std::string file_notlink = path + "/test.txt";
-    std::string file_islink = path + "/link.txt";
-    std::string file_error = "./something.txt";
+    std::string file_notlink = working_dir + "/util_file_test.txt";
+    std::string file_islink = working_dir + "/util_file_link.txt";
+    std::string file_error = working_dir + "/util_file_noexist.txt";
     char *file_cstr;
 
     file_cstr = &file_notlink[0];
@@ -25,8 +25,7 @@ TEST_F(UtilFileTest, FileIsLink) {
 
 
 TEST_F(UtilFileTest, CanGetFileSize) {
-    std::string path = current_path;
-    std::string file = path + "/test.txt";
+    std::string file = working_dir + "/util_file_test.txt";
 
     char *file_cstr;
     file_cstr = &file[0];
@@ -36,11 +35,22 @@ TEST_F(UtilFileTest, CanGetFileSize) {
 
 
 TEST_F(UtilFileTest, CheckFileWritable) {
-    std::string path = current_path;
-    std::string file = path + "/test.txt";
+    std::string file = working_dir + "/util_file_test.txt";
 
     char *file_cstr;
     file_cstr = &file[0];
 
     EXPECT_EQ(util_file_is_writable(file_cstr), 1);
 }
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+
+  if (argc > 1) {
+    working_dir = argv[1];
+  }
+
+  std::cout << "Working directory: " << working_dir << std::endl;
+
+  return RUN_ALL_TESTS();
+}
diff --git a/test/googletest/c_test/ctest_util_hash.cpp b/test/c/ctest_util_hash.cpp
similarity index 94%
rename from test/googletest/c_test/ctest_util_hash.cpp
rename to test/c/ctest_util_hash.cpp
index 0d8cc05dd775962bcfccc9e0eeb8c691626cbcf6..eb128cc9b305752b92c19f897e6d4cecb7ba8ead 100644
--- a/test/googletest/c_test/ctest_util_hash.cpp
+++ b/test/c/ctest_util_hash.cpp
@@ -3,7 +3,7 @@
 #include <stdexcept>
 #include <string>
 
-#include <external_function.hpp>
+#include <util_hash.h>
 
 class UtilHashCTest : public ::testing::Test {};
 
diff --git a/test/fortran/CMakeLists.txt b/test/fortran/CMakeLists.txt
index 9ea990094ebb60e4a3d888e5cc3426999d8d5b1b..ce45562505274eeb79269f1e22685a5d89ca881f 100644
--- a/test/fortran/CMakeLists.txt
+++ b/test/fortran/CMakeLists.txt
@@ -7,19 +7,24 @@ FetchContent_Declare(fortutf
   URL https://github.com/artemis-beta/FortUTF/archive/10ea512e4b21fe9157ed838b037210874fbe92eb.tar.gz
   URL_HASH MD5=f1a5f9733553b32f2c6297c45b7c3f13
 )
-FetchContent_MakeAvailable(fortutf)
+# We do not need libFortUTF and want to avoid the redundant building of the
+# library. Therefore, we do not call FetchContent_MakeAvailable(fortutf), which
+# adds ${fortutf_SOURCE_DIR} as a subdirectory:
+FetchContent_GetProperties(fortutf)
+if(NOT fortutf_POPULATED)
+  FetchContent_Populate(fortutf)
+endif()
 message(CHECK_PASS "done")
 
-SET(FORTUTF_PROJECT_TEST_DIR ${CMAKE_CURRENT_SOURCE_DIR})
-SET(FORTUTF_PROJECT_SRC_LIBRARY ${PROJECT_NAME}::fortran-support)
-SET(FORTUTF_PROJECT_SRC_FILES helpers.f90)
+set(FORTUTF_PROJECT_TEST_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
+set(FORTUTF_PROJECT_SRC_LIBRARY fortran-support::fortran-support)
+set(FORTUTF_PROJECT_SRC_FILES helpers.f90)
 
-INCLUDE(${fortutf_SOURCE_DIR}/cmake/fortutf.cmake)
+include("${fortutf_SOURCE_DIR}/cmake/fortutf.cmake")
 
 # We have to prevent FortUTF from generating invalid Fortran code:
-SET(project_name ${PROJECT_NAME})
+set(project_name ${PROJECT_NAME})
 string(REPLACE "-" "_" PROJECT_NAME ${PROJECT_NAME})
 FortUTF_Find_Tests()
-SET(PROJECT_NAME ${project_name})
-add_test(NAME FortUTF_UnitTest
-    COMMAND fortran_support_Tests)
+set(PROJECT_NAME ${project_name})
+add_test(NAME FortUTF_UnitTest COMMAND fortran_support_Tests)
diff --git a/test/googletest/CMakeLists.txt b/test/googletest/CMakeLists.txt
deleted file mode 100644
index c9e00d0dd05f9ebdacc6df495401105ccc324a24..0000000000000000000000000000000000000000
--- a/test/googletest/CMakeLists.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-include(${PROJECT_SOURCE_DIR}/cmake/gtest_helper.cmake)
-
-add_subdirectory(c_test)
diff --git a/test/googletest/c_test/CMakeLists.txt b/test/googletest/c_test/CMakeLists.txt
deleted file mode 100644
index 598c2ee51a29214ab4047013e6f127212d10365e..0000000000000000000000000000000000000000
--- a/test/googletest/c_test/CMakeLists.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-add_icon_c_test(UtilArithmeticExprTest ctest_util_arithmetic_expr.cpp)
-add_icon_c_test(UtilHashTest ctest_util_hash.cpp)
-
-set(CurrentPath ${CMAKE_CURRENT_BINARY_DIR})
-configure_file("${CMAKE_CURRENT_SOURCE_DIR}/path_config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/path_config.h")
-file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/test.txt" DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
-file(CREATE_LINK "${CMAKE_CURRENT_SOURCE_DIR}/test.txt" "${CMAKE_CURRENT_BINARY_DIR}/link.txt" SYMBOLIC)
-add_icon_c_test(UtilFileTest ctest_util_file.cpp)
diff --git a/test/googletest/c_test/path_config.h.in b/test/googletest/c_test/path_config.h.in
deleted file mode 100644
index a3b62bbfe60c5dea584be4d8f7397f095dd11154..0000000000000000000000000000000000000000
--- a/test/googletest/c_test/path_config.h.in
+++ /dev/null
@@ -1 +0,0 @@
-#define current_path "@CurrentPath@"
diff --git a/test/googletest/c_test/test.txt b/test/googletest/c_test/test.txt
deleted file mode 100644
index 4b34f50d85831db515e9912e1908bf9aa5a6db55..0000000000000000000000000000000000000000
--- a/test/googletest/c_test/test.txt
+++ /dev/null
@@ -1 +0,0 @@
-This is a test file for unit tests for util_file.c
diff --git a/test/googletest/include/external_function.hpp b/test/googletest/include/external_function.hpp
deleted file mode 100644
index 85c4687b2d6249d40dfbe2697a8e8bd970f9ab4c..0000000000000000000000000000000000000000
--- a/test/googletest/include/external_function.hpp
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef EXTERNAL_FUNCTION_HPP_
-#define EXTERNAL_FUNCTION_HPP_
-
-
-extern "C" {
-// util_arithmetic_expr.c
-int priority(char op);
-int left_associative(char op);
-int do_parse_infix(const char *in_parse_line, struct t_list *queue);
-int get_fctname(const int id, char *string);
-
-// util_file.c
-int util_islink(const char *path);
-long int util_filesize(char *filename);
-int util_file_is_writable(char *filename);
-
-// util_hash.c
-uint32_t util_hashword(const void *, size_t, uint32_t);
-}
-
-
-#endif  // EXTERNAL_FUNCTION_HPP_
\ No newline at end of file