You should be able to see the pattern here. INCLUDE_DIRECTORIES, INTERFACE_INCLUDE_DIRECTORIESĬOMPILE_OPTIONS, INTERFACE_COMPILE_OPTIONSĬOMPILE_DEFINITIONS, INTERFACE_COMPILE_DEFINITIONS The table below shows exactly which commands are used to configure which aspects of a module, and do so by modifying which properties. This of course is implemented in terms of properties. CMake allows us to directly specify the remaining mentioned aspects of a well-defined target – a module, using dedicated commands. Let’s now look at the remaining aspects of defining a target as a module. We already know how to handle linking the library with CMake. I would argue that this is the correct level of abstraction to handle these details – it should not be done in source code, in form of relative #include‘s, or conditional #define‘s, but rather, in the build system. All of these things can and should be, handled by the build system. What is the interface of a library, or more precisely, a module? From the point of view of structuring and building a project, it’s the include paths, compiler flags required to build the code, and potential defines that control how the library works. That way it wouldn’t matter where the add directory is located in relation to the maths directory – it wouldn’t even need to be part of the same project, and could be provided by a package manager instead, as long as it exposed CMake targets that could be consumed by the Maths library. Instead, we’d like to have a clearly defined interface that could be relied on, without depending on details like relative directory layout. We are no longer free to change anything about the Add library without affecting the its consumers. Relying on the relative directory structure is bad for the same reasons relying on implementation details is. Let’s fix this issue by modularising our project. This can be seen as equivalent to breaking encapsulation or relying on implementation details in object-oriented code. We’re relying on relative directory paths instead of on our build system to handle the dependencies. Now it should be obvious that this is a sign of an issue. This didn’t seem so bad when dealing just with the Add library, but here, to include the Add and Subtract libraries from within the maths directory, we would need to go up a directory, like so: #include "./add/add.h" So far we’ve been relying on relative paths to include appropriate headers. The directory tree currently looks as follows. As we begin writing the Maths library, we face an issue. Thus we create a Maths library, again with a dedicated maths directory. The code isn’t up to our standards so we’d like to wrap our Add and Subtract libraries to align the interfaces and ensure consistent error-handling across the entire library. We remember that we have some old C code that handles subtracting somewhere, and decide to reuse it – we will create a subtract directory and place the following files within it. Let’s say that the Add library does not quite meet our needs any longer, and we’d like to build it up to a fully featured mathematical library. We will start by extending our project to facilitate the discussion. This will further grow our example project, preparing us for future discussions. While doing so I also return to the closely related concept of modular design, briefly mentioned in the previous post. I will show examples of using PRIVATE, INTERFACE and PUBLIC specifiers well. In this part instead of introducing yet more new features, I illustrate some practical applications of those already discussed. In the previous two parts of the series, I introduced quite a few new concepts.
0 Comments
Leave a Reply. |
AuthorWrite something about yourself. No need to be fancy, just an overview. ArchivesCategories |