Since cib is a library for efficiently building firmware through composition a simple example takes a few more lines than a typical "Hello, world!"
This example shows how a core service, say_message
, can be extended by
multiple independent components using cib.
The core
component of this example exports the say_message
service. Pay close
attention to the #include
directives in each file.
#include <cib/cib.hpp>
struct say_message : public cib::callback_meta<>{};
struct core {
constexpr static auto config =
cib::config(cib::exports<say_message>);
};
The hello_world
component extends the say_message
service with new
contained in a lambda.
#include <iostream>
#include <cib/cib.hpp>
struct say_hello_world {
constexpr static auto config =
cib::config(
cib::extend<say_message>([](){
std::cout << "Hello, world!" << std::endl;
})
);
};
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.
Note the use of the &
operator to take the function pointer of
talk_about_the_dog
.
#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)
);
};
The dont_panic
component illustrates how functions defined in object files
may still be used to extend services.
#include <iostream>
#include <cib/cib.hpp>
// functions declared outside the component may be used within the component
void so_long();
struct dont_panic {
// functions can be declared in a header and defined in a object file
static void say_it();
// any number of extensions can be made
constexpr static auto config =
cib::config(
cib::extend<say_message>(&say_it),
cib::extend<say_message>(&so_long)
);
};
All the components are brought together in the project configuration, hello_world
.
#include "core.hpp"
#include "say_hello_world.hpp"
#include "lazy_dog.hpp"
struct hello_world {
constexpr static auto config =
cib::components<core, say_hello_world, lazy_dog>;
};
The cib::nexus
brings all the services and features together. This is
where the compile-time initialization and build process actually occurs.
#include "hello_world.hpp"
cib::nexus<hello_world> nexus{};
int main() {
// services can be accessed directly from the nexus...
nexus.service<say_message>();
// ...or they can be accessed anywhere through cib::service
nexus.init();
cib::service<say_message>();
return 0;
}
Use cmake to build the hello_world example:
cmake -B build
cmake --build build
All of the initialization and registration occurs at compile-time, but the new functionality is still executed at run-time:
shell> ./build/hello_world
So long and thanks for all the fish.
Don't Panic.
The quick brown fox jumps over the lazy dog.
Hello, world!
So long and thanks for all the fish.
Don't Panic.
The quick brown fox jumps over the lazy dog.
Hello, world!
For more details on how to use cib, see the User Guide.