From 0d66b5a135f3e56041e0252e1f6d7cf8f598c8c2 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 10 Nov 2023 23:15:37 -0500 Subject: [PATCH 1/8] This should be in C++23, but some compilers/libraries still lack it. --- lifetime.h | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 lifetime.h diff --git a/lifetime.h b/lifetime.h new file mode 100644 index 0000000..aa44f0e --- /dev/null +++ b/lifetime.h @@ -0,0 +1,39 @@ +static_assert( __cplusplus > 2020'00 ); + +#pragma once + +#include + +#include + +#include + +namespace Alepha::Hydrogen ::detail:: lifetime_m +{ + inline namespace exports + { + // When C++23 arrives, just turn these into `using` statements. + + template< typename T > + std::add_pointer_t< T > + start_lifetime_as( void *const mp ) noexcept + { + const auto raw= new( mp ) std::byte[ sizeof( T ) ]; + const auto data= reinterpret_cast< std::add_pointer_t< T > >( raw ); + return std::launder( data ); + } + + template< typename T > + std::add_pointer_t< std::add_const_t< T > > + start_lifetime_as( const void *const p ) noexcept + { + const auto mp= const_cast< void * >( p ); + return start_lifetime_as< std::add_const_t< T > >( mp ); + } + } +} + +namespace Alepha::Hydrogen::inline exports::inline lifetime_m +{ + using namespace detail::lifetime_m::exports; +} From 0728478f246a36afb3891ee11b7eddbc31257d29 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 10 Nov 2023 23:18:07 -0500 Subject: [PATCH 2/8] Assertion facility. --- CMakeLists.txt | 1 + assertion.h | 30 ++++++++++++++++++++++++++++++ assertion.test/0.cc | 4 ++++ assertion.test/CMakeLists.txt | 1 + 4 files changed, 36 insertions(+) create mode 100644 assertion.h create mode 100644 assertion.test/0.cc create mode 100644 assertion.test/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index afd6717..039b94d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,7 @@ add_subdirectory( word_wrap.test ) add_subdirectory( string_algorithms.test ) add_subdirectory( tuplize_args.test ) add_subdirectory( Thread.test ) +add_subdirectory( assertion.test ) # Sample applications add_executable( example example.cc ) diff --git a/assertion.h b/assertion.h new file mode 100644 index 0000000..17f4b07 --- /dev/null +++ b/assertion.h @@ -0,0 +1,30 @@ +static_assert( __cplusplus > 2020'99 ); + +#pragma once + +#include + +#include + +namespace Alepha::Hydrogen ::detail:: assertion_m +{ + inline namespace exports + { + class ProgrammerExpectationViolation : public virtual Violation {}; + + inline void + assertion( const bool b ) + { + if( not b ) + { + throw build_exception< ProgrammerExpectationViolation >( + "Expectation violated." ); + } + } + } +} + +namespace Alepha::Hydrogen::inline exports::inline assertion_m +{ + using namespace detail::assertion_m::exports; +} diff --git a/assertion.test/0.cc b/assertion.test/0.cc new file mode 100644 index 0000000..8603305 --- /dev/null +++ b/assertion.test/0.cc @@ -0,0 +1,4 @@ +static_assert( __cplusplus > 2020'99 ); + +#include "../assertion.h" + diff --git a/assertion.test/CMakeLists.txt b/assertion.test/CMakeLists.txt new file mode 100644 index 0000000..b099603 --- /dev/null +++ b/assertion.test/CMakeLists.txt @@ -0,0 +1 @@ +unit_test( 0 ) From e61e754d561d5914b1a3298774c63ac41c0a37e8 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 10 Nov 2023 23:24:37 -0500 Subject: [PATCH 3/8] Concept hydrogenation. --- Concepts.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Concepts.h b/Concepts.h index 04716ef..6c2c19c 100644 --- a/Concepts.h +++ b/Concepts.h @@ -11,7 +11,7 @@ static_assert( __cplusplus > 2020'99 ); #include "meta.h" #include "function_traits.h" -namespace Alepha::Hydrogen ::detail:: core_concepts +namespace Alepha::Hydrogen ::detail:: Concepts_m { inline namespace exports { @@ -282,7 +282,7 @@ namespace Alepha::Hydrogen ::detail:: core_concepts } } -namespace Alepha::Hydrogen::inline exports::inline core_concepts +namespace Alepha::Hydrogen::inline exports::inline Concepts_m::inline Concepts { - using namespace detail::core_concepts::exports; + using namespace detail::Concepts_m::exports; } From e2c5d28c5b48050b004f14cfd768310592b4bff7 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 10 Nov 2023 23:25:12 -0500 Subject: [PATCH 4/8] Constness. --- CMakeLists.txt | 1 + Constness.h | 152 ++++++++++++++++++++++++++++++++++ Constness.test/0.cc | 3 + Constness.test/CMakeLists.txt | 1 + 4 files changed, 157 insertions(+) create mode 100644 Constness.h create mode 100644 Constness.test/0.cc create mode 100644 Constness.test/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 039b94d..4b46b65 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ add_subdirectory( string_algorithms.test ) add_subdirectory( tuplize_args.test ) add_subdirectory( Thread.test ) add_subdirectory( assertion.test ) +add_subdirectory( Constness.test ) # Sample applications add_executable( example example.cc ) diff --git a/Constness.h b/Constness.h new file mode 100644 index 0000000..3d58d64 --- /dev/null +++ b/Constness.h @@ -0,0 +1,152 @@ +static_assert( __cplusplus > 2020'00 ); + +#pragma once + +#include + +#include +#include + +#include "meta.h" + +namespace Alepha::Hydrogen ::detail:: Constness_m +{ + inline namespace exports + { + /*! + * Conditional constness primitive. + * + * C++17 and beyond have many conditional attributes on functions. Conditional `noexcept`, + * conditional `explicit`, and even conditional instantiation. The `noexcept` and `explicit` + * are conditional on a boolean condition. This enum permits making templates which + * are conditionally const, depending upon instantiation. + * + * Simple example: + * + * ``` + * template< Constness constness > + * struct IntWrapper + * { + * private: + * int variable; + * + * public: + * maybe_const_t< int &, constness > // This is sort of like if one could write `const( constness ) int &` + * view() { return variable; } + * + * const int & + * view() const { return variable; } + * }; + * + * using MutableIntWrapper= IntWrapper< Mutable >; + * using ConstIntWrapper= IntWrapper< Const >; + * ``` + * + * Now `Constness< Mutable >` has a member `view` which returns `int &` and `Constness< Const >` has a member + * `view` which returns `const int &`. This facility can be useful in implementing pairs of `const`/non-`const` + * iterators, and other gadgets. + */ + enum Constness : bool + { + Const= true, + Mutable= false, + }; + + + /*! + * Apply the `Constness` requested to the specified type. + * + * If the type isn't `const`, but `Constness` is set, then + * it evaluates to `const T`. + */ + template< typename Type, Constness > struct maybe_const; + + template< typename Type > + struct maybe_const< Type, Const > + { + using type= std::add_const_t< Type >; + }; + + template< typename Type > + struct maybe_const< Type, Mutable > + { + using type= Type; + }; + + template< typename Type > + struct maybe_const< Type &, Const > + { + using type= std::add_const_t< Type > &; + }; + + template< typename Type > + struct maybe_const< Type &, Mutable > + { + using type= Type &; + }; + + /*! + * Conditionally make `Type` `const`. + */ + template< typename Type, Constness constness > + using maybe_const_t= typename maybe_const< Type, constness >::type; + + /*! + * Conditionally make `Type` into `const Type *`. + */ + template< typename Type, Constness constness > + using maybe_const_ptr_t= std::add_pointer_t< maybe_const_t< Type, constness > >; + + /*! + * Conditionally call `std::as_const`. + * + * Sometimes `std::as_const` is appropriate to call, and sometimes it isn't. + * In some cases, one might want to have type deduction driven by an expression + * where, in a `const` "branch" `std::as_const` is called, but in a non-`const` + * branch, it isn't. This facilitates that: + * + * ``` + * template< Constness constness > + * struct IntWrapper + * { + * private: + * int variable; + * + * public: + * // `std::as_const` will be called when `constness` is true, + * // and will be skipped otherwise. This permits the right + * // client overload to be called + * template< typename Function > + * decltype( auto ) + * apply( Function func ) + * { + * return func( maybe_as_const< constness >( variable ) ); + * } + * }; + * ``` + */ + template< Constness constness, typename T > + constexpr auto & + maybe_as_const( T &t ) noexcept + { + if constexpr( constness ) return std::as_const( t ); + else return t; + } + + template< Constness constness, typename T > + constexpr decltype( auto ) + maybe_as_const( const T &t ) noexcept + { + if constexpr( constness ) return std::as_const( t ); + else static_assert( dependent_value< false, T > ); + } + + template< Constness constness, typename T > + void maybe_as_const( T && )= delete; + } +} + +namespace Alepha::Hydrogen::inline exports::inline Constness_m +{ + using namespace detail::Constness_m::exports; +} diff --git a/Constness.test/0.cc b/Constness.test/0.cc new file mode 100644 index 0000000..013b3f8 --- /dev/null +++ b/Constness.test/0.cc @@ -0,0 +1,3 @@ +static_assert( __cplusplus > 2020'99 ); + +#include "../Constness.h" diff --git a/Constness.test/CMakeLists.txt b/Constness.test/CMakeLists.txt new file mode 100644 index 0000000..b099603 --- /dev/null +++ b/Constness.test/CMakeLists.txt @@ -0,0 +1 @@ +unit_test( 0 ) From 96d09e3df23d3016c88c550dfbbbe3935aa0697d Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 10 Nov 2023 23:26:44 -0500 Subject: [PATCH 5/8] Missing ctor. --- Exception.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Exception.h b/Exception.h index 8692a2a..10f3f79 100644 --- a/Exception.h +++ b/Exception.h @@ -277,6 +277,8 @@ namespace Alepha::Hydrogen ::detail:: exceptions ~Violation() override { if( not active ) abort(); } + Violation()= default; + Violation( const Violation © )= delete; Violation( Violation © ) : active( copy.active ) { copy.active= false; } }; From 59a969da84bbc80e2dd54a81e5532bd9f4cb968c Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 10 Nov 2023 23:27:06 -0500 Subject: [PATCH 6/8] Swappable facility. --- swappable.h | 173 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 swappable.h diff --git a/swappable.h b/swappable.h new file mode 100644 index 0000000..36152aa --- /dev/null +++ b/swappable.h @@ -0,0 +1,173 @@ +static_assert( __cplusplus > 2020'00 ); + +#pragma once + +#include + +#include + +#include "Capabilities.h" +#include "Concepts.h" + +namespace Alepha::Hydrogen ::detail:: swappable_m +{ + inline namespace exports + { + /*! + * Swappable capability hook. + * + * While it is possible to build `swap` in terms of `std::move` operations, it is often the case + * that `std::move` operations are built in terms of `std::swap`. This isn't a major problem, + * but when writing classes which manage a lot of state, and one has to write the lifecycle + * methods, it can become cumbersome to maintain the list of members that track through lifecycle + * transformations. + * + * Instead, similar to how `Alepha::comparable` works, `Alepha::swappable` is a hook-`Capability` + * which permits the user to write `swap_lens` as a member and the swap operation will be + * built in terms of that lens. This permits the list of members that need to be swapped to be + * written exactly once, in one place, with no repetition of members. + * + * Because `std::tie` objects are not swappable, the `Alepha::swap_magma` binding has been + * provided. + * + * Example: + * ``` + * class Employee : Alepha::swappable + * { + * private: + * std::string firstName; + * std::string lastName; + * + * public: + * // With this member, `Employee` now has a fully built `swap` operation, which will + * // swap all of the listed members. + * auto swap_lens() noexcept { return Alepha::swap_magma( firstName, lastName ); } + * + * // The lifecycle methods of this class could now be implemented in terms of + * // `swap`, instead of manually listing large numbers of members to swap, or + * // calling a swap member which would have to be written. + * }; + *``` + * The primary benefit of using `swap_lens` is that the list of members to swap is not doubled-up, + * and the function signature is simpler. Otherwise this would be the normal approach: + * ``` + * void + * swap( Employee &that ) noexcept + * { + * swap( this->firstName, that.firstName ); + * swap( this->lastName, that.lastName ); + * } + * ``` + * + * The above approach is prone to error, especially when multiple members are of the same type. + * `swap( this->firstName, that.lastName )` is a common mistake, for example. Instead, the + * `swap_lens` permits the members to be listed only once, in a single canonical place. + * + * @note Due to a quirk of template metaprogramming and SFINAE with ADL, `swap_lens` must appear + * before any ADL usage of `swap` in the class which defines it. If `operator =( Self && )` will + * be defined in terms of this swap, `swap_lens` must be defined before `operator =( Self && )`. + */ + + struct swappable {}; + } + + template< typename T > + concept HasMemberSwapLens= + requires( T t ) + { + { std::move( t ).swap_lens() }; + }; + + template< typename T > + concept MemberSwapLensable= Capability< T, swappable > or HasMemberSwapLens< T >; + + template< MemberSwapLensable T > + constexpr decltype( auto ) + swap_lens( T &&item ) noexcept( noexcept( std::forward< T >( item ).swap_lens() ) ) + { + return std::forward< T >( item ).swap_lens(); + } + + template< typename T > + concept SupportsSwapLens= + requires( T t ) + { + { swap_lens( std::move( t ) ) }; + }; + + template< typename T > + concept SwapLensable= Capability< T, swappable > and SupportsSwapLens< T >; + + + // To compute the noexceptness of a swap expression, we + // have to render it as it would be called via ADL. + // + // Thus we make a namespace to guard all this mess + // and then expose the noexceptness of that expression + // as the noexceptness of a different function we can + // name. + namespace check_noexcept + { + using std::swap; + + template< typename T > + constexpr void swap_check( T &&a, T &&b ) noexcept( noexcept( swap( std::forward< T >( a ), std::forward< T >( b ) ) ) ); + } + + template< typename T > + // requires( SwapLensable< T > ) + constexpr void + swap( T &&a, T &&b ) noexcept //noexcept( noexcept( check_noexcept::swap_check( swap_lens( std::forward< T >( a ) ), swap_lens( std::forward< T >( b ) ) ) ) ) + { + using std::swap; + return swap( swap_lens( std::forward< T >( a ) ), swap_lens( std::forward< T >( b ) ) ); + } + + // The swap binding and magma system allows one to specify a group of members (objects) which are suitable for the swap operation. + template< typename ... Args > + struct binding + { + std::tuple< Args... > data; + }; + + // Bindings have a complex swap implementation that recursively calls swap on those elements. + // This is necessary, since `std::tie` built tuples don't have a functioning swap operation. + template< std::size_t depth, typename ... Args > + constexpr void + swap_impl( std::tuple< Args... > &a, std::tuple< Args... > &b ) + noexcept + ( + ( ... & noexcept( check_noexcept::swap_check( std::declval< Args & >(), std::declval< Args & >() ) ) ) + ) + { + using std::swap; + if constexpr( sizeof...( Args ) == depth ) return; + else + { + swap( std::get< depth >( a ), std::get< depth >( b ) ); + return swap_impl< depth + 1 >( a, b ); + } + } + + template< typename ... Args > + constexpr void + swap( binding< Args... > a, binding< Args... > b ) noexcept( noexcept( swap_impl< 0 >( a.data, b.data ) ) ) + { + return swap_impl< 0 >( a.data, b.data ); + } + + namespace exports + { + template< typename ... Args > + constexpr auto + swap_magma( Args && ... args ) noexcept + { + return binding< Args... >{ std::tie( std::forward< Args >( args )... ) }; + } + } +} + +namespace Alepha::Hydrogen::inline exports::inline swappable_m +{ + using namespace detail::swappable_m::exports; +} From 3458c6af9ac0455616ed67c6768da530b96d0470 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 10 Nov 2023 23:27:29 -0500 Subject: [PATCH 7/8] A special case for capabilities. I keep putting of a rewrite here... I need to get to it. --- Capabilities.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Capabilities.h b/Capabilities.h index 92ffcb3..de7a75c 100644 --- a/Capabilities.h +++ b/Capabilities.h @@ -101,6 +101,13 @@ namespace Alepha::Hydrogen return has_cap( Meta::Container::vector< TParams... >{}, cap ); } + template< typename Type, typename Cap > + consteval bool + has_cap( const Meta::type_value< Type > &, Meta::type_value< Cap > ) + { + return false; + } + namespace exports { template< typename T, typename cap > From c78237e8442945dd0515d3bf544e36f72b524610 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 10 Nov 2023 23:29:20 -0500 Subject: [PATCH 8/8] Get Blob and Buffer building and a bit tested --- Blob.h | 82 ++++++++++++++++++++++------------------ Blob.test/0.cc | 25 ++++++++++++ Blob.test/CMakeLists.txt | 1 + Buffer.h | 82 +++++++++++++++++++--------------------- CMakeLists.txt | 1 + 5 files changed, 111 insertions(+), 80 deletions(-) create mode 100644 Blob.test/0.cc create mode 100644 Blob.test/CMakeLists.txt diff --git a/Blob.h b/Blob.h index 987bba1..876cfac 100644 --- a/Blob.h +++ b/Blob.h @@ -2,17 +2,22 @@ static_assert( __cplusplus > 2020'99 ); #pragma once +#include + +#include + #include #include "Buffer.h" #include "swappable.h" -#include "stringify.h" -#include "Exceptions.h" -#include "evaluation_helpers.h" -#include "threading.h" +#include "Exception.h" +//#include "threading.h" #include "error.h" -namespace Alepha::inline Cavorite ::detail:: blob +#include +#include + +namespace Alepha::Hydrogen ::detail:: Blob_m { inline namespace exports { @@ -34,28 +39,29 @@ namespace Alepha::inline Cavorite ::detail:: blob } using std::begin, std::end; + using IOStreams::stringify; class exports::DataCarveTooLargeError - : public virtual OutOfRangeError + : public virtual Buffer_m::OutOfRangeError { public: explicit DataCarveTooLargeError( const void *const location, const std::size_t request, const std::size_t available ) : std::out_of_range( "Tried to carve " + stringify( request ) + " bytes from `Blob` object at location " - + stringify( location ) + " which only has " + stringify( avail ) + " bytes allocated." ), + + stringify( location ) + " which only has " + stringify( available ) + " bytes allocated." ), OutOfRangeError( location, request, available ) {} }; class exports::DataCarveOutOfRangeError - : public virtual OutOfRangeError + : public virtual Buffer_m::OutOfRangeError { public: explicit - DataCarveOutOfRanceError( const void *const location, const std::size_t request, const std::size_t available ) + DataCarveOutOfRangeError( const void *const location, const std::size_t request, const std::size_t available ) : std::out_of_range( "Tried to carve " + stringify( request ) + " bytes from `Blob` object at location " - + stringify( location ) + " which only has " + stringify( avail ) + " bytes allocated." ), + + stringify( location ) + " which only has " + stringify( available ) + " bytes allocated." ), OutOfRangeError( location, request, available ) {} }; @@ -66,25 +72,27 @@ namespace Alepha::inline Cavorite ::detail:: blob private: using IndirectStorage= std::shared_ptr< std::shared_ptr< Blob > >; IndirectStorage storage; // If this is empty, then this `Blob` object doesn't share ownership. This references the shared pool. - Buffer buffer; + Buffer< Mutable > buffer; std::size_t viewLimit= 0; // TODO: Consider allowing for unrooted sub-buffer views? // TODO: Take the `storage` parameter and make it not increment when this ctor is called -- only when the dice roll passes. explicit - Blob( IndirectStorage storage, Buffer buffer ) noexcept - : storage( evaluate <=[storage= std::move( storage )] () -> IndirectBacking + Blob( IndirectStorage storage, Buffer< Mutable > buffer ) noexcept + : storage( Utility::evaluate <=[storage= std::move( storage )] () -> IndirectStorage { - if( fastRandomBits( C::storageSplitRandomBitDepth ) ) return std::move( storage ); - if( C::debugSplitSharing ) error() << "Observed a use count of " << storage.use_count() << " when we failed the dice roll." << std::endl; - auto split= std::make_shared< std::shared_ptr< Blob > >( *storage ); - if( C:: + //if( fastRandomBits( C::storageSplitRandomBitDepth ) ) + return std::move( storage ); + //if( C::debugSplitSharing ) error() << "Observed a use count of " << storage.use_count() << " when we failed the dice roll." << std::endl; + //auto split= std::make_shared< std::shared_ptr< Blob > >( *storage ); + //if( C:: + //return split; }), - buffer( buffer ) + buffer( buffer ), viewLimit( buffer.size() ) {} public: - ~Buffer() { reset(); } + ~Blob() { reset(); } auto swap_lens() noexcept @@ -106,7 +114,7 @@ namespace Alepha::inline Cavorite ::detail:: blob void reset() noexcept { - if( not storage ) delete buffer; + if( not storage ) delete [] buffer.byte_data(); else storage.reset(); buffer= {}; @@ -125,7 +133,7 @@ namespace Alepha::inline Cavorite ::detail:: blob * @note: No data are copied. */ void - reset( const std::size_t ) + reset( const std::size_t size ) { Blob tmp{ size }; swap( tmp, *this ); @@ -133,8 +141,8 @@ namespace Alepha::inline Cavorite ::detail:: blob // Copy deep copies the data. Blob( const Blob © ) - : buffer( new std::byte[ copy.buffer.size() ] ), - viewLimit( copy.viewLimit ) + : buffer( new std::byte[ copy.size() ], copy.size() ), + viewLimit( copy.size() ) { if( C::debugCtors ) error() << "Blob copy invoked." << std::endl; copyData( *this, copy ); @@ -178,13 +186,13 @@ namespace Alepha::inline Cavorite ::detail:: blob explicit Blob( const std::size_t amount ) - : buffer( new std::byte[ amount ]{} ), // The data must be 0'ed upon allocation. + : buffer( new std::byte[ amount ]{}, amount ), // The data must be 0'ed upon allocation. viewLimit( amount ) {} explicit Blob( const Buffer< Const > b ) - : Buffer( b.size() ) + : Blob( b.size() ) { copyData( buffer, b ); } @@ -228,7 +236,7 @@ namespace Alepha::inline Cavorite ::detail:: blob // Now we assume that there's a two-layer scheme, so we operate based upon that. - Blob rv{ storage, Buffer< Mutable >{ buffer, amount } } + Blob rv{ storage, Buffer< Mutable >{ buffer, amount } }; buffer= buffer + amount; viewLimit-= amount; @@ -260,7 +268,7 @@ namespace Alepha::inline Cavorite ::detail:: blob template< typename T > void operator []( T ) const= delete; template< typename T > void operator []( T )= delete; - constexpr std::size_t capacity() const noecept { return buffer.size(); } + constexpr std::size_t capacity() const noexcept { return buffer.size(); } bool isContiguousWith( const Blob &other ) const & noexcept @@ -269,7 +277,7 @@ namespace Alepha::inline Cavorite ::detail:: blob ( storage != nullptr and - *storage == *other.backing + *storage == *other.storage and byte_data() + size() == other.byte_data() ); @@ -331,7 +339,7 @@ namespace Alepha::inline Cavorite ::detail:: blob bool couldConcatenate( const Buffer< Const > buffer ) const noexcept { - return data.size() <= ( capacity() - size() ); + return buffer.size() <= ( capacity() - size() ); } /*! @@ -372,9 +380,9 @@ namespace Alepha::inline Cavorite ::detail:: blob [[nodiscard]] Buffer< constness > concatenate( const Buffer< constness > data ) noexcept { - const auto amount= std::min( capacity - size(), data.size() ); - const DataWindow< const > fitted{ data, amount }; - copyData( buffer, + size(), fitted ); + const auto amount= std::min( capacity() - size(), data.size() ); + const Buffer< Const > fitted{ data, amount }; + copyData( buffer + size(), fitted ); setSize( size() + amount ); return data + amount; } @@ -406,7 +414,7 @@ namespace Alepha::inline Cavorite ::detail:: blob } else { - const auto amount= concatenate( Buffer< Const >{ blob } ).size() + const auto amount= concatenate( Buffer< Const >{ blob } ).size(); const auto rv= blob.carveTail( amount ); blob.reset(); return rv; @@ -482,11 +490,11 @@ namespace Alepha::inline Cavorite ::detail:: blob } }; - static_assert( HasCapability< Blob, swappable > ); - static_assert( detail::swaps::SwapLensable< Blob > ); + //static_assert( Capability< Blob, swappable > ); + //static_assert( detail::swaps::SwapLensable< Blob > ); } -namespace Alepha::Cavorite::inline exports::inline blob +namespace Alepha::Hydrogen::inline exports::inline Blob_m { - using namespace detail::blob::exports; + using namespace detail::Blob_m::exports; } diff --git a/Blob.test/0.cc b/Blob.test/0.cc new file mode 100644 index 0000000..afa887d --- /dev/null +++ b/Blob.test/0.cc @@ -0,0 +1,25 @@ +static_assert( __cplusplus > 2020'99 ); + +#include "../Blob.h" + +#include + +#include + +static auto init= Alepha::Utility::enroll <=[] +{ + using namespace Alepha::Testing::literals::test_literals; + "Simple carve head test"_test <=[] + { + Alepha::Blob b{ 1024 }; + + auto b2= b.carveHead( 256 ); + + assert( b.size() == 768 ); + assert( b2.size() == 256 ); + + std::string h= "Hello world"; + + copyData( b2, Alepha::make_buffer( h ) ); + }; +}; diff --git a/Blob.test/CMakeLists.txt b/Blob.test/CMakeLists.txt new file mode 100644 index 0000000..b099603 --- /dev/null +++ b/Blob.test/CMakeLists.txt @@ -0,0 +1 @@ +unit_test( 0 ) diff --git a/Buffer.h b/Buffer.h index aae3bf6..c3d450d 100644 --- a/Buffer.h +++ b/Buffer.h @@ -2,6 +2,11 @@ static_assert( __cplusplus > 2020'99 ); #pragma once +#include + +#include +#include + #include #include #include @@ -10,19 +15,22 @@ static_assert( __cplusplus > 2020'99 ); #include #include -#include +#include +#include + +#include #include "Concepts.h" #include "assertion.h" #include "Capabilities.h" -#include "lifetime.h" -namespace Alepha::inline Cavorite ::detail:: buffer +namespace Alepha::Hydrogen ::detail:: Buffer_m { inline namespace exports {} using namespace std::literals::string_literals; + using IOStreams::stringify; namespace exports { @@ -42,7 +50,7 @@ namespace Alepha::inline Cavorite ::detail:: buffer : baseAddress( address ), requestedSize( requestedSize ), availableSize( availableSize ) {} - public + public: const void *getAddress() const noexcept { return baseAddress; } const std::size_t getRequestedSize() const noexcept { return requestedSize; } const std::size_t getAvailableSize() const noexcept { return availableSize; } @@ -62,20 +70,20 @@ namespace Alepha::inline Cavorite ::detail:: buffer : std::out_of_range( "Tried to access an object of type "s + type.name() + " which is " + stringify( requestedSize ) + " bytes in size. " + "The request was at location " + stringify( location ) + " which only has " + stringify( availableSize ) + " bytes allocated" ), - OutOfRangeError( location, requestedSIze, availableSize ), + OutOfRangeError( location, requestedSize, availableSize ), typeID( type ) {} }; class OutOfRangeSizeError - : virtual public OutOfRangeException + : virtual public OutOfRangeError { public: explicit - OutOfRangeSizeException( const void *const location, const std::ptrdiff_t requestedOffset, const std::size_t availableSpace ) + OutOfRangeSizeError( const void *const location, const std::ptrdiff_t requestedOffset, const std::size_t availableSpace ) : std::out_of_range( "Tried to view a byte offset of " + stringify( requestedOffset ) + " into location " + stringify( location ) + " which is " + stringify( availableSpace ) + " bytes in size." ), - OutOfRangeException( location, requestedOffset, availableSpace ) + OutOfRangeError( location, requestedOffset, availableSpace ) {} }; @@ -106,26 +114,31 @@ namespace Alepha::inline Cavorite ::detail:: buffer constexpr Buffer( const pointer_type ptr, const std::size_t bytes ) noexcept - : ptr( static_cast< byte_pointer_type >( ptr ), bytes( bytes ) + : ptr( static_cast< byte_pointer_type >( ptr ) ), bytes( bytes ) {} constexpr Buffer( const Buffer< Mutable > © ) noexcept : ptr( copy.byte_data() ), bytes( copy.size() ) {} + template< Constness constness_= constness > + requires( constness_ == Mutable ) constexpr - Buffer( const Buffer< Const > © ) noexcept requires( Constness == Mutable )= delete; + Buffer( const Buffer< Const > © ) noexcept = delete; constexpr byte_pointer_type byte_data() const noexcept { return ptr; } constexpr pointer_type data() const noexcept { return ptr; } - constexpr const_byte_pointer_type byte_data() const noexcept { return ptr; } - constexpr const_pointer_type data() const noexcept { return ptr; } + constexpr const_byte_pointer_type const_byte_data() const noexcept { return ptr; } + constexpr const_pointer_type const_data() const noexcept { return ptr; } constexpr std::size_t size() const noexcept { return bytes; } constexpr bool empty() const noexcept { return size() == 0; } + constexpr byte_pointer_type begin() const noexcept { return byte_data(); } + constexpr byte_pointer_type end() const noexcept { return begin() + size(); } + constexpr const_byte_pointer_type cbegin() const noexcept { return begin(); } constexpr const_byte_pointer_type cend() const noexcept { return end(); } @@ -137,7 +150,7 @@ namespace Alepha::inline Cavorite ::detail:: buffer as( std::nothrow_t ) const noexcept { assertion( sizeof( T ) <= bytes ); - return *start_lifetime_as< std::add_lvalue_reference_t< maybe_const_t< T, constness > > >( ptr ); + return *Alepha::start_lifetime_as< std::add_lvalue_reference_t< maybe_const_t< T, constness > > >( ptr ); } template< typename T > @@ -153,7 +166,7 @@ namespace Alepha::inline Cavorite ::detail:: buffer const_as( std::nothrow_t ) const noexcept { assertion( sizeof( T ) <= bytes ); - return *start_lifetime_as< std::add_const_t< T > >( ptr ); + return *Alepha::start_lifetime_as< std::add_const_t< T > >( ptr ); } template< typename T > @@ -222,7 +235,7 @@ namespace Alepha::inline Cavorite ::detail:: buffer struct BufferModel_capability {}; template< typename T > - concept UndecayedBufferModelable= HasCapability< T, BufferModel_capability >; + concept UndecayedBufferModelable= Capability< T, BufferModel_capability >; template< typename T > concept BufferModelable= UndecayedBufferModelable< std::decay_t< T > >; @@ -234,8 +247,8 @@ namespace Alepha::inline Cavorite ::detail:: buffer constexpr auto &crtp() noexcept { return static_cast< Derived & >( *this ); } constexpr const auto &crtp() const noexcept { return static_cast< const Derived & >( *this ); } - constexpr auto &buffer() { return static_cast< Buffer< Mutable > >( crtp() ); } - constexpr const auto &buffer() const { return static_cast< Buffer< Const > >( crtp() ); } + constexpr auto buffer() { return static_cast< Buffer< Mutable > >( crtp() ); } + constexpr auto buffer() const { return static_cast< Buffer< Const > >( crtp() ); } public: constexpr auto byte_data() { return buffer().byte_data(); } @@ -273,8 +286,8 @@ namespace Alepha::inline Cavorite ::detail:: buffer template< typename T > extern Constness constness_of_v; - template< UndecayedBufferModelble T > - constexpr Constness constness_of_v= std::is_const_v< T >; + template< UndecayedBufferModelable T > + constexpr Constness constness_of_v< T >{ std::is_const_v< T > }; template< Constness constness > constexpr Constness constness_of_v< Buffer< constness > >{ constness }; @@ -283,7 +296,7 @@ namespace Alepha::inline Cavorite ::detail:: buffer constexpr auto operator + ( const Buffer< constness > buffer, const std::size_t offset ) { - if( offset > buffer.size() ) throw OutOfRangeError{ buffer.data(), offset, buffer.size() }; + if( offset > buffer.size() ) throw OutOfRangeSizeError{ buffer.data(), std::ptrdiff_t( offset ), buffer.size() }; return Buffer< constness >{ buffer.byte_data() + offset, buffer.size() - offset }; } @@ -372,23 +385,6 @@ namespace Alepha::inline Cavorite ::detail:: buffer } - template< Concepts::StandardLayout T, std::size_t size > - constexpr Buffer< Mutable > - make_buffer( T array[ size ] ) noexcept - { - // TODO: Do we need to consider overflow here? - return { array, sizeof( array ) }; - } - - template< Concepts::StandardLayout T, std::size_t size > - constexpr Buffer< Const > - make_buffer( const T array[ size ] ) noexcept - { - // TODO: Do we need to consider overflow here? - return { array, sizeof( array ) }; - } - - inline Buffer< Mutable > make_buffer( std::string &string ) noexcept { @@ -407,7 +403,7 @@ namespace Alepha::inline Cavorite ::detail:: buffer { if( source.size() > destination.size() ) throw InsufficientSizeError{ destination.data(), source.size(), destination.size(), typeid( std::byte ) }; - ::memcpy( destination, source, source.size() ); + std::memcpy( destination, source, source.size() ); return { destination, source.size() }; } @@ -419,13 +415,13 @@ namespace Alepha::inline Cavorite ::detail:: buffer namespace exports { - using detail::buffer::make_buffer; + using detail::Buffer_m::make_buffer; } } -namespace Alepha::Cavorite::inline exports::inline buffer +namespace Alepha::Hydrogen::inline exports::inline Buffer_m { - using namespace detail::buffer::exports; + using namespace detail::Buffer_m::exports; } /* @@ -453,8 +449,8 @@ namespace Alepha::Cavorite::inline exports::inline buffer template<> constexpr auto -std::cbegin( const ::Alepha::Cavorite::Buffer< Alepha::Cavorite::Mutable > &range ) -> decltype( range.begin() )= delete; +std::cbegin( const ::Alepha::Hydrogen::Buffer< Alepha::Hydrogen::Mutable > &range ) -> decltype( range.begin() )= delete; template<> constexpr auto -std::cend( const ::Alepha::Cavorite::Buffer< Alepha::Cavorite::Mutable > &range ) -> decltype( range.end() )= delete; +std::cend( const ::Alepha::Hydrogen::Buffer< Alepha::Hydrogen::Mutable > &range ) -> decltype( range.end() )= delete; diff --git a/CMakeLists.txt b/CMakeLists.txt index 4b46b65..e873c3c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,7 @@ add_subdirectory( tuplize_args.test ) add_subdirectory( Thread.test ) add_subdirectory( assertion.test ) add_subdirectory( Constness.test ) +add_subdirectory( Blob.test ) # Sample applications add_executable( example example.cc )