The Bareflank Support Library (BSL) is a Rust 2018 and C++20, "constexpr everything", AUTOSAR compliant header-only library intended to support the development of critical systems applications. Although the BSL does not adhere to the Rust and C++ Standard Library specifications, it attempts to where possible, to ensure most of the APIs are as familiar as possible. Since a number of critical systems applications do not support dynamic memory or exceptions, the BSL uses neither, but is capable of supporting both if desired. Since AUTOSAR only support C++, the Rust code was written with the same AUTOSAR rules where applicable.
With respect to testing, the BSL provides full unit testing with 100% code coverage. Futhermore, the C++ portion of the BSL is written entirely as a "constexpr", meaning APIs are unit tested both at compile-time and run-time. This allows us to ensure that the compiler's rules for constexpr and undefined behavior are leveraged to prove the C++ code does not invoke UB at runtime.
Get the latest version of the BSL from GitHub:
git clone https://github.com/bareflank/bsl
mkdir bsl/build && cd bsl/build
cmake -GNinja -DCMAKE_CXX_COMPILER="clang++" ..
ninja
Enjoy:
#include <bsl/arguments.hpp>
#include <bsl/array.hpp>
#include <bsl/convert.hpp>
#include <bsl/cstr_type.hpp>
#include <bsl/debug.hpp>
#include <bsl/exit_code.hpp>
#include <bsl/safe_idx.hpp>
#include <bsl/safe_integral.hpp>
#include <bsl/unlikely.hpp>
[[nodiscard]] auto
main(bsl::int32 const argc, bsl::cstr_type const *const argv) noexcept
-> bsl::exit_code
{
constexpr auto num_expected_args{2_umx};
bsl::arguments const args{argc, argv};
if (args.size() < num_expected_args) {
bsl::error() << "Invalid number of args" << bsl::endl << bsl::here();
return bsl::exit_failure;
}
constexpr auto index_of_arg{1_umx};
auto const val{args.at<bsl::safe_i32>(index_of_arg)};
if (bsl::unlikely(val.is_invalid())) {
bsl::error() << "Invalid arg" << bsl::endl << bsl::here();
return bsl::exit_failure;
}
constexpr auto size_of_arr{42_umx};
bsl::array<bsl::safe_i32, size_of_arr.get()> mut_arr{};
for (auto &mut_elem : mut_arr) {
mut_elem = val;
}
for (bsl::safe_idx mut_i{}; mut_i < mut_arr.size(); ++mut_i) {
bsl::print() << " elem[" // --
<< mut_i // --
<< "] == " // --
<< bsl::fmt{"#010x", *mut_arr.at_if(mut_i)} // --
<< bsl::endl; // --
}
return bsl::exit_success;
}
Check out our Can You Hack It?® challenge and test your skills! Submit your score to show us what you’ve got. We have offices across the country and offer competitive pay and outstanding benefits. Join a team that is not only committed to the future of cyberspace, but to our employee’s success as well.
Currently, the BSL only supports the Clang/LLVM 11+ compiler. This, however, ensures the BSL can be natively compiled on Windows including support for cross-compiling. Support for other C++20 compilers can be added if needed, just let us know if that is something you need.
To compile the BSL on Windows, you must first install the following:
- Visual Studio (Enable "Desktop development with C++")
- LLVM 10+
- CMake 3.13+
- Ninja
Visual Studio is needed as it contains Windows specific libraries that are needed during compilation. Instead of using the Clang/LLVM project that natively ships with Visual Studio, we use the standard Clang/LLVM binaries provided by the LLVM project which ensures we get all of the tools including LLD, Clang Tidy and Clang Format. Also note that you must put Ninja somewhere in your path (we usually drop into CMake's bin folder).
To compile the BSL, use the following:
git clone https://github.com/bareflank/bsl
mkdir bsl/build && cd bsl/build
cmake -GNinja -DCMAKE_CXX_COMPILER="clang++" -DBUILD_EXAMPLES=ON -DBUILD_TESTS=ON ..
ninja info
ninja
To compile the BSL on Ubuntu (20.04 or higher) you must first install the following dependencies:
sudo apt-get install -y clang cmake
To compile the BSL, use the following:
git clone https://github.com/bareflank/bsl
mkdir bsl/build && cd bsl/build
cmake -DCMAKE_CXX_COMPILER="clang++" -DBUILD_EXAMPLES=ON -DBUILD_TESTS=ON ..
make info
make
The BSL implements everything as a "constexpr". Beyond the usual reasons for compile-time code, the BSL enables unit tests to be executed at compile-time. Although C++20 added an enormous amount of additional support for "constexpr", there are a number of things that are still not allowed in a "constexpr", and this is a good thing, especially if critical systems compliance is important to your use case. This includes:
- No support for Undefined Behavior (UB)
- No support for casts from a void* to something else, or any other attempts to change an object's type without proper lifetime management.
- No support for memory leaks and any other memory related issues like out-of-bounds array accesses, etc.
- No support for reinterpret_cast, goto, etc.
- No support for the use of uninitialized variables
- No support for global or static local variables
- And much more
In other words, the constexpr validation checker in the compiler is in a way, the ultimate dynamic analysis tool. Tools like ASAN, UBSAN and Clang-Tidy are helpful, and should still be used, but if you ensure 100% code coverage at compile-time, most of the hard to find bugs related to undefined behavior will be eliminated. To perform compile-time unit testing, consider the following test:
[[nodiscard]] constexpr auto
tests() noexcept -> bsl::exit_code
{
bsl::ut_scenario{"verify += adds correctly"} = []() noexcept {
bsl::ut_given{} = []() noexcept {
constexpr auto data1{42_umx};
bsl::safe_umx data2{};
bsl::ut_when{} = [&]() noexcept {
data2 += data1;
bsl::ut_then{} = [&]() noexcept {
bsl::ut_check(data2.checked() == data1);
};
};
};
};
return bsl::ut_success();
}
To execute this test at compile-time, simply do the following:
[[nodiscard]] auto
main() noexcept -> bsl::exit_code
{
static_assert(tests() == bsl::ut_success());
return tests();
}
Using this pattern, all unit tests will be executed at both compile-time and at runtime, allowing you to use things like code coverage tools to ensure complete coverage of your unit tests.
If you think this is impossible in any real-world example, take a look at the Bareflank Hypervisor. This is not a simple "Hello World" application, but instead is a hypervisor microkernel, complete with paging, ASM logic, etc. With a couple small exceptions (like virtual address to physical address conversions), all of the C++ code in this repro is tested at compile-time with 100% code coverage including branch coverage.
The BSL is mostly compliant with AUTOSAR, and in a lot of ways far exceeds the AUTOSAR rules. This means the BSL is one of the few, if not the only partial C++ libraries in open source that is AUTOSAR compliant. There are some rules that are not adhered to:
- The BSL uses C++20. Without C++20, constexpr unit testing would not be possible. AUTOSAR requires the use of C++14. The advantages of "constexpr everything" for safety far out-weight the disadvantages of needing an exception to this rule. To ensure as much compliance as possible, the BSL avoids most of the new features added with C++17 and C++20 and will wait until a new AUTOSAR/MISRA specification is available to provide proper guidance.
- Since C++14 is not used, issues with things like
auto i{};
are fixed and therefor allowed. Two's compliment as well. This does not mean that all uses ofauto
are allowed as a number of rules still exist aroundauto
that have nothing to do with the ambiguity issues withauto i{};
in C++14. - Some user-defined literals are provided for initializing fixed-width integrals. How this is done to ensure there are no issues with initializing variables can be found in the convert.hpp header file, but the short story is, it is absolutely possible to create safe user-defined literals if raw literals are used (instead of their cooked counterparts). Raw literals require the code to manually parse the literal's tokens and thanks to C++20's extensive support for constexpr, this is all possible at compile-time.
- In some rare situations the BSL does not use an explicit single argument constructor to ensure compatibility with the C++ standard library where it makes sense. In these cases, a deleted single argument template function is provided which ensures that implicit conversions are still not possible, mitigating this issue. In fact, implicit conversions of any kind are not allowed by our custom version of the Clang-Tidy static analysis engine, including implicit conversions of integral types.
- There are some rules that simply cannot be adhered to when implementing the C++ standard library features. For example, the BSL must perform pointer arithmetic using the subscript operator when implementing bsl::array. It must also include some non-C++ headers like stdint.h to implement cstdint.hpp as required by the spec.
- C++ exceptions are not used by the BSL, but they are supported (meaning you can use C++ exceptions with the BSL if you choose). This is due to the fact that a lot of embedded, IoT and kernel/hypervisor applications for the BSL cannot support C++ exceptions due to the need for an unwinder, which is only really provided for Windows, Linux and macOS.
The Bareflank Support Library provides a ton of useful resources to learn how to use the library including:
- Examples: https://github.com/Bareflank/bsl/tree/master/examples
- Unit Tests: https://github.com/Bareflank/bsl/tree/master/tests
If you have any questions, bugs, or feature requests, please feel free to ask on any of the following:
- Slack: https://bareflank.herokuapp.com/
- Issue Tracker: https://github.com/Bareflank/bsl/issues
If you would like to help:
- Pull Requests: https://github.com/Bareflank/bsl/pulls
- Contributing Guidelines: https://github.com/Bareflank/bsl/blob/master/contributing.md
The Bareflank Support Library leverages the following tools to ensure the highest possible code quality. Each pull request undergoes the following rigorous testing and review:
- Static Analysis: Clang Tidy
- Dynamic Analysis: Google's ASAN and UBSAN
- Code Coverage: Code Coverage with CodeCov
- Coding Standards: AUTOSAR C++14 and C++ Core Guidelines
- Style: Clang Format
- Documentation: Doxygen