Skip to content

Commit 9908f04

Browse files
committed
refactoring for more flexibility
1 parent fd4e171 commit 9908f04

18 files changed

+1026
-610
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# This starter workflow is for a CMake project running on multiple platforms. There is a different starter workflow if you just want a single platform.
2+
# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-single-platform.yml
3+
name: CMake on multiple platforms
4+
5+
on:
6+
push:
7+
branches: [ "main", "develop" ]
8+
pull_request:
9+
branches: [ "main", "develop" ]
10+
11+
permissions:
12+
contents: read
13+
14+
jobs:
15+
build:
16+
runs-on: ${{ matrix.os }}
17+
18+
strategy:
19+
# Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable.
20+
fail-fast: false
21+
22+
# Set up a matrix to run the following 3 configurations:
23+
# 1. <Windows, Release, latest MSVC compiler toolchain on the default runner image, default generator>
24+
# 2. <Linux, Release, latest GCC compiler toolchain on the default runner image, default generator>
25+
# 3. <Linux, Release, latest Clang compiler toolchain on the default runner image, default generator>
26+
#
27+
# To add more build types (Release, Debug, RelWithDebInfo, etc.) customize the build_type list.
28+
matrix:
29+
os: [ubuntu-latest]
30+
build_type: [Release]
31+
c_compiler: [gcc, clang, cl]
32+
include:
33+
- os: ubuntu-latest
34+
c_compiler: gcc
35+
cpp_compiler: g++
36+
- os: ubuntu-latest
37+
c_compiler: clang
38+
cpp_compiler: clang++
39+
exclude:
40+
- os: ubuntu-latest
41+
c_compiler: cl
42+
43+
steps:
44+
- uses: actions/checkout@v4
45+
46+
- name: Set reusable strings
47+
# Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file.
48+
id: strings
49+
shell: bash
50+
run: |
51+
echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT"
52+
53+
- name: Configure CMake
54+
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
55+
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
56+
run: >
57+
cmake -B ${{ steps.strings.outputs.build-output-dir }}
58+
-DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }}
59+
-DCMAKE_C_COMPILER=${{ matrix.c_compiler }}
60+
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
61+
-S ${{ github.workspace }}
62+
63+
- name: Build
64+
# Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator).
65+
run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }}
66+
67+
- name: Test
68+
working-directory: ${{ steps.strings.outputs.build-output-dir }}
69+
# Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator).
70+
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
71+
run: ctest --build-config ${{ matrix.build_type }}

.github/workflows/cmake-single-platform.yml

Lines changed: 0 additions & 38 deletions
This file was deleted.

.github/workflows/codeql.yml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
name: "CodeQL Advanced"
2+
3+
on:
4+
push:
5+
branches: [ "main" ]
6+
pull_request:
7+
branches: [ "main" ]
8+
schedule:
9+
- cron: '25 3 * * 2'
10+
11+
jobs:
12+
analyze:
13+
name: Analyze (${{ matrix.language }})
14+
runs-on: 'ubuntu-latest'
15+
permissions:
16+
# required for all workflows
17+
security-events: write
18+
# required to fetch internal or private CodeQL packs
19+
packages: read
20+
21+
strategy:
22+
fail-fast: false
23+
matrix:
24+
include:
25+
- language: actions
26+
build-mode: none
27+
- language: c-cpp
28+
build-mode: autobuild
29+
steps:
30+
- name: Checkout repository
31+
uses: actions/checkout@v4
32+
33+
# Add any setup steps before running the `github/codeql-action/init` action.
34+
# This includes steps like installing compilers or runtimes (`actions/setup-node`
35+
# or others). This is typically only required for manual builds.
36+
# - name: Setup runtime (example)
37+
# uses: actions/setup-example@v1
38+
39+
# Initializes the CodeQL tools for scanning.
40+
- name: Initialize CodeQL
41+
uses: github/codeql-action/init@v3
42+
with:
43+
languages: ${{ matrix.language }}
44+
build-mode: ${{ matrix.build-mode }}
45+
# If you wish to specify custom queries, you can do so here or in a config file.
46+
# By default, queries listed here will override any specified in a config file.
47+
# Prefix the list here with "+" to use these queries and those in the config file.
48+
49+
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
50+
# queries: security-extended,security-and-quality
51+
52+
- name: Perform CodeQL Analysis
53+
uses: github/codeql-action/analyze@v3
54+
with:
55+
category: "/language:${{matrix.language}}"

README.md

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,21 @@ struct legacy
1919

2020
We can manipulate each field as normal members of `struct legacy`, their values don't cross bit boundaries,
2121
the signed integers even get sign extended, that's all well.
22-
However, the `legacy` type itself cannot be assigned to any other type.
22+
There are a number of drawbacks to using standard bitfields, such as:
23+
1. Lack of standardization (and thus portability),
24+
a C++ compiler implementation can freely decide how these bits are stored in memory
25+
(see also [bitfield traits](./bitfilled/bitfield_traits.hpp) for an overview).
26+
2. Lack of flexibility,
27+
e.g. they may only have integral or enum type,
28+
they cannot be passed by reference,
29+
nor can they be organized into arrays, etc.
30+
2331
This is a problem in many use cases involving bit-fields. So let's solve that:
2432

