Step 7: Adding System Introspection — CMake 4.1.4 Documentation

# Step 7: Adding System Introspection

Let us consider adding some code to our project that depends on features the target platform may not have. For this example, we will add some code that depends on whether or not the target platform has the `log` and `exp` functions. Of course almost every platform has these functions but for this tutorial assume that they are not common.

## Exercise 1 - Assessing Dependency Availability

### Goal

Change implementation based on available system dependencies.

### Helpful Resources

- `CheckCXXSourceCompiles`

- `target_compile_definitions()`

### Files to Edit

- `MathFunctions/CMakeLists.txt`

- `MathFunctions/mysqrt.cxx`

### Getting Started

The starting source code is provided in the `Step7` directory. In this exercise, complete `TODO 1` through `TODO 5`.

Start by editing `MathFunctions/CMakeLists.txt`. Include the `CheckCXXSourceCompiles` module. Then, use `check_cxx_source_compiles()` to determine whether `log` and `exp` are available from `cmath`. If they are available, use `target_compile_definitions()` to specify `HAVE_LOG` and `HAVE_EXP` as compile definitions.

In the `MathFunctions/mysqrt.cxx`, include `cmath`. Then, if the system has `log` and `exp`, use them to compute the square root.

### Build and Run

Make a new directory called `Step7_build`. Run the `cmake` executable or the `cmake-gui` to configure the project and then build it with your chosen build tool and run the `Tutorial` executable.

This can look like the following:

    mkdir Step7_build
    cd Step7_build
    cmake ../Step7
    cmake --build .

Which function gives better results now, `sqrt` or `mysqrt`?

### Solution

In this exercise we will use functions from the `CheckCXXSourceCompiles` module so first we must include it in `MathFunctions/CMakeLists.txt`.

TODO 1: Click to show/hide answer
TODO 1: MathFunctions/CMakeLists.txt

      include(CheckCXXSourceCompiles)

Then test for the availability of `log` and `exp` using `check_cxx_compiles_source`. This function lets us try compiling simple code with the required dependency prior to the true source code compilation. The resulting variables `HAVE_LOG` and `HAVE_EXP` represent whether those dependencies are available.

TODO 2: Click to show/hide answer
TODO 2: MathFunctions/CMakeLists.txt

      check_cxx_source_compiles("
        #include <cmath>
        int main() {
          std::log(1.0);
          return 0;
        }
      " HAVE_LOG)
      check_cxx_source_compiles("
        #include <cmath>
        int main() {
          std::exp(1.0);
          return 0;
        }
      " HAVE_EXP)

Next, we need to pass these CMake variables to our source code. This way, our source code can tell what resources are available. If both `log` and `exp` are available, use `target_compile_definitions()` to specify `HAVE_LOG` and `HAVE_EXP` as `PRIVATE` compile definitions.

TODO 3: Click to show/hide answer
TODO 3: MathFunctions/CMakeLists.txt

      if(HAVE_LOG AND HAVE_EXP)
        target_compile_definitions(SqrtLibrary
                                   PRIVATE "HAVE_LOG" "HAVE_EXP"
                                   )
      endif()

      target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
    endif()

Since we may be using `log` and `exp`, we need to modify `mysqrt.cxx` to include `cmath`.

TODO 4: Click to show/hide answer
TODO 4: MathFunctions/mysqrt.cxx

    #include <cmath>

If `log` and `exp` are available on the system, then use them to compute the square root in the `mysqrt` function. The `mysqrt` function in `MathFunctions/mysqrt.cxx` will look as follows:

TODO 5: Click to show/hide answer
TODO 5: MathFunctions/mysqrt.cxx

    #if defined(HAVE_LOG) && defined(HAVE_EXP)
      double result = std::exp(std::log(x) * 0.5);
      std::cout << "Computing sqrt of " << x << " to be " << result
                << " using log and exp" << std::endl;
    #else
      double result = x;

      // do ten iterations
      for (int i = 0; i < 10; ++i) {
        if (result <= 0) {
          result = 0.1;
        }
        double delta = x - (result * result);
        result = result + 0.5 * delta / result;
        std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
      }
    #endif