Skip to content

Commit 2c3d210

Browse files
committed
MSVC experimental support through __declspec(property())
1 parent c703160 commit 2c3d210

File tree

10 files changed

+161
-46
lines changed

10 files changed

+161
-46
lines changed

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,13 @@ jobs:
2626
#
2727
# To add more build types (Release, Debug, RelWithDebInfo, etc.) customize the build_type list.
2828
matrix:
29-
os: [ubuntu-latest]
29+
os: [ubuntu-latest, windows-latest]
3030
build_type: [Release]
3131
c_compiler: [gcc, clang, cl]
3232
include:
33+
- os: windows-latest
34+
c_compiler: cl
35+
cpp_compiler: cl
3336
- os: ubuntu-latest
3437
c_compiler: gcc
3538
cpp_compiler: g++
@@ -39,6 +42,10 @@ jobs:
3942
exclude:
4043
- os: ubuntu-latest
4144
c_compiler: cl
45+
- os: windows-latest
46+
c_compiler: gcc
47+
- os: windows-latest
48+
c_compiler: clang
4249

4350
steps:
4451
- uses: actions/checkout@v4

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,5 @@ build*
3333
*.exe
3434
*.out
3535
*.app
36+
37+
.vs/

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ with important features on the horizon. [Feedback](https://github.com/Intergated
88

99
What makes bitfilled different from the language standard's [bit fields][stdbitfield]?
1010

11-
1. **Portability** - regardless of platform or toolchain, the code behavior is the same (**except MSVC**, as it refuses to implement `[[no_unique_address]]`)
11+
1. **Portability** - regardless of platform or toolchain, the code behavior is the same
12+
(**except MSVC**, as it refuses to implement `[[no_unique_address]]`, so instead a compatible alternative is implemented with `__declspec(property())`)
1213
2. **Performance** - optimized binary is identical to standard bit fields
1314
3. **Flexibility** - allows bit fields on custom types, bit field arrays, and customizing bit operations (e.g. bit-banding)
1415

@@ -83,12 +84,12 @@ Some examples are due:
8384
(Do not be alarmed by the macros, their main purpose is to reduce the character count,
8485
as having `[[no_unique_address]]` and a long type name isn't all that informative in this context.)
8586
```cpp
86-
#include <bitfilled/bitfilled.hpp>
87+
#include <bitfilled.hpp>
8788
struct myint : bitfilled::host_integer<unsigned>
8889
{
8990
BF_BITS(bool, 0) boolean; // 1 bit at offset 0
9091
BF_BITS(std::memory_order, 1, 3) enumerated; // 3 bits at offset 1
91-
BF_BITSET(bool, 1, 16, 4) bitset; // 16 * 1 bits at offset 4
92+
BF_BITSET(bool, 1, 16, 4) bitset BF_BITSET_POSTFIX; // 16 * 1 bits at offset 4
9293
};
9394
```
9495
@@ -103,7 +104,7 @@ Let's look at a more advanced use-case, memory-mapped register definition.
103104
We will use the SysTick timer, found in most popular ARM MCUs:
104105

105106
```cpp
106-
#include <bitfilled/bitfilled.hpp>
107+
#include <bitfilled.hpp>
107108
struct systick {
108109
struct csr : BF_MMREG(std::uint32_t, rw) {
109110
BF_COPY_SUPERCLASS(csr)

bitfilled/bitfilled/base_ops.hpp

Lines changed: 44 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#define __BITFILLED_BASE_OPS_HPP__
44

55
#include "bitfilled/access.hpp"
6+
#include "bitfilled/macros.hpp"
67
#include "bitfilled/size.hpp"
78

89
namespace bitfilled
@@ -17,6 +18,14 @@ namespace bitfilled
1718
#define BITFILLED_ASSIGN_RETURN_EXPR(EXPR)
1819
#endif
1920

21+
#if _MSC_VER
22+
#define BITFILLED_FIELD_PROPS_PARAM_T T
23+
#define BITFILLED_FIELDSET_PROPS_PARAM_T T
24+
#else
25+
#define BITFILLED_FIELD_PROPS_PARAM_T bitfield_props<FIRST_BIT, LAST_BIT>
26+
#define BITFILLED_FIELDSET_PROPS_PARAM_T regbitfieldset_props<ITEM_SIZE, ITEM_COUNT, OFFSET>
27+
#endif
28+
2029
/// @brief copy const-volatile qualifiers from one reference type to another
2130
// https://stackoverflow.com/a/31173086
2231
template <typename T, typename U>
@@ -156,99 +165,106 @@ struct base
156165

157166
public:
158167
template <std::size_t FIRST_BIT, std::size_t LAST_BIT, typename TVal>
159-
static void set_field(bitfield_props<FIRST_BIT, LAST_BIT>& bf, TVal value)
168+
static void set_field(BITFILLED_FIELD_PROPS_PARAM_T& bf, TVal value)
160169
requires(is_writeable<bitfield_ops::access()>)
161170
{
162171
const auto v = static_cast<int_type>(value);
163172
if constexpr (!is_readable<bitfield_ops::access()> or
164173
is_ephemeralwrite<bitfield_ops::access()>)
165174
{
166-
setter(bf, bf.position_field(v));
175+
setter(bf, bitfield_props<FIRST_BIT, LAST_BIT>::position_field(v));
167176
}
168177
else
169178
{
170-
setter(bf, bf.insert_field(getter(bf), v));
179+
setter(bf, bitfield_props<FIRST_BIT, LAST_BIT>::insert_field(getter(bf), v));
171180
}
172181
}
173182
template <std::size_t FIRST_BIT, std::size_t LAST_BIT, typename TVal>
174-
static void set_field(volatile bitfield_props<FIRST_BIT, LAST_BIT>& bf, TVal value)
183+
static void set_field(volatile BITFILLED_FIELD_PROPS_PARAM_T& bf, TVal value)
175184
requires(is_writeable<bitfield_ops::access()>)
176185
{
177186
const auto v = static_cast<int_type>(value);
178187
if constexpr (!is_readable<bitfield_ops::access()> or
179188
is_ephemeralwrite<bitfield_ops::access()>)
180189
{
181-
setter(bf, bf.position_field(v));
190+
setter(bf, bitfield_props<FIRST_BIT, LAST_BIT>::position_field(v));
182191
}
183192
else
184193
{
185-
setter(bf, bf.insert_field(getter(bf), v));
194+
setter(bf, bitfield_props<FIRST_BIT, LAST_BIT>::insert_field(getter(bf), v));
186195
}
187196
}
188197

189198
template <typename TVal, std::size_t FIRST_BIT, std::size_t LAST_BIT>
190-
static TVal get_field(const bitfield_props<FIRST_BIT, LAST_BIT>& bf)
199+
static TVal get_field(const BITFILLED_FIELD_PROPS_PARAM_T& bf)
191200
requires(is_readable<bitfield_ops::access()>)
192201
{
193-
auto x = static_cast<TVal>(bf.extract_field(getter(bf)));
194-
return bf.sign_extend(x);
202+
auto x =
203+
static_cast<TVal>(bitfield_props<FIRST_BIT, LAST_BIT>::extract_field(getter(bf)));
204+
return bitfield_props<FIRST_BIT, LAST_BIT>::sign_extend(x);
195205
}
196206
template <typename TVal, std::size_t FIRST_BIT, std::size_t LAST_BIT>
197-
static TVal get_field(const volatile bitfield_props<FIRST_BIT, LAST_BIT>& bf)
207+
static TVal get_field(const volatile BITFILLED_FIELD_PROPS_PARAM_T& bf)
198208
requires(is_readable<bitfield_ops::access()>)
199209
{
200-
auto x = static_cast<TVal>(bf.extract_field(getter(bf)));
201-
return bf.sign_extend(x);
210+
auto x =
211+
static_cast<TVal>(bitfield_props<FIRST_BIT, LAST_BIT>::extract_field(getter(bf)));
212+
return bitfield_props<FIRST_BIT, LAST_BIT>::sign_extend(x);
202213
}
203214

204215
template <std::size_t ITEM_SIZE, std::size_t ITEM_COUNT, std::size_t OFFSET, typename TVal>
205-
static void set_item(regbitfieldset_props<ITEM_SIZE, ITEM_COUNT, OFFSET>& bf,
206-
std::size_t index, TVal value)
216+
static void set_item(BITFILLED_FIELDSET_PROPS_PARAM_T& bf, std::size_t index, TVal value)
207217
requires(is_writeable<bitfield_ops::access()>)
208218
{
209219
const auto v = static_cast<int_type>(value);
210220
if constexpr (!is_readable<bitfield_ops::access()> or
211221
is_ephemeralwrite<bitfield_ops::access()>)
212222
{
213-
setter(bf, bf.position_field(v, index));
223+
setter(bf, regbitfieldset_props<ITEM_SIZE, ITEM_COUNT, OFFSET>::position_field(
224+
v, index));
214225
}
215226
else
216227
{
217-
setter(bf, bf.insert_field(getter(bf), v, index));
228+
setter(bf, regbitfieldset_props<ITEM_SIZE, ITEM_COUNT, OFFSET>::insert_field(
229+
getter(bf), v, index));
218230
}
219231
}
220232
template <std::size_t ITEM_SIZE, std::size_t ITEM_COUNT, std::size_t OFFSET, typename TVal>
221-
static void set_item(volatile regbitfieldset_props<ITEM_SIZE, ITEM_COUNT, OFFSET>& bf,
222-
std::size_t index, TVal value)
233+
static void set_item(volatile BITFILLED_FIELDSET_PROPS_PARAM_T& bf, std::size_t index,
234+
TVal value)
223235
requires(is_writeable<bitfield_ops::access()>)
224236
{
225237
const auto v = static_cast<int_type>(value);
226238
if constexpr (!is_readable<bitfield_ops::access()> or
227239
is_ephemeralwrite<bitfield_ops::access()>)
228240
{
229-
setter(bf, bf.position_field(v, index));
241+
setter(bf, regbitfieldset_props<ITEM_SIZE, ITEM_COUNT, OFFSET>::position_field(
242+
v, index));
230243
}
231244
else
232245
{
233-
setter(bf, bf.insert_field(getter(bf), v, index));
246+
setter(bf, regbitfieldset_props<ITEM_SIZE, ITEM_COUNT, OFFSET>::insert_field(
247+
getter(bf), v, index));
234248
}
235249
}
236250

237251
template <typename TVal, std::size_t ITEM_SIZE, std::size_t ITEM_COUNT, std::size_t OFFSET>
238-
static TVal get_item(const regbitfieldset_props<ITEM_SIZE, ITEM_COUNT, OFFSET>& bf,
239-
std::size_t index)
252+
static TVal get_item(const BITFILLED_FIELDSET_PROPS_PARAM_T& bf, std::size_t index)
240253
requires(is_readable<bitfield_ops::access()>)
241254
{
242-
auto x = static_cast<TVal>(bf.extract_field(getter(bf), index));
243-
return bf.sign_extend(x);
255+
auto x = static_cast<TVal>(
256+
regbitfieldset_props<ITEM_SIZE, ITEM_COUNT, OFFSET>::extract_field(getter(bf),
257+
index));
258+
return regbitfieldset_props<ITEM_SIZE, ITEM_COUNT, OFFSET>::sign_extend(x);
244259
}
245260
template <typename TVal, std::size_t ITEM_SIZE, std::size_t ITEM_COUNT, std::size_t OFFSET>
246-
static TVal get_item(const volatile regbitfieldset_props<ITEM_SIZE, ITEM_COUNT, OFFSET>& bf,
247-
std::size_t index)
261+
static TVal get_item(const volatile BITFILLED_FIELDSET_PROPS_PARAM_T& bf, std::size_t index)
248262
requires(is_readable<bitfield_ops::access()>)
249263
{
250-
auto x = static_cast<TVal>(bf.extract_field(getter(bf), index));
251-
return bf.sign_extend(x);
264+
auto x = static_cast<TVal>(
265+
regbitfieldset_props<ITEM_SIZE, ITEM_COUNT, OFFSET>::extract_field(getter(bf),
266+
index));
267+
return regbitfieldset_props<ITEM_SIZE, ITEM_COUNT, OFFSET>::sign_extend(x);
252268
}
253269
};
254270
};

bitfilled/bitfilled/integer.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ struct packed_integer
194194
{
195195
return storage_.template to_integral<value_type>(endianness);
196196
}
197+
BITFILLED_OPS_FORWARDING
197198
};
198199

199200
/// @brief The host_integer class wraps an arithmetic type to allow subclassing it
@@ -214,6 +215,7 @@ struct host_integer
214215
raw_ = other;
215216
return *this;
216217
}
218+
BITFILLED_OPS_FORWARDING
217219

218220
private:
219221
T raw_{};

bitfilled/bitfilled/macros.hpp

Lines changed: 81 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,86 @@
22
#ifndef __BITFILLED_MACROS_HPP__
33
#define __BITFILLED_MACROS_HPP__
44

5+
#define BF_CONCAT_IMPL(X, Y) X##Y
6+
#define BF_CONCAT(X, Y) BF_CONCAT_IMPL(X, Y)
7+
#define BF_UNIQUE_NAME(NAME) BF_CONCAT(NAME, __LINE__)
8+
9+
#if _MSC_VER
10+
#define BITFILLED_OPS_FORWARDING \
11+
template <typename TVal, std::size_t FIRST_BIT, std::size_t LAST_BIT = FIRST_BIT> \
12+
void _set_field(TVal value) \
13+
{ \
14+
bf_ops::template set_field<FIRST_BIT, LAST_BIT>(*this, value); \
15+
} \
16+
template <typename TVal, std::size_t FIRST_BIT, std::size_t LAST_BIT = FIRST_BIT> \
17+
void _set_field(TVal value) volatile \
18+
{ \
19+
bf_ops::template set_field<FIRST_BIT, LAST_BIT>(*this, value); \
20+
} \
21+
template <typename TVal, std::size_t FIRST_BIT, std::size_t LAST_BIT = FIRST_BIT> \
22+
auto _get_field() const \
23+
{ \
24+
return bf_ops::template get_field<TVal, FIRST_BIT, LAST_BIT>(*this); \
25+
} \
26+
template <typename TVal, std::size_t FIRST_BIT, std::size_t LAST_BIT = FIRST_BIT> \
27+
auto _get_field() const volatile \
28+
{ \
29+
return bf_ops::template get_field<TVal, FIRST_BIT, LAST_BIT>(*this); \
30+
} \
31+
template <typename TVal, std::size_t ITEM_SIZE, std::size_t ITEM_COUNT, std::size_t OFFSET> \
32+
void _set_item(std::size_t index, TVal value) \
33+
{ \
34+
bf_ops::template set_item<ITEM_SIZE, ITEM_COUNT, OFFSET>(*this, index, value); \
35+
} \
36+
template <typename TVal, std::size_t ITEM_SIZE, std::size_t ITEM_COUNT, std::size_t OFFSET> \
37+
void _set_item(std::size_t index, TVal value) volatile \
38+
{ \
39+
bf_ops::template set_item<ITEM_SIZE, ITEM_COUNT, OFFSET>(*this, index, value); \
40+
} \
41+
template <typename TVal, std::size_t ITEM_SIZE, std::size_t ITEM_COUNT, std::size_t OFFSET> \
42+
auto _get_item(std::size_t index) const \
43+
{ \
44+
return bf_ops::template get_item<TVal, ITEM_SIZE, ITEM_COUNT, OFFSET>(*this, index); \
45+
} \
46+
template <typename TVal, std::size_t ITEM_SIZE, std::size_t ITEM_COUNT, std::size_t OFFSET> \
47+
auto _get_item(std::size_t index) const volatile \
48+
{ \
49+
return bf_ops::template get_item<TVal, ITEM_SIZE, ITEM_COUNT, OFFSET>(*this, index); \
50+
}
51+
52+
#define BF_BITS(TYPE, ...) \
53+
TYPE BF_UNIQUE_NAME(_getter)() const \
54+
{ \
55+
return _get_field<TYPE, __VA_ARGS__>(); \
56+
} \
57+
void BF_UNIQUE_NAME(_setter)(TYPE v) \
58+
{ \
59+
_set_field<TYPE, __VA_ARGS__>(v); \
60+
} \
61+
__declspec(property(get = BF_UNIQUE_NAME(_getter), put = BF_UNIQUE_NAME(_setter))) TYPE
62+
63+
#define BF_BITSET(TYPE, ...) \
64+
TYPE BF_UNIQUE_NAME(_getter)(std::size_t i) const \
65+
{ \
66+
return _get_item<TYPE, __VA_ARGS__>(i); \
67+
} \
68+
void BF_UNIQUE_NAME(_setter)(std::size_t i, TYPE v) \
69+
{ \
70+
_set_item<TYPE, __VA_ARGS__>(i, v); \
71+
} \
72+
__declspec(property(get = BF_UNIQUE_NAME(_getter), put = BF_UNIQUE_NAME(_setter))) TYPE
73+
74+
#define BF_BITSET_POSTFIX []
75+
76+
#else
77+
78+
#define BITFILLED_OPS_FORWARDING
579
#define BF_BITS(TYPE, ...) [[no_unique_address]] ::bitfilled::bitfield<TYPE, bf_ops, __VA_ARGS__>
680

781
#define BF_BITSET(TYPE, ...) \
882
[[no_unique_address]] ::bitfilled::bitfieldset<TYPE, bf_ops, __VA_ARGS__>
983

10-
#define BF_COPY_SUPERCLASS(CLASS) \
11-
using superclass::superclass; \
12-
using superclass::operator=;
84+
#define BF_BITSET_POSTFIX
1385

1486
#define BF_MMREGBITS_TYPE(TYPE, ACCESS, NAME, ...) \
1587
using NAME = ::bitfilled::regbitfield<TYPE, bf_ops, ::bitfilled::access::ACCESS, __VA_ARGS__>; \
@@ -23,16 +95,18 @@
2395
[[no_unique_address]] ::bitfilled::regbitfieldset<TYPE, bf_ops, ::bitfilled::access::ACCESS, \
2496
__VA_ARGS__>
2597

98+
#endif
99+
100+
#define BF_COPY_SUPERCLASS(CLASS) \
101+
using superclass::superclass; \
102+
using superclass::operator=;
103+
26104
/// @brief Macro to define a memory-mapped register type with bitfields.
27105
/// @param TYPE The underlying type of the register (e.g., uint32_t).
28106
/// @param ACCESS The access type (e.g., rw, r, w).
29107
/// @param ... Custom bitfield operations when desired (e.g., bitband<PERIPH_BASE>).
30108
#define BF_MMREG(TYPE, ...) public ::bitfilled::mmreg<TYPE, ::bitfilled::access::__VA_ARGS__>
31109

32-
#define BF_CONCAT_IMPL(X, Y) X##Y
33-
#define BF_CONCAT(X, Y) BF_CONCAT_IMPL(X, Y)
34-
#define BF_UNIQUE_NAME(NAME) BF_CONCAT(NAME, __LINE__)
35-
36110
#define BF_MMREG_RESERVED(WIDTH, SIZE) \
37111
private: \
38112
const ::std::array<::bitfilled::sized_unsigned_t<WIDTH>, SIZE> BF_UNIQUE_NAME(_reserved_); \

bitfilled/bitfilled/mmreg.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ struct mmreg : public detail::accesscondition<ACCESS, detail::mmr_r<T>, detail::
127127
constexpr mmreg& operator=(const mmreg&)
128128
requires(!is_readwrite<ACCESS>)
129129
= delete;
130+
131+
BITFILLED_OPS_FORWARDING
130132
};
131133

132134
} // namespace bitfilled

test/CMakeLists.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ add_executable(${PROJECT_NAME}-test main.cpp)
66
target_sources(${PROJECT_NAME}-test
77
PRIVATE
88
integer.test.cpp
9-
mmreg.test.cpp
109
size.test.cpp
1110
variable_bits.test.cpp
11+
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:mmreg.test.cpp>
1212
)
1313
target_link_libraries(${PROJECT_NAME}-test
1414
PRIVATE
@@ -17,6 +17,9 @@ target_link_libraries(${PROJECT_NAME}-test
1717
)
1818
add_test(NAME ${PROJECT_NAME}-test COMMAND ${PROJECT_NAME}-test)
1919

20+
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
21+
return()
22+
endif()
2023

2124
# https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
2225
# https://embeddedartistry.com/blog/2023/09/20/leveraging-your-toolchain-to-improve-security/

0 commit comments

Comments
 (0)