2533
```cpp
26-
struct nextgen : public bitfilled::host<std::uint8_t>
34+
struct nextgen : bitfilled::host_integer<std::uint8_t>
2735
{
28-
using superclass::operator=;
36+
BF_COPY_SUPERCLASS(nextgen)
2937
BF_BITS(bool, 0) boolean;
3038
BF_BITS(std::endian, 1, 2) enumerated;
3139
BF_BITS(std::int32_t, 3, 7) integer;
@@ -34,7 +42,7 @@ struct nextgen : public bitfilled::host<std::uint8_t>
3442
3543
Our `nextgen` type's bit-fields work with the exact same syntax as their `legacy` counterparts.
3644
The difference here is that a `nextgen` object can be converted to and from any `uint8_t` type,
37-
no more sketchy casting necessary to get the underlying integer type.
45+
no more explicit casting necessary to get the underlying integer type.
3846
One thing to note is that the nextgen fields have absolute bit offsets, as opposed to the legacy
3947
fields (which in turn only have a bit size specifier).
4048
This characteristic of the behavior also means that nextgen's bit-fields can be made to overlap one another.
@@ -44,21 +52,25 @@ This characteristic of the behavior also means that nextgen's bit-fields can be
4452
The bitfilled logic consists of two building blocks, that work in tandem to provide the desired functionality:
4553
1. The bitfilled member variables hold the bit-field's **properties** (`props`): the position of the bits and the access rights,
4654
and also - indirectly - the memory location of the bits (more on that later).
47-
2. The encapsulating object type provides the **operators** (`ops`), which are used by the bitfilled members
55+
2. The encapsulating object type defines the **operators** (`ops`), which are used by the bitfilled members
4856
to perform the memory access and bit operations needed to read or modify the bit-field.
4957
5058
The term "property" is used to refer to the bitfilled members due to them functioning as [properties][property wiki]
5159
as known in other programming languages (C# and Python to name a few):
5260
- they don't increase the size of the encapsulating type [(achieved with `[[no_unique_address]]`)](https://en.cppreference.com/w/cpp/language/attributes/no_unique_address)
5361
- their value is derived from the encapsulating type's state
5462
63+
Note that `[[no_unique_address]]` isn't effective when two bitfields with the same type parameters are defined,
64+
(i.e. same value type, access, operations, bit position) as the C++ core rule of unique identity would be violated
65+
(this applies even if the two bitfield types are distinct, but inherit the same base class).
66+
5567
The key point to understand, and the reason for the design requiring the *operators* in the tandem, is this:
5668
By dereferencing their address, the bitfilled members provide access to memory that is not theirs,
5769
but rather whichever member is preceeding them in the encapsulating type layout.
5870
Therefore the encapsulating type must have a preceeding member variable for bit-field use,
5971
and the bitfilled members must be made aware of this member variable's type - this is what the operators are achieving.
6072
Mismatches between the storage member variable and the operators type is impossible to catch at compile time,
61-
therefore it is recommended to use predefined helper base classes such as `bitfilled::host` and `bitfilled::mmr`,
73+
therefore it is recommended to use predefined helper base classes such as `bitfilled::host_integer` and `bitfilled::mmreg`,
6274
instead of defining the storage member variable and the operators independently.
6375
6476
## Memory mapped registers

bitfilled/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ target_sources(${PROJECT_NAME}
33
${CMAKE_CURRENT_SOURCE_DIR}/bitfilled.hpp
44
${CMAKE_CURRENT_SOURCE_DIR}/bitfield_traits.hpp
55
${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}/access.hpp
6+
${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}/base_ops.hpp
7+
${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}/bitband_ops.hpp
68
${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}/bits.hpp
79
${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}/integer.hpp
810
${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}/macros.hpp

bitfilled/bitfield_traits.hpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ constexpr inline bool has_padding_field = []() constexpr
4949
}();
5050

5151
// bit-field endianness is either little (least significant bit field first), or big
52+
#if defined(__GNUC__) && (__GNUC__ >= 11)
5253
constexpr inline std::endian endianness = []() constexpr
5354
{
5455
constexpr struct s
@@ -60,6 +61,18 @@ constexpr inline std::endian endianness = []() constexpr
6061
static_assert(x);
6162
return x == 1 ? std::endian::little : std::endian::big;
6263
}();
64+
#else
65+
inline std::endian endianness = []()
66+
{
67+
struct s
68+
{
69+
char a : 1 = 1;
70+
char b : 7 = 0;
71+
} val;
72+
static const auto x = std::bit_cast<std::array<char, bitfilled::aligned_size<s>>>(val).front();
73+
return x == 1 ? std::endian::little : std::endian::big;
74+
}();
75+
#endif
6376

6477
// an implementation either wraps overflows, or clamps them
6578
constexpr inline bool overflow_wraps = []() constexpr

bitfilled/bitfilled.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1+
#include "bitfilled/bitband_ops.hpp"
12
#include "bitfilled/bits.hpp"
2-
#include "bitfilled/bitband.hpp"
33
#include "bitfilled/macros.hpp"
44
#include "bitfilled/mmreg.hpp"

0 commit comments

Comments
 (0)