Skip to content

Commit

Permalink
👋🏽 Add example on how to import and use cib in a project. (#39)
Browse files Browse the repository at this point in the history
* clarifications to the build in the README
* create hello_world example by extracting it from the README
  • Loading branch information
lukevalenty authored Apr 15, 2022
1 parent ea1d603 commit 1018429
Show file tree
Hide file tree
Showing 15 changed files with 385 additions and 121 deletions.
1 change: 1 addition & 0 deletions .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,4 @@ jobs:
with:
name: cib.hpp
path: ${{github.workspace}}/build/include/cib/cib.hpp

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
/Testing/
/cmake-build*
/venv/
/examples/hello_world/build/
119 changes: 70 additions & 49 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,58 +1,79 @@
cmake_minimum_required(VERSION 3.16)
project(compile_time_init_build)


if (DEFINED ENV{CXX_STANDARD} AND NOT $ENV{CXX_STANDARD} EQUAL "")
set(CMAKE_CXX_STANDARD $ENV{CXX_STANDARD})
if(NOT DEFINED PROJECT_NAME)
set(NOT_SUBPROJECT ON)
else()
set(CMAKE_CXX_STANDARD 17)
set(NOT_SUBPROJECT OFF)
endif()
message("CMAKE_CXX_STANDARD = ${CMAKE_CXX_STANDARD}")

enable_testing()

add_subdirectory(lib/Catch2)
add_subdirectory(test)
add_subdirectory(benchmark)

file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/include/cib/)

