forked from Alepha/Alepha
Merge branch 'master' of /Sources/Repositories/Alepha
This commit is contained in:
82
Blob.h
82
Blob.h
@ -2,17 +2,22 @@ static_assert( __cplusplus > 2020'99 );
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <Alepha/Alepha.h>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "Buffer.h"
|
#include "Buffer.h"
|
||||||
#include "swappable.h"
|
#include "swappable.h"
|
||||||
#include "stringify.h"
|
#include "Exception.h"
|
||||||
#include "Exceptions.h"
|
//#include "threading.h"
|
||||||
#include "evaluation_helpers.h"
|
|
||||||
#include "threading.h"
|
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
|
|
||||||
namespace Alepha::inline Cavorite ::detail:: blob
|
#include <Alepha/IOStreams/String.h>
|
||||||
|
#include <Alepha/Utility/evaluation_helpers.h>
|
||||||
|
|
||||||
|
namespace Alepha::Hydrogen ::detail:: Blob_m
|
||||||
{
|
{
|
||||||
inline namespace exports
|
inline namespace exports
|
||||||
{
|
{
|
||||||
@ -34,28 +39,29 @@ namespace Alepha::inline Cavorite ::detail:: blob
|
|||||||
}
|
}
|
||||||
|
|
||||||
using std::begin, std::end;
|
using std::begin, std::end;
|
||||||
|
using IOStreams::stringify;
|
||||||
|
|
||||||
class exports::DataCarveTooLargeError
|
class exports::DataCarveTooLargeError
|
||||||
: public virtual OutOfRangeError
|
: public virtual Buffer_m::OutOfRangeError
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit
|
explicit
|
||||||
DataCarveTooLargeError( const void *const location, const std::size_t request, const std::size_t available )
|
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 "
|
: 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 )
|
OutOfRangeError( location, request, available )
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class exports::DataCarveOutOfRangeError
|
class exports::DataCarveOutOfRangeError
|
||||||
: public virtual OutOfRangeError
|
: public virtual Buffer_m::OutOfRangeError
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit
|
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 "
|
: 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 )
|
OutOfRangeError( location, request, available )
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
@ -66,25 +72,27 @@ namespace Alepha::inline Cavorite ::detail:: blob
|
|||||||
private:
|
private:
|
||||||
using IndirectStorage= std::shared_ptr< std::shared_ptr< Blob > >;
|
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.
|
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?
|
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.
|
// TODO: Take the `storage` parameter and make it not increment when this ctor is called -- only when the dice roll passes.
|
||||||
explicit
|
explicit
|
||||||
Blob( IndirectStorage storage, Buffer buffer ) noexcept
|
Blob( IndirectStorage storage, Buffer< Mutable > buffer ) noexcept
|
||||||
: storage( evaluate <=[storage= std::move( storage )] () -> IndirectBacking
|
: storage( Utility::evaluate <=[storage= std::move( storage )] () -> IndirectStorage
|
||||||
{
|
{
|
||||||
if( fastRandomBits( C::storageSplitRandomBitDepth ) ) return std::move( storage );
|
//if( fastRandomBits( C::storageSplitRandomBitDepth ) )
|
||||||
if( C::debugSplitSharing ) error() << "Observed a use count of " << storage.use_count() << " when we failed the dice roll." << std::endl;
|
return std::move( storage );
|
||||||
auto split= std::make_shared< std::shared_ptr< Blob > >( *storage );
|
//if( C::debugSplitSharing ) error() << "Observed a use count of " << storage.use_count() << " when we failed the dice roll." << std::endl;
|
||||||
if( C::
|
//auto split= std::make_shared< std::shared_ptr< Blob > >( *storage );
|
||||||
|
//if( C::
|
||||||
|
//return split;
|
||||||
}),
|
}),
|
||||||
buffer( buffer )
|
buffer( buffer ),
|
||||||
viewLimit( buffer.size() )
|
viewLimit( buffer.size() )
|
||||||
{}
|
{}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~Buffer() { reset(); }
|
~Blob() { reset(); }
|
||||||
|
|
||||||
auto
|
auto
|
||||||
swap_lens() noexcept
|
swap_lens() noexcept
|
||||||
@ -106,7 +114,7 @@ namespace Alepha::inline Cavorite ::detail:: blob
|
|||||||
void
|
void
|
||||||
reset() noexcept
|
reset() noexcept
|
||||||
{
|
{
|
||||||
if( not storage ) delete buffer;
|
if( not storage ) delete [] buffer.byte_data();
|
||||||
else storage.reset();
|
else storage.reset();
|
||||||
|
|
||||||
buffer= {};
|
buffer= {};
|
||||||
@ -125,7 +133,7 @@ namespace Alepha::inline Cavorite ::detail:: blob
|
|||||||
* @note: No data are copied.
|
* @note: No data are copied.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
reset( const std::size_t )
|
reset( const std::size_t size )
|
||||||
{
|
{
|
||||||
Blob tmp{ size };
|
Blob tmp{ size };
|
||||||
swap( tmp, *this );
|
swap( tmp, *this );
|
||||||
@ -133,8 +141,8 @@ namespace Alepha::inline Cavorite ::detail:: blob
|
|||||||
|
|
||||||
// Copy deep copies the data.
|
// Copy deep copies the data.
|
||||||
Blob( const Blob © )
|
Blob( const Blob © )
|
||||||
: buffer( new std::byte[ copy.buffer.size() ] ),
|
: buffer( new std::byte[ copy.size() ], copy.size() ),
|
||||||
viewLimit( copy.viewLimit )
|
viewLimit( copy.size() )
|
||||||
{
|
{
|
||||||
if( C::debugCtors ) error() << "Blob copy invoked." << std::endl;
|
if( C::debugCtors ) error() << "Blob copy invoked." << std::endl;
|
||||||
copyData( *this, copy );
|
copyData( *this, copy );
|
||||||
@ -178,13 +186,13 @@ namespace Alepha::inline Cavorite ::detail:: blob
|
|||||||
|
|
||||||
explicit
|
explicit
|
||||||
Blob( const std::size_t amount )
|
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 )
|
viewLimit( amount )
|
||||||
{}
|
{}
|
||||||
|
|
||||||
explicit
|
explicit
|
||||||
Blob( const Buffer< Const > b )
|
Blob( const Buffer< Const > b )
|
||||||
: Buffer( b.size() )
|
: Blob( b.size() )
|
||||||
{
|
{
|
||||||
copyData( buffer, b );
|
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.
|
// 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;
|
buffer= buffer + amount;
|
||||||
viewLimit-= 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 ) const= delete;
|
||||||
template< typename T > void operator []( T )= 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
|
bool
|
||||||
isContiguousWith( const Blob &other ) const & noexcept
|
isContiguousWith( const Blob &other ) const & noexcept
|
||||||
@ -269,7 +277,7 @@ namespace Alepha::inline Cavorite ::detail:: blob
|
|||||||
(
|
(
|
||||||
storage != nullptr
|
storage != nullptr
|
||||||
and
|
and
|
||||||
*storage == *other.backing
|
*storage == *other.storage
|
||||||
and
|
and
|
||||||
byte_data() + size() == other.byte_data()
|
byte_data() + size() == other.byte_data()
|
||||||
);
|
);
|
||||||
@ -331,7 +339,7 @@ namespace Alepha::inline Cavorite ::detail:: blob
|
|||||||
bool
|
bool
|
||||||
couldConcatenate( const Buffer< Const > buffer ) const noexcept
|
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 >
|
[[nodiscard]] Buffer< constness >
|
||||||
concatenate( const Buffer< constness > data ) noexcept
|
concatenate( const Buffer< constness > data ) noexcept
|
||||||
{
|
{
|
||||||
const auto amount= std::min( capacity - size(), data.size() );
|
const auto amount= std::min( capacity() - size(), data.size() );
|
||||||
const DataWindow< const > fitted{ data, amount };
|
const Buffer< Const > fitted{ data, amount };
|
||||||
copyData( buffer, + size(), fitted );
|
copyData( buffer + size(), fitted );
|
||||||
setSize( size() + amount );
|
setSize( size() + amount );
|
||||||
return data + amount;
|
return data + amount;
|
||||||
}
|
}
|
||||||
@ -406,7 +414,7 @@ namespace Alepha::inline Cavorite ::detail:: blob
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const auto amount= concatenate( Buffer< Const >{ blob } ).size()
|
const auto amount= concatenate( Buffer< Const >{ blob } ).size();
|
||||||
const auto rv= blob.carveTail( amount );
|
const auto rv= blob.carveTail( amount );
|
||||||
blob.reset();
|
blob.reset();
|
||||||
return rv;
|
return rv;
|
||||||
@ -482,11 +490,11 @@ namespace Alepha::inline Cavorite ::detail:: blob
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert( HasCapability< Blob, swappable > );
|
//static_assert( Capability< Blob, swappable > );
|
||||||
static_assert( detail::swaps::SwapLensable< Blob > );
|
//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;
|
||||||
}
|
}
|
||||||
|
|||||||
25
Blob.test/0.cc
Normal file
25
Blob.test/0.cc
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
|
#include "../Blob.h"
|
||||||
|
|
||||||
|
#include <Alepha/Testing/test.h>
|
||||||
|
|
||||||
|
#include <Alepha/Utility/evaluation_helpers.h>
|
||||||
|
|
||||||
|
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 ) );
|
||||||
|
};
|
||||||
|
};
|
||||||
1
Blob.test/CMakeLists.txt
Normal file
1
Blob.test/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
unit_test( 0 )
|
||||||
82
Buffer.h
82
Buffer.h
@ -2,6 +2,11 @@ static_assert( __cplusplus > 2020'99 );
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <Alepha.h>
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <array>
|
#include <array>
|
||||||
@ -10,19 +15,22 @@ static_assert( __cplusplus > 2020'99 );
|
|||||||
#include <exception>
|
#include <exception>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
#include <boost/lexical_cast.hpp>
|
#include <Alepha/Constness.h>
|
||||||
|
#include <Alepha/lifetime.h>
|
||||||
|
|
||||||
|
#include <Alepha/IOStreams/String.h>
|
||||||
|
|
||||||
#include "Concepts.h"
|
#include "Concepts.h"
|
||||||
#include "assertion.h"
|
#include "assertion.h"
|
||||||
#include "Capabilities.h"
|
#include "Capabilities.h"
|
||||||
#include "lifetime.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace Alepha::inline Cavorite ::detail:: buffer
|
namespace Alepha::Hydrogen ::detail:: Buffer_m
|
||||||
{
|
{
|
||||||
inline namespace exports {}
|
inline namespace exports {}
|
||||||
|
|
||||||
using namespace std::literals::string_literals;
|
using namespace std::literals::string_literals;
|
||||||
|
using IOStreams::stringify;
|
||||||
|
|
||||||
namespace exports
|
namespace exports
|
||||||
{
|
{
|
||||||
@ -42,7 +50,7 @@ namespace Alepha::inline Cavorite ::detail:: buffer
|
|||||||
: baseAddress( address ), requestedSize( requestedSize ), availableSize( availableSize )
|
: baseAddress( address ), requestedSize( requestedSize ), availableSize( availableSize )
|
||||||
{}
|
{}
|
||||||
|
|
||||||
public
|
public:
|
||||||
const void *getAddress() const noexcept { return baseAddress; }
|
const void *getAddress() const noexcept { return baseAddress; }
|
||||||
const std::size_t getRequestedSize() const noexcept { return requestedSize; }
|
const std::size_t getRequestedSize() const noexcept { return requestedSize; }
|
||||||
const std::size_t getAvailableSize() const noexcept { return availableSize; }
|
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. "
|
: 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 )
|
+ "The request was at location " + stringify( location ) + " which only has " + stringify( availableSize )
|
||||||
+ " bytes allocated" ),
|
+ " bytes allocated" ),
|
||||||
OutOfRangeError( location, requestedSIze, availableSize ),
|
OutOfRangeError( location, requestedSize, availableSize ),
|
||||||
typeID( type )
|
typeID( type )
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
class OutOfRangeSizeError
|
class OutOfRangeSizeError
|
||||||
: virtual public OutOfRangeException
|
: virtual public OutOfRangeError
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit
|
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 )
|
: std::out_of_range( "Tried to view a byte offset of " + stringify( requestedOffset ) + " into location " + stringify( location )
|
||||||
+ " which is " + stringify( availableSpace ) + " bytes in size." ),
|
+ " 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
|
constexpr
|
||||||
Buffer( const pointer_type ptr, const std::size_t bytes ) noexcept
|
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
|
constexpr
|
||||||
Buffer( const Buffer< Mutable > © ) noexcept
|
Buffer( const Buffer< Mutable > © ) noexcept
|
||||||
: ptr( copy.byte_data() ), bytes( copy.size() ) {}
|
: ptr( copy.byte_data() ), bytes( copy.size() ) {}
|
||||||
|
|
||||||
|
template< Constness constness_= constness >
|
||||||
|
requires( constness_ == Mutable )
|
||||||
constexpr
|
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 byte_pointer_type byte_data() const noexcept { return ptr; }
|
||||||
constexpr pointer_type 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_byte_pointer_type const_byte_data() const noexcept { return ptr; }
|
||||||
constexpr const_pointer_type 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 std::size_t size() const noexcept { return bytes; }
|
||||||
constexpr bool empty() const noexcept { return size() == 0; }
|
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 cbegin() const noexcept { return begin(); }
|
||||||
constexpr const_byte_pointer_type cend() const noexcept { return end(); }
|
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
|
as( std::nothrow_t ) const noexcept
|
||||||
{
|
{
|
||||||
assertion( sizeof( T ) <= bytes );
|
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 >
|
template< typename T >
|
||||||
@ -153,7 +166,7 @@ namespace Alepha::inline Cavorite ::detail:: buffer
|
|||||||
const_as( std::nothrow_t ) const noexcept
|
const_as( std::nothrow_t ) const noexcept
|
||||||
{
|
{
|
||||||
assertion( sizeof( T ) <= bytes );
|
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 >
|
template< typename T >
|
||||||
@ -222,7 +235,7 @@ namespace Alepha::inline Cavorite ::detail:: buffer
|
|||||||
struct BufferModel_capability {};
|
struct BufferModel_capability {};
|
||||||
|
|
||||||
template< typename T >
|
template< typename T >
|
||||||
concept UndecayedBufferModelable= HasCapability< T, BufferModel_capability >;
|
concept UndecayedBufferModelable= Capability< T, BufferModel_capability >;
|
||||||
|
|
||||||
template< typename T >
|
template< typename T >
|
||||||
concept BufferModelable= UndecayedBufferModelable< std::decay_t< 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 auto &crtp() noexcept { return static_cast< Derived & >( *this ); }
|
||||||
constexpr const auto &crtp() const noexcept { return static_cast< const Derived & >( *this ); }
|
constexpr const auto &crtp() const noexcept { return static_cast< const Derived & >( *this ); }
|
||||||
|
|
||||||
constexpr auto &buffer() { return static_cast< Buffer< Mutable > >( crtp() ); }
|
constexpr auto buffer() { return static_cast< Buffer< Mutable > >( crtp() ); }
|
||||||
constexpr const auto &buffer() const { return static_cast< Buffer< Const > >( crtp() ); }
|
constexpr auto buffer() const { return static_cast< Buffer< Const > >( crtp() ); }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
constexpr auto byte_data() { return buffer().byte_data(); }
|
constexpr auto byte_data() { return buffer().byte_data(); }
|
||||||
@ -273,8 +286,8 @@ namespace Alepha::inline Cavorite ::detail:: buffer
|
|||||||
template< typename T >
|
template< typename T >
|
||||||
extern Constness constness_of_v;
|
extern Constness constness_of_v;
|
||||||
|
|
||||||
template< UndecayedBufferModelble T >
|
template< UndecayedBufferModelable T >
|
||||||
constexpr Constness constness_of_v= std::is_const_v< T >;
|
constexpr Constness constness_of_v< T >{ std::is_const_v< T > };
|
||||||
|
|
||||||
template< Constness constness >
|
template< Constness constness >
|
||||||
constexpr Constness constness_of_v< Buffer< constness > >{ constness };
|
constexpr Constness constness_of_v< Buffer< constness > >{ constness };
|
||||||
@ -283,7 +296,7 @@ namespace Alepha::inline Cavorite ::detail:: buffer
|
|||||||
constexpr auto
|
constexpr auto
|
||||||
operator + ( const Buffer< constness > buffer, const std::size_t offset )
|
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 };
|
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 >
|
inline Buffer< Mutable >
|
||||||
make_buffer( std::string &string ) noexcept
|
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 ) };
|
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() };
|
return { destination, source.size() };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -419,13 +415,13 @@ namespace Alepha::inline Cavorite ::detail:: buffer
|
|||||||
|
|
||||||
namespace exports
|
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<>
|
template<>
|
||||||
constexpr auto
|
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<>
|
template<>
|
||||||
constexpr auto
|
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;
|
||||||
|
|||||||
@ -33,6 +33,9 @@ add_subdirectory( word_wrap.test )
|
|||||||
add_subdirectory( string_algorithms.test )
|
add_subdirectory( string_algorithms.test )
|
||||||
add_subdirectory( tuplize_args.test )
|
add_subdirectory( tuplize_args.test )
|
||||||
add_subdirectory( Thread.test )
|
add_subdirectory( Thread.test )
|
||||||
|
add_subdirectory( assertion.test )
|
||||||
|
add_subdirectory( Constness.test )
|
||||||
|
add_subdirectory( Blob.test )
|
||||||
|
|
||||||
# Sample applications
|
# Sample applications
|
||||||
add_executable( example example.cc )
|
add_executable( example example.cc )
|
||||||
|
|||||||
@ -101,6 +101,13 @@ namespace Alepha::Hydrogen
|
|||||||
return has_cap( Meta::Container::vector< TParams... >{}, cap );
|
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
|
namespace exports
|
||||||
{
|
{
|
||||||
template< typename T, typename cap >
|
template< typename T, typename cap >
|
||||||
|
|||||||
@ -11,7 +11,7 @@ static_assert( __cplusplus > 2020'99 );
|
|||||||
#include "meta.h"
|
#include "meta.h"
|
||||||
#include "function_traits.h"
|
#include "function_traits.h"
|
||||||
|
|
||||||
namespace Alepha::Hydrogen ::detail:: core_concepts
|
namespace Alepha::Hydrogen ::detail:: Concepts_m
|
||||||
{
|
{
|
||||||
inline namespace exports
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
152
Constness.h
Normal file
152
Constness.h
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
static_assert( __cplusplus > 2020'00 );
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Alepha/Alepha.h>
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
3
Constness.test/0.cc
Normal file
3
Constness.test/0.cc
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
|
#include "../Constness.h"
|
||||||
1
Constness.test/CMakeLists.txt
Normal file
1
Constness.test/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
unit_test( 0 )
|
||||||
@ -277,6 +277,8 @@ namespace Alepha::Hydrogen ::detail:: exceptions
|
|||||||
|
|
||||||
~Violation() override { if( not active ) abort(); }
|
~Violation() override { if( not active ) abort(); }
|
||||||
|
|
||||||
|
Violation()= default;
|
||||||
|
|
||||||
Violation( const Violation © )= delete;
|
Violation( const Violation © )= delete;
|
||||||
Violation( Violation © ) : active( copy.active ) { copy.active= false; }
|
Violation( Violation © ) : active( copy.active ) { copy.active= false; }
|
||||||
};
|
};
|
||||||
|
|||||||
30
assertion.h
Normal file
30
assertion.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Alepha/Alepha.h>
|
||||||
|
|
||||||
|
#include <Alepha/Exception.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
4
assertion.test/0.cc
Normal file
4
assertion.test/0.cc
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
|
#include "../assertion.h"
|
||||||
|
|
||||||
1
assertion.test/CMakeLists.txt
Normal file
1
assertion.test/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
unit_test( 0 )
|
||||||
39
lifetime.h
Normal file
39
lifetime.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
static_assert( __cplusplus > 2020'00 );
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Alepha/Alepha.h>
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
173
swappable.h
Normal file
173
swappable.h
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
static_assert( __cplusplus > 2020'00 );
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Alepha/Alepha.h>
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user