Skip to content

Commit 5780a19

Browse files
authored
Merge pull request #10 from KredeGC/dev
More compiletime bounds
2 parents 6eeae1a + 588ab41 commit 5780a19

File tree

10 files changed

+418
-39
lines changed

10 files changed

+418
-39
lines changed

.github/workflows/main.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,14 @@ jobs:
5454
- name: Run premake
5555
run: premake5 ${{ matrix.machine.action }} --toolset=${{ matrix.machine.toolset }} --dialect=${{ matrix.dialect }}
5656
- name: Initialize CodeQL
57+
continue-on-error: true
5758
uses: github/codeql-action/init@v2
5859
with:
5960
languages: cpp
6061
- name: Build
6162
run: premake5 build --config=${{ matrix.config }} --architecture=${{ matrix.architecture }}
6263
- name: Perform CodeQL Analysis
64+
continue-on-error: true
6365
uses: github/codeql-action/analyze@v2
64-
- name: Print contents of bin
65-
if: matrix.machine.os == 'ubuntu-latest'
66-
run: ls bin
6766
- name: Run test
6867
run: premake5 test --config=${{ matrix.config }} --architecture=${{ matrix.architecture }}

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Based on [Glenn Fiedler's articles](https://gafferongames.com/post/reading_and_w
2727
* [Compile-time bounded integers - bounded_int\<T, T Min, T Max\>](#compile-time-bounded-integers---bounded_intt-t-min-t-max)
2828
* [C-style strings - const char*](#c-style-strings---const-char)
2929
* [Modern strings - std::basic_string\<T\>](#modern-strings---stdbasic_stringt)
30+
* [Double-precision float - double](#double-precision-float---double)
3031
* [Single-precision float - float](#single-precision-float---float)
3132
* [Half-precision float - half_precision](#half-precision-float---half_precision)
3233
* [Bounded float - bounded_range](#bounded-float---bounded_range)
@@ -263,8 +264,8 @@ The examples below follow the same structure: First writing to a buffer and then
263264
Writing the first 5 bits of an int to the buffer, then reading it back:
264265
```cpp
265266
// Create a writer, referencing the buffer and its size
266-
byte_buffer<4> buffer; // Buffer must be a multiple of 4 bytes / 32 bits
267-
bit_writer writer(buffer);
267+
alignas(uint32_t) uint8_t buffer[4]; // Buffer must be a multiple of 4 bytes / 32 bits and 4-byte-aligned
268+
bit_writer writer(buffer, 4);
268269
269270
// Write the value
270271
uint32_t value = 27; // We can choose any value below 2^5. Otherwise we need more than 5 bits
@@ -284,7 +285,7 @@ reader.serialize_bits(out_value, 5); // out_value should now have a value of 27
284285
Writing a signed int to the buffer, within a range:
285286
```cpp
286287
// Create a writer, referencing the buffer and its size
287-
byte_buffer<4> buffer;
288+
byte_buffer<4> buffer; // byte_bufer is just a wrapper for a 4-byte aligned buffer
288289
bit_writer writer(buffer);
289290

290291
// Write the value

include/bitstream/stream/bit_reader.h

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -315,11 +315,9 @@ namespace bitstream
315315
* @param ...args The arguments to pass to the serialize function
316316
* @return Whether successful or not
317317
*/
318-
template<typename Trait, typename... Args>
319-
[[nodiscard]] bool serialize(Args&&... args) noexcept(utility::is_noexcept_serialize_v<Trait, bit_reader, Args...>)
318+
template<typename Trait, typename... Args, typename = utility::has_serialize_t<Trait, bit_reader, Args...>>
319+
[[nodiscard]] bool serialize(Args&&... args) noexcept(utility::is_serialize_noexcept_v<Trait, bit_reader, Args...>)
320320
{
321-
static_assert(utility::has_serialize_v<Trait, bit_reader, Args...>, "Could not find serializable trait for the given type. Remember to specialize serializable_traits<> with the given type");
322-
323321
return serialize_traits<Trait>::serialize(*this, std::forward<Args>(args)...);
324322
}
325323

@@ -333,14 +331,10 @@ namespace bitstream
333331
* @param ...args The rest of the arguments to pass to the serialize function
334332
* @return Whether successful or not
335333
*/
336-
template<typename Trait, typename... Args>
337-
[[nodiscard]] bool serialize(Trait&& arg, Args&&... args) noexcept(utility::is_noexcept_serialize_v<utility::deduce_trait_t<Trait, bit_reader, Args...>, bit_reader, Trait, Args...>)
334+
template<typename... Args, typename Trait, typename = utility::has_deduce_serialize_t<Trait, bit_reader, Args...>>
335+
[[nodiscard]] bool serialize(Trait&& arg, Args&&... args) noexcept(utility::is_deduce_serialize_noexcept_v<Trait, bit_reader, Args...>)
338336
{
339-
using deduce_t = utility::deduce_trait_t<Trait, bit_reader, Args...>;
340-
341-
static_assert(utility::has_serialize_v<deduce_t, bit_reader, Trait, Args...>, "Could not deduce serializable trait for the given arguments. Remember to specialize serializable_traits<> with the given type");
342-
343-
return serialize_traits<deduce_t>::serialize(*this, std::forward<Trait>(arg), std::forward<Args>(args)...);
337+
return serialize_traits<utility::deduce_trait_t<Trait, bit_reader, Args...>>::serialize(*this, std::forward<Trait>(arg), std::forward<Args>(args)...);
344338
}
345339

346340
private:

include/bitstream/stream/bit_writer.h

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -372,11 +372,9 @@ namespace bitstream
372372
* @param ...args The arguments to pass to the serialize function
373373
* @return Whether successful or not
374374
*/
375-
template<typename Trait, typename... Args>
376-
[[nodiscard]] bool serialize(Args&&... args) noexcept(utility::is_noexcept_serialize_v<Trait, bit_writer, Args...>)
375+
template<typename Trait, typename... Args, typename = utility::has_serialize_t<Trait, bit_writer, Args...>>
376+
[[nodiscard]] bool serialize(Args&&... args) noexcept(utility::is_serialize_noexcept_v<Trait, bit_writer, Args...>)
377377
{
378-
static_assert(utility::has_serialize_v<Trait, bit_writer, Args...>, "Could not find serializable trait for the given type. Remember to specialize serializable_traits<> with the given type");
379-
380378
return serialize_traits<Trait>::serialize(*this, std::forward<Args>(args)...);
381379
}
382380

@@ -390,14 +388,10 @@ namespace bitstream
390388
* @param ...args The rest of the arguments to pass to the serialize function
391389
* @return Whether successful or not
392390
*/
393-
template<typename Trait, typename... Args>
394-
[[nodiscard]] bool serialize(Trait&& arg, Args&&... args) noexcept(utility::is_noexcept_serialize_v<utility::deduce_trait_t<Trait, bit_writer, Args...>, bit_writer, Trait, Args...>)
391+
template<typename... Args, typename Trait, typename = utility::has_deduce_serialize_t<Trait, bit_writer, Args...>>
392+
[[nodiscard]] bool serialize(Trait&& arg, Args&&... args) noexcept(utility::is_deduce_serialize_noexcept_v<Trait, bit_writer, Args...>)
395393
{
396-
using deduce_t = utility::deduce_trait_t<Trait, bit_writer, Args...>;
397-
398-
static_assert(utility::has_serialize_v<deduce_t, bit_writer, Trait, Args...>, "Could not deduce serializable trait for the given arguments. Remember to specialize serializable_traits<> with the given type");
399-
400-
return serialize_traits<deduce_t>::serialize(*this, std::forward<Trait>(arg), std::forward<Args>(args)...);
394+
return serialize_traits<utility::deduce_trait_t<Trait, bit_writer, Args...>>::serialize(*this, std::forward<Trait>(arg), std::forward<Args>(args)...);
401395
}
402396

403397
private:

include/bitstream/stream/byte_buffer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,7 @@ namespace bitstream
1616
static_assert(Size % 4 == 0, "Buffer size must be a multiple of 4");
1717

1818
alignas(uint32_t) uint8_t Bytes[Size];
19+
20+
uint8_t& operator[](size_t i) noexcept { return Bytes[i]; }
1921
};
2022
}

include/bitstream/traits/enum_trait.h

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,14 @@
1010
namespace bitstream
1111
{
1212
/**
13-
* @brief A trait used to serialize an enum type
13+
* @brief Wrapper type for compiletime known integer bounds
14+
* @tparam T
15+
*/
16+
template<typename T, std::underlying_type_t<T> = (std::numeric_limits<T>::min)(), std::underlying_type_t<T> = (std::numeric_limits<T>::max)()>
17+
struct bounded_enum;
18+
19+
/**
20+
* @brief A trait used to serialize an enum type with runtime bounds
1421
*/
1522
template<typename T>
1623
struct serialize_traits<T, typename std::enable_if_t<std::is_enum_v<T>>>
@@ -35,4 +42,32 @@ namespace bitstream
3542
return true;
3643
}
3744
};
45+
46+
/**
47+
* @brief A trait used to serialize an enum type with compiletime bounds
48+
*/
49+
template<typename T, std::underlying_type_t<T> Min, std::underlying_type_t<T> Max>
50+
struct serialize_traits<bounded_enum<T, Min, Max>, typename std::enable_if_t<std::is_enum_v<T>>>
51+
{
52+
using value_type = std::underlying_type_t<T>;
53+
using bound_type = bounded_int<value_type, Min, Max>;
54+
55+
static bool serialize(bit_writer& writer, T value) noexcept
56+
{
57+
value_type unsigned_value = static_cast<value_type>(value);
58+
59+
return writer.serialize<bound_type>(unsigned_value);
60+
}
61+
62+
static bool serialize(bit_reader& reader, T& value) noexcept
63+
{
64+
value_type unsigned_value;
65+
66+
BS_ASSERT(reader.serialize<bound_type>(unsigned_value));
67+
68+
value = static_cast<T>(unsigned_value);
69+
70+
return true;
71+
}
72+
};
3873
}

0 commit comments

Comments
 (0)