add_custom_command(
DEPENDS
${CMAKE_SOURCE_DIR}/tools/gen_release_header.py
${CMAKE_SOURCE_DIR}/include/cib/*
${CMAKE_SOURCE_DIR}/include/cib/detail/*
COMMAND
python3 ${CMAKE_SOURCE_DIR}/tools/gen_release_header.py ${CMAKE_SOURCE_DIR}/include/cib/cib.hpp > ${CMAKE_BINARY_DIR}/include/cib/cib.hpp
OUTPUT
${CMAKE_BINARY_DIR}/include/cib/cib.hpp
)

add_custom_target(release_header
DEPENDS
${CMAKE_BINARY_DIR}/include/cib/cib.hpp)



add_library(Cib INTERFACE)
project(
cib
VERSION 0.1.0
LANGUAGES CXX
DESCRIPTION "A header-only C++ library for composing modular firmware at compile-time."
HOMEPAGE_URL "https://github.com/intel/compile-time-init-build")

add_library(cib INTERFACE)
target_compile_features(cib INTERFACE cxx_std_17)



if(NOT_SUBPROJECT)
if (DEFINED ENV{CXX_STANDARD} AND NOT $ENV{CXX_STANDARD} EQUAL "")
set(CMAKE_CXX_STANDARD $ENV{CXX_STANDARD})
else()
set(CMAKE_CXX_STANDARD 17)
endif()

find_package(Git QUIET)
if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git")
message("Updating git submodules...")
execute_process(
COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
RESULT_VARIABLE GIT_RETVAL)
if(NOT GIT_RETVAL EQUAL "0")
message(FATAL_ERROR "git submodule update failed!")
endif()
endif()

# Enable functional and performance test suites.
enable_testing()
add_subdirectory(lib/Catch2)
add_subdirectory(test)
add_subdirectory(benchmark)

# Build single-header release.
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/cib/)

find_package(PythonInterp 3 REQUIRED)

add_custom_command(
DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/tools/gen_release_header.py
${CMAKE_CURRENT_SOURCE_DIR}/include/cib/*
${CMAKE_CURRENT_SOURCE_DIR}/include/cib/detail/*
COMMAND
python3 ${CMAKE_CURRENT_SOURCE_DIR}/tools/gen_release_header.py ${CMAKE_CURRENT_SOURCE_DIR}/include/cib/cib.hpp > ${CMAKE_CURRENT_BINARY_DIR}/include/cib/cib.hpp
OUTPUT
${CMAKE_CURRENT_BINARY_DIR}/include/cib/cib.hpp
)

add_custom_target(release_header
DEPENDS
${CMAKE_CURRENT_BINARY_DIR}/include/cib/cib.hpp)
endif()

if ($ENV{SINGLE_HEADER})
message("Using single-header version of cib.hpp.")

add_dependencies(Cib release_header)

target_include_directories(Cib
INTERFACE
${CMAKE_BINARY_DIR}/include/)
add_dependencies(cib release_header)

target_include_directories(cib INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include/>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/>
)
else()
message("Using multi-header version of cib.hpp.")

target_include_directories(Cib
INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/include)

target_sources(Cib
INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/include/cib/cib.hpp)
endif()
target_include_directories(cib INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/>
)
endif()
94 changes: 24 additions & 70 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ in your repo and add the cib directory in your CMakeLists.txt file:

```cmake
add_subdirectory(lib/compile-time-init-build)
target_link_libraries(your_target PRIVATE Cib)
target_link_libraries(your_target PRIVATE cib)
```

With either of these methods, include the cib.hpp header in your code to use it.
Expand All @@ -40,101 +40,51 @@ With either of these methods, include the cib.hpp header in your code to use it.
Since *cib* is a library for efficiently building firmware through composition
a simple example takes a few more lines than a typical "Hello, world!"

#### core.hpp
The `core` component of this example **exports** the `say_message` **service**. Pay close
attention to the `#include` directives in each file.
```c++
#include <cib/cib.hpp>
#include <iostream>

struct say_message : public cib::callback_meta<>{};

// the 'core' component exposes the 'say_message' service for others to extend
struct core {
constexpr static auto config =
cib::config(cib::exports<say_message>);
constexpr static auto config = cib::exports<say_message>;
};
```
#### hello_world.hpp
The `hello_world` component **extends** the `say_message` **service** with new
contained in a lambda.
```c++
#include <iostream>
#include <cib/cib.hpp>

struct hello_world {
// the 'say_hello_world' component extends 'say_message' with its own functionality
struct say_hello_world {
constexpr static auto config =
cib::config(
cib::extend<say_message>([](){
std::cout << "Hello, world!" << std::endl;
})
);
cib::extend<say_message>([](){
std::cout << "Hello, world!" << std::endl;
});
};
```
#### lazy_dog.hpp
Another component, `lazy_dog` is also extending the `say_message` **service**.
This time it is using a function pointer instead of a lambda. The function
definition of `talk_about_the_dog` could also be placed in a `lazy_dog.cpp`
file if desired.
```c++
#include <iostream>
#include <cib/cib.hpp>

struct lazy_dog {
static void talk_about_the_dog() {
std::cout << "The quick brown fox jumps over the lazy dog." << std::endl;
}

constexpr static auto config =
cib::config(
cib::extend<say_message>(talk_about_the_dog)
);
};
```
#### my_project.hpp
All the components are brought together in the project configuration, `my_project`.
```c++
#include "core.hpp"
#include "hello_world.hpp"
#include "lazy_dog.hpp"
struct my_project {
// the 'hello_world' project composes 'core' and 'say_hello_world'
struct hello_world {
constexpr static auto config =
cib::components<core, hello_world, lazy_dog>;
cib::components<core, say_hello_world>;
};
```
#### main.cpp
The `cib::nexus` brings all the **services** and **features** together. This is
where the compile-time initialization and build process actually occurs.
```c++
#include "my_project.hpp"

cib::nexus<my_project> nexus{};
// the nexus instantiates the project
cib::nexus<hello_world> nexus{};

int main() {
// services can be accessed directly from the nexus...
// the fully extended and built services are ready to be used
nexus.service<say_message>();

// ...or they can be accessed anywhere through cib::service
nexus.init();
cib::service<say_message>();
return 0;
}
```
#### Execution
All of the initialization and registration occurs at compile-time, but the
new functionality is still executed at run-time:
```
shell> ./my_project
Hello, world!
The quick brown fox jumps over the lazy dog.
Hello, world!
The quick brown fox jumps over the lazy dog.
```
A larger and more illustrative example can be found in this repo at
[examples/hello_world](examples/hello_world).
### Building
*cib* is built with CMake. The single header is built with the
`release_header` target:
```shell
git clone https://github.com/intel/compile-time-init-build.git
cmake -B build
cmake --build build -t release_header
ls build/include/cib/ | grep cib.hpp
Expand All @@ -143,6 +93,10 @@ ls build/include/cib/ | grep cib.hpp
This combines all the *cib* header files in the `include` tree by recursively
including the `#include` directives and ignoring all other macros.

**NOTE:** *cib* uses git submodules to include its testing dependencies. The
CMake configuration *should* fetch the submodules for you, but only if the
repository was cloned as a git repo and not downloaded as an archive.

Unit tests are registered with CTest:

```shell
Expand Down
2 changes: 1 addition & 1 deletion benchmark/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@ target_compile_options(compilation_benchmark
-ftemplate-backtrace-limit=0
)

target_link_libraries(compilation_benchmark PRIVATE Cib)
target_link_libraries(compilation_benchmark PRIVATE cib)
19 changes: 19 additions & 0 deletions examples/hello_world/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
cmake_minimum_required(VERSION 3.16)

project(hello_world LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)

add_executable(hello_world main.cpp dont_panic.cpp)

include(FetchContent)
FetchContent_Declare(
cib
GIT_REPOSITORY https://github.com/intel/compile-time-init-build.git

# update this to a more recent commit ID for your project
GIT_TAG 8b35ed8f6fb358234a916768c81a924a373c80ff
)
FetchContent_MakeAvailable(cib)

target_link_libraries(hello_world cib)
Loading

0 comments on commit 1018429

Please sign in to comment.