diff --git a/Alepha.h b/Alepha.h
index cad2af7..2781b4d 100644
--- a/Alepha.h
+++ b/Alepha.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Atomic/Mailbox.h b/Atomic/Mailbox.h
new file mode 100644
index 0000000..93733c5
--- /dev/null
+++ b/Atomic/Mailbox.h
@@ -0,0 +1,193 @@
+/*!
+ * @file Mailbox.h
+ * @brief Class which abstracts a "mailbox" metaphor, for threaded programming
+ *
+ * Copyright (C) 2010 Alepha Library. All rights reserved.
+ * @author ADAM David Alan Martin
+ */
+
+#ifndef ALEPHA_MAILBOX_HEADER
+#define ALEPHA_MAILBOX_HEADER
+
+#include
+
+#include
+
+#include
+#include
+
+namespace Alepha
+{
+ namespace Atomic
+ {
+ /*!
+ * @brief The Mailbox class implements a mailbox metaphor. Just like postal service mailboxes,
+ * the mailbox class lets users add items to the box from a producer thread or group of threads,
+ * and in a (single, for now) consumer thread, take the entire contents of the mailbox out, and
+ * work with them.
+ *
+ * @tparam Item The Item type which is used in the mailbox containers.
+ *
+ * @note Mailboxes, for now, only support one consumer, and any number of producers.
+ *
+ * @invariant Mailboxes give out their contents in the order placed in.
+ *
+ * The Mailbox primitive is implemented as two "containers" which are swapped periodically by the
+ * consumer, and continually filled by the producers. This swapping keeps the identity in principle,
+ * but allows the user to access the contents of the "other side", in practice. The two mailbox
+ * containers are: "preparation" and "filled". The consumer takes a whole "filled" mailbox out, and
+ * replaces his current mailbox into the system for use as the new "preparation" box. The producer
+ * continually inserts mail into the "preparation" mailbox until full. The consumer clears his mailbox
+ * when returning it.
+ */
+ template< typename Item >
+ class Mailbox
+ {
+ private:
+ /*! @brief The maximum number of "mail" items which can be placed into a "preparation" mailbox */
+ const size_t boxLimit;
+
+ const size_t minSwapFill;
+
+ boost::mutex mailboxAccess;
+
+ boost::condition boxFull;
+ boost::condition boxEmpty;
+
+ std::vector< Item > preparing;
+ std::vector< Item > filled;
+
+ bool finished;
+ bool terminated;
+
+ bool
+ testTerminated() const
+ {
+ if( terminated ) throw Alepha::TerminatedException();
+ return true;
+ }
+
+ bool
+ testFinished() const
+ {
+ if( finished ) throw Alepha::FinishedException();
+ return true;
+ }
+
+ public:
+ explicit inline
+ Mailbox( const size_t lim, const size_t min= 1 )
+ : boxLimit( lim ), minSwapFill( min ), finished( false ), terminated( false )
+ {
+ this->preparing.reserve( boxLimit );
+ this->filled.reserve( boxLimit );
+ }
+
+ /*!
+ * @brief This function will give back the "now-current" preparation mailbox, and check out the current
+ * mail as the filled mailbox.
+ * @returns A reference to the current filled mailbox.
+ *
+ * @throw ClosureException When a closure event is encountered. (Any ClosureException derived type, see
+ * the ClosureException schedule for why these events get thrown.)
+ *
+ * @pre The mailbox must have some items in it. This is established for you by the internal locking system.
+ * @post The preparing mailbox is cleared, and the filled mailbox has the new mail.
+ * @invariant The filled mailbox will have some items in it, or a Alepha::ClosureException will be thrown.
+ * @invariant This function is not threadsafe in the presence of multiple consumers (fetchers).
+ */
+ std::vector< Item > &
+ fetchItems()
+ {
+ boost::this_thread::interruption_point();
+ /* Our mailbox is done, so let's empty it. (Don't hold the lock during this, to reduce contention.) */
+ this->filled.clear();
+
+ ALEPHA_TEMPLATE_NAMED_USAGE( lock, this->mailboxAccess )
+ {
+ /* Make sure that there's mail we can get. */
+ while( this->testTerminated() && ( this->preparing.size() < minSwapFill ) )
+ //while( ( this->testTerminated() ) && ( this->preparing.empty() ) )
+ {
+ //this->testFinished();
+ if( this->finished && !this->preparing.empty() )
+ {
+ break;
+ }
+ this->testFinished();
+ this->boxFull.wait( lock );
+ }
+ assert( !this->preparing.empty() );
+
+ /* Exchange our mailbox with the preparation box. And give it back. */
+ std::swap( this->filled, this->preparing );
+ this->boxEmpty.notify_all();
+ }
+ return this->filled;
+ }
+
+ /*!
+ * @brief This function will add mail to the mailbox.
+ *
+ * @throw ClosureException When a closure event is encountered. (Any ClosureException derived type, see
+ * the ClosureException schedule for why these events get thrown.)
+ *
+ * @pre The mailbox must have room. This is established for you by the internal locking system.
+ * @post The preparing mailbox will have "item" as the last item added to this box.
+ * @invariant The mailbox will not be allowed to become overfull.
+ */
+ void
+ push_back( const Item &item )
+ {
+ boost::this_thread::interruption_point();
+ ALEPHA_TEMPLATE_NAMED_USAGE( lock, this->mailboxAccess )
+ {
+ /* Make sure we can put the mail in */
+ while( ( this->testTerminated() ) && ( this->preparing.size() == this->boxLimit ) )
+ {
+ this->boxEmpty.wait( lock );
+ }
+ assert( !this->preparing.size() < this->boxLimit );
+
+ /* Put the item into the mailbox. */
+ this->preparing.push_back( item );
+ this->boxFull.notify_one();
+ }
+ }
+
+ /*!
+ * @brief This function will close the mailbox to any new mail, and deliver a closure
+ * event to the other side.
+ *
+ * @pre The mailbox must not be finished.
+ * @pre The mailbox will be put into the finished state.
+ */
+ void
+ close()
+ {
+ ALEPHA_TEMPLATE_SCOPED_USAGE( this->mailboxAccess )
+ {
+ this->finished= true;
+ this->boxFull.notify_all();
+ }
+ }
+
+ void
+ terminate()
+ {
+ ALEPHA_TEMPLATE_SCOPED_USAGE( this->mailboxAccess )
+ {
+ this->terminated= true;
+ this->boxEmpty.notify_all();
+ this->boxFull.notify_all();
+ }
+ }
+ };
+ }
+}
+
+#endif /*** ALEPHA_MAILBOX_HEADER ***/
+
+/*
+ * vim:ts=3:sw=3:sts=3:sta:et:ft=cpp
+ */
diff --git a/AutoRAII.h b/AutoRAII.h
index 03509db..b6bbd12 100644
--- a/AutoRAII.h
+++ b/AutoRAII.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/AutoRAII.test/0.cc b/AutoRAII.test/0.cc
index 0abaf92..f278399 100644
--- a/AutoRAII.test/0.cc
+++ b/AutoRAII.test/0.cc
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#include
diff --git a/Blob.h b/Blob.h
index edb17e1..876cfac 100644
--- a/Blob.h
+++ b/Blob.h
@@ -1,18 +1,23 @@
-static_assert( __cplusplus > 2020'00 );
+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/Utility/StackableStreambuf.test/CMakeLists.txt b/Blob.test/CMakeLists.txt
similarity index 100%
rename from Utility/StackableStreambuf.test/CMakeLists.txt
rename to Blob.test/CMakeLists.txt
diff --git a/Buffer.h b/Buffer.h
index bce25ca..c3d450d 100644
--- a/Buffer.h
+++ b/Buffer.h
@@ -1,7 +1,12 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
+#include
+
+#include
+#include
+
#include
#include
#include
@@ -10,19 +15,22 @@ static_assert( __cplusplus > 2020'00 );
#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 7d24654..8d68e48 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,6 +3,7 @@ project( Alepha )
include( cmake/rules.cmake )
+link_libraries( pthread )
# The core alepha library:
@@ -31,6 +32,10 @@ add_subdirectory( Exception.test )
add_subdirectory( word_wrap.test )
add_subdirectory( string_algorithms.test )
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 )
diff --git a/Capabilities.h b/Capabilities.h
index ad7944d..de7a75c 100644
--- a/Capabilities.h
+++ b/Capabilities.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
@@ -78,6 +78,7 @@ namespace Alepha::Hydrogen
has_cap_in_capability_base( const Left &, Meta::type_value< Cap > cap )
{
throw "Unevaluated";
+ return false;
}
template< typename Cap, typename ... TParams >
@@ -100,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 >
diff --git a/Concepts.h b/Concepts.h
index 2804055..6c2c19c 100644
--- a/Concepts.h
+++ b/Concepts.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
@@ -11,7 +11,7 @@ static_assert( __cplusplus > 2020'00 );
#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;
}
diff --git a/Console.cc b/Console.cc
index 9110a9c..254ab6d 100644
--- a/Console.cc
+++ b/Console.cc
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#include "Console.h"
@@ -13,9 +13,9 @@ static_assert( __cplusplus > 2020'00 );
#include
#include
-#include
#include
+#include
#include "Enum.h"
#include "ProgramOptions.h"
@@ -348,8 +348,7 @@ namespace Alepha::Hydrogen ::detail:: console_m
struct Console::Impl
{
int fd;
- // TODO: Do we want to make this not gnu libstdc++ specific?
- __gnu_cxx::stdio_filebuf< char > filebuf;
+ IOStreams::OutUnixFileBuf filebuf;
std::ostream stream;
std::stack< std::pair< struct termios, ConsoleMode > > modeStack;
ConsoleMode mode= cooked;
@@ -357,7 +356,7 @@ namespace Alepha::Hydrogen ::detail:: console_m
explicit
Impl( const int fd )
- : fd( fd ), filebuf( fd, std::ios::out ), stream( &filebuf )
+ : fd( fd ), filebuf( fd ), stream( &filebuf )
{}
};
diff --git a/Console.h b/Console.h
index 3bd5597..0530f62 100644
--- a/Console.h
+++ b/Console.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/ConstexprString.h b/ConstexprString.h
index 4316937..ab12897 100644
--- a/ConstexprString.h
+++ b/ConstexprString.h
@@ -1,7 +1,9 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
+#include
+
#include
#include
@@ -9,9 +11,7 @@ static_assert( __cplusplus > 2020'00 );
#include
#include
-#include
-
-namespace Alepha::Cavorite ::detail:: constexpr_string
+namespace Alepha::Hydrogen ::detail:: ConstexprString_m
{
namespace C
{
@@ -134,12 +134,12 @@ namespace Alepha::Cavorite ::detail:: constexpr_string
}
}
-namespace Alepha::Cavorite::inline exports::inline constexpr_string
+namespace Alepha::Hydrogen::inline exports::inline ConstexprString_m
{
- using namespace detail::constexpr_string::exports;
+ using namespace detail::ConstexprString_m::exports;
}
-namespace Alepha::Cavorite::inline exports::inline literals::inline constexpr_string_literals
+namespace Alepha::Hydrogen::inline exports::inline literals::inline constexpr_string_literals
{
- using namespace detail::constexpr_string::exports::literals;
+ using namespace ConstexprString_m::literals;
}
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 )
diff --git a/DataChain.h b/DataChain.h
index 90f0aca..b7216f5 100644
--- a/DataChain.h
+++ b/DataChain.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Enum.h b/Enum.h
index e8c4c35..ab3ce67 100644
--- a/Enum.h
+++ b/Enum.h
@@ -1,7 +1,9 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
+#include
+
#include
#include
@@ -17,7 +19,7 @@ static_assert( __cplusplus > 2020'00 );
#include "ConstexprString.h"
#include "meta.h"
-namespace Alepha::inline Cavorite ::detail:: enhanced_enum
+namespace Alepha::Hydrogen ::detail:: Enum_m
{
inline namespace exports {}
@@ -246,12 +248,12 @@ namespace Alepha::inline Cavorite ::detail:: enhanced_enum
}
}
-namespace Alepha::Cavorite::inline exports::inline enhanced_enum
+namespace Alepha::Hydrogen::inline exports::inline Enum_m
{
- using namespace detail::enhanced_enum::exports;
+ using namespace detail::Enum_m::exports;
}
-namespace Alepha::Cavorite::inline exports::inline literals::inline enum_literals
+namespace Alepha::Hydrogen::inline exports::inline literals::inline enum_literals
{
- using namespace detail::enhanced_enum::exports::literals;
+ using namespace Enum_m::literals;
}
diff --git a/Exception.h b/Exception.h
index 7584bfa..e65c5e9 100644
--- a/Exception.h
+++ b/Exception.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
@@ -277,6 +277,8 @@ namespace Alepha::Hydrogen ::detail:: Exception_m
~Violation() override { if( not active ) abort(); }
+ Violation()= default;
+
Violation( const Violation © )= delete;
Violation( Violation © ) : active( copy.active ) { copy.active= false; }
};
diff --git a/Exception.test/exception.cc b/Exception.test/exception.cc
index a9fc834..d988a3e 100644
--- a/Exception.test/exception.cc
+++ b/Exception.test/exception.cc
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#include
diff --git a/Format b/Format
new file mode 100644
index 0000000..98a3eeb
--- /dev/null
+++ b/Format
@@ -0,0 +1,388 @@
+Basic Code Formatting Rules:
+
+0) Like most things in Alepha, these rules are "strong guidelines". When in doubt, these rules
+form a baseline of good current practice. However, there's no substitute for good judgement.
+Therefore, a large degree of formatting latitude is afforded to code. The primary value
+being sought is clarity, not consistency. Therefore, new stylistic forms are constantly being
+adopted for specific cases -- it is cumbersome (at best) to keep track of all of them here.
+
+Reason: Format should convey meaning. It's fine to invent new conventions. Documenting them is
+good too, but it shouldn't be cumbersome to do so -- thus this file itself will always be an
+incomplete representation of the format.
+
+Exceptions: Certain core guidelines are universally required, like tabs.
+
+1) Alepha code uses tab character ('\t', ASCII:0x09) for indentation. One tab per indent level. Tabs
+are not to be used for alignment, except with other tabs. Tabs have no expected set width.
+
+Reason: Tabs permit customization of code indent level to a reader's taste via the tabwidth settings
+of editors.
+
+Exceptions: None. The tab character as _indent only_ permits cooperation amongst those with different
+visual preferences.
+
+2) Semicolons on all lines shall appear immediately after the last syntactic character, without a separating space,
+immediately followed by a newline (or trailing comment). It is permissible put a semicolon on its own line to
+facilitate a block structure where appropriate for the language.
+
+Examples:
+
+++i;
+f( a, b );
+
+const bool value= true
+ or a
+ or b
+;
+
+3) Declare only one variable per "line".
+
+Examples:
+
+int x;
+float *y;
+int &xr= x;
+
+Reason: C++ declaration grammar inherits ambiguities and oddities from C. By declaring one variable per line, we sidestep this issue.
+
+Exceptions: None.
+
+
+4) All bracing constructs, "(), {}, [], and <>", must have a space between the opening brace and the first
+argument, and between the last argument and the closing brace. If there are no arguments then there should
+be no spaces between the opening and closing brace. No space shall exist between a keyword or an entity name
+and the following opening bracing construct. A newline character counts as a space for these constructs,
+thus permitting block-like structure.
+
+Examples:
+
+if( someFunction( argument ) ) return 1; // Good. Notice that there is no trailing space after the "if"
+std::vector< std::map< std::string, std::set< std::string > > > complexDataEntity; // Even for templates
+complexDataEntity[ 10 ][ "Hello "].find( "Something" ); // Even for array-like operations.
+std::cout << [](){ return "Hello World!"; }() << std::endl; // For one-lining lambdas and initializers, the rule applies.
+for( int i= 0; i < 10; ++i ) std::cout << i << std::endl; // Even within a loop construct.
+
+Reason: Extra space can be visually helpful. Many styles encourage visual cues to code flow.
+
+
+5) All 2-argument operators that do not modify the left hand side (binary-function operators) must have a space
+on both sides of the operator, whereas data-modifying equals-based operators like ' =, +=, -=, *=, /=, ^=, |=,
+&=, ~=, <<=, >>=, %= ' shall have no space to their left, thus binding them to the expression that they modify.
+Note that ' !=, <=, >= ' are not assignment-like operators, but a comparison operators. Comma "operators"
+shall also follow left-hand no spacing rules, as this matches their natural use in other circumstances.
+
+Examples:
+
+int a= 10;
+int b= 11, c= 12;
+c= ( a * b + c ) / ( a * 22 );
+a%= c / b;
+if( ( a != b ) or ( c < a ) && ( a= b ) ) return true;
+
+Reason: Extra visual space helps. Cuddling the assignment operators helps distinguish them from comparison
+operators. "<=" vs "<<=" and "==" vs "=". By having extra code-intent presented in a side-channel (spacing),
+it helps prevent accidental typos and bugs.
+
+Exceptions: `<=` when used as a "left fat arrow" operator may be used with the spacing rules that most
+make sense for their context. `<=[]` on lambdas, for example. `/` when used as a directory separator
+between path components from `std::filesystem` should also omit surrounding spaces.
+
+
+6) All unary operators shall "cuddle" their applied variable. Pointers and references shall be be declared
+with the "&" or "*" attached to the variable name.
+
+Examples:
+
+int a= 10;
+++a;
+a--;
+a= ~a;
+if( !a ) return false;
+
+Reason: Somewhat improves readability. For declarations, although "int &" is the type of "int &x;", this
+does not extend to: "int &x, y". Those are distinct types, but by banning multiple declarations per line,
+we mostly avoid this issue.
+
+
+7) Brace placement shall be "Allman/BSD"-like. Opening braces shall be on a new line, closing braces on a
+new line. Braces shall be indented by the same level as the line of their "control" statement. A braced-block
+that is not subordinate to a control statement shall be indented as if it had an "if( true )" control statement
+above it. A do-while loop shall have the while on its own line, after the closing brace, where possible. Note
+that one-line compaction is permitted, as is omitting braces for fully subsumed blocks.
+
+Examples:
+
+int
+main()
+{
+ int x= 10;
+ int y= 11;
+ if( x < y )
+ {
+ std::vector< int > v;
+ if( y % 2 )
+ {
+ rv.push_back( v );
+ }
+
+ // Note that this if block has no parent, but it is indented like it has one.
+ {
+ std::list< int > l( v.begin(), v.end() );
+ if( x == y ) for( const auto &x: stuff )
+ {
+ std::cout << x << std::endl;
+ }
+ std::cout << l.size() << std::endl;;
+ }
+
+ do
+ {
+ std::cout << v.size() << std::endl;
+ }
+ while( false );
+
+ }
+
+ return 0;
+}
+
+Exceptions: Very small functions or blocks can be subsumed into one line, for brevity.
+
+8) Return types shall be placed above the name of the function in definitions, but on the same line for
+declarations. inline or explicit for ctors, dtors, and conversion functions shall follow this rule as well.
+
+Example:
+
+bool is_even( int x );
+
+bool
+is_even( int x )
+{
+ return ~x & 1;
+}
+
+
+9) Template clauses for definitions shall be on their own line. For declarations, a separate line is optional.
+
+Examples:
+
+template< typename T > bool is_even( T x );
+
+template< typename T >
+bool
+is_even( T x )
+{
+ return ( x % 2 ) == 0;
+}
+
+
+10) In class definitions, public, protected, and private regions shall be indented beyond the access control
+keywords, which must also be indented. In switch/case blocks, cases shall be similarly single-indented,
+as too breaks shall remain single indented. All switch statements shall have a break, return, or throw
+at the end of each case clause or shall use [[fallthrough]] to document fallthrough behavior, where
+it is used. Default clauses are not mandatory, but are recommended. (Default clauses might be blank,
+or all cases might be covered, especially when the argument is an enum.)
+
+Examples:
+
+class Foobar
+{
+ private:
+ int x;
+
+ public:
+ void setX( int newx ) { x= newx; }
+ int getX() const { return x }
+
+ void
+ operation( int val )
+ {
+ switch( val )
+ {
+ case 2:
+ x= 11;
+ break;
+
+ case 11:
+ x= 2;
+ break;
+
+ case 12:
+ specialCase();
+ [[fallthrough]];
+
+ default:
+ x= val;
+ break;
+ }
+ }
+};
+
+struct Open
+{
+ int x;
+};
+
+
+11) Continued lines shall be indented twice past the main parent's indent. When breaking a line on an operator,
+the next line should begin with an operator. Spaces may also be used to line up nicely for vertical alignment
+of operators; however, note that spacing alignment starts from the tab-depth of the previous line. (Never use
+spaces to align to a specific tab depth.) Control structures which have a single subordinate statement
+on a separate line shall enclose that line in braces. If wrapping becomes necessary with "one line" control
+structures, then bracing is required.
+
+Example:
+
+int
+main()
+{
+ std::cout << "A really long message which "
+ << "needed to be indented "
+ << "across multiple lines." << std::endl;
+
+ std::cout << "A really long message which "
+ << "needed to be indented "
+ << "across multiple lines. "
+ << "This message was aligned "
+ << "on ostream operators."
+ << std::endl;
+
+ if( condition ) action();
+
+ if( moreComplicatedCondition )
+ {
+ moreComplicatedAction();
+ }
+}
+
+Reasons: It is important to know when lines have wrapped. A specific visually distinct
+style helps make this stand out from other indented scenarios. By avoiding braceless
+subordinates to control structures we avoid a class of silly maintenance bugs.
+
+
+12) Initializer lists shall be treated as-if they're part of the ctor body. They get
+single indented, unless the function is really short.
+
+Example:
+
+struct StringWrapper
+{
+ std::string string;
+
+ explicit
+ StringWrapper( const std::string &s )
+ : string( s ) {}
+
+ explicit StringWrapper() : string( "no value" ) {} // A short ctor or other function can be on one-line.
+};
+
+Exceptions: Single line functions and ctors, and the like are sometimes good to conserve space. Trivial
+implementations should take trivial amounts of space.
+
+
+13) Private data shall be declared before the functionality of the class is declared or defined. Constructors,
+copy assignment, and destructors shall be declared (or defined) before other member functions.
+
+Example:
+
+template< typename T >
+class smart_ptr : boost::noncopyable
+{
+ private:
+ T *ptr;
+
+ public:
+ explicit inline
+ smart_ptr( T *const p )
+ : ptr( p ) {}
+
+ inline ~smart_ptr() { delete p; }
+
+ T &
+ operator *() const
+ {
+ return *ptr;
+ }
+
+ T *
+ operator->() const
+ {
+ return ptr;
+ }
+};
+
+Reasons: It's easier to reason about the correctness of a class's primary invariant mechanism (construction
+and destruction) when its near the variables and types it manages. Knowing the contents of a class helps to
+understand about the invariants which must be maintained. Some argue this makes finding the public interfaces
+harder -- but Hera classes are designed to be perused by doxygen, and thus this documentation shall serve as
+a primary public usage reference. Within Hera, the only reason to read a header file is to understand more
+about a particular implementation.
+
+Exceptions: None.
+
+
+14) Class parents (inheritance) shall logically be on the same line as the declaration. If on another line,
+must count as a continuation.
+
+Reasons: It helps to isolate a class's name from the parent list, which is (when you think about it) part of
+the class, and needs indentation.
+
+
+15) Preprocessor nested scopes (which sometimes do happen) shall indent, by requisite spacing, all characters
+AFTER the leading "#".
+
+Examples:
+
+#ifndef SOME_THING
+# ifndef SOME_OTHER_THING
+# define NEITHER_THING_IS_AVAILABLE
+# else
+# error Must have SOME_THING if SOME_OTHER_THING is around
+# endif
+#endif
+
+Reasons: Because C preprocessor macros also follow logical scope, it helps to trace their nesting by indentation.
+By indenting after the leading "#", we help distinguish C++ code from C preprocessor code.
+
+Exceptions: The header guards, being ubiquitous, shall not count as a level of indentation in nested preprocessor
+construct
+
+
+16) In declaration of entities, the following order of keywords shall always be followed:
+
+virtual explicit extern static constexpr consteval constinit inline mutable const volatile
+
+For integers & double:
+unsigned long long int
+signed short int
+long double
+
+signed and int are each optional.
+
+For characters:
+unsigned char
+signed char
+char
+
+Each of those char types are three distinct types -- signed or unsigned are NOT optional.
+
+
+Examples:
+struct Type
+{
+ virtual inline ~Type() noexcept {} // Yes, this is actually allowed.
+ explicit inline Type() {}
+ static inline const std::string string() { return "String"; }
+
+ // Allowed, but violates the style guide:
+ long const unsigned volatile int virtual long inline *resource() const;
+
+ // The same declaration, following the style guide:
+ virtual inline const volatile unsigned long long int *resource() const;
+};
+
+Reasons: C++ is very forgiving in the order of its type-and-qualifiers, but standardizing them makes finding
+specific things simpler. There are many mutually exclusive keywords above, like static and virtual and explicit,
+for example. Many may not know it but 'long const unsigned volatile int virtual long inline *' is a valid token
+sequence for declaring a member function. Standardization gives us: 'virtual inline const volatile unsigned
+long long int *', which makes the declaration type far less confusing.
diff --git a/IOStreams/CMakeLists.txt b/IOStreams/CMakeLists.txt
index c3ed932..21d3f4d 100644
--- a/IOStreams/CMakeLists.txt
+++ b/IOStreams/CMakeLists.txt
@@ -1,3 +1,10 @@
add_subdirectory( IStreamable.test )
add_subdirectory( OStreamable.test )
add_subdirectory( streamable.test )
+add_subdirectory( OutUnixFileBuf.test )
+add_subdirectory( StackableStreambuf.test )
+
+target_sources( alepha PRIVATE
+ StackableStreambuf.cc
+)
+
diff --git a/IOStreams/IStreamable.h b/IOStreams/IStreamable.h
index d9a1c32..28c69f4 100644
--- a/IOStreams/IStreamable.h
+++ b/IOStreams/IStreamable.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/IOStreams/IStreamable.test/0.cc b/IOStreams/IStreamable.test/0.cc
index a1d7096..57d03ab 100644
--- a/IOStreams/IStreamable.test/0.cc
+++ b/IOStreams/IStreamable.test/0.cc
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#include "../IStreamable.h"
diff --git a/IOStreams/OStreamable.h b/IOStreams/OStreamable.h
index fb91b26..6f8b658 100644
--- a/IOStreams/OStreamable.h
+++ b/IOStreams/OStreamable.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/IOStreams/OStreamable.test/0.cc b/IOStreams/OStreamable.test/0.cc
index cffc0c7..8f1828a 100644
--- a/IOStreams/OStreamable.test/0.cc
+++ b/IOStreams/OStreamable.test/0.cc
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#include "../OStreamable.h"
diff --git a/IOStreams/OutUnixFileBuf.h b/IOStreams/OutUnixFileBuf.h
new file mode 100644
index 0000000..1827953
--- /dev/null
+++ b/IOStreams/OutUnixFileBuf.h
@@ -0,0 +1,47 @@
+static_assert( __cplusplus > 2020'99 );
+
+#pragma once
+
+#include
+
+#include
+
+#include
+
+namespace Alepha::Hydrogen::IOStreams ::detail:: OutUnixFileBuf_m
+{
+ inline namespace exports
+ {
+ class OutUnixFileBuf;
+ }
+
+ class exports::OutUnixFileBuf
+ : virtual public std::streambuf
+ {
+ private:
+ int fd;
+
+ int
+ overflow( const int ch_ ) override
+ {
+ if( ch_ == EOF ) throw std::runtime_error{ "Unexpected EOF" };
+ const char ch= ch_;
+ xsputn( &ch, 1 );
+ return ch_;
+ }
+
+ std::streamsize
+ xsputn( const char *const data, const std::streamsize amt ) override
+ {
+ return ::write( fd, data, amt );
+ }
+
+ public:
+ explicit OutUnixFileBuf( const int fd ) : fd( fd ) {}
+ };
+}
+
+namespace Alepha::Hydrogen::IOStreams::inline exports::inline OutUnixFileBuf_m
+{
+ using namespace detail::OutUnixFileBuf_m::exports;
+}
diff --git a/IOStreams/OutUnixFileBuf.test/0.cc b/IOStreams/OutUnixFileBuf.test/0.cc
new file mode 100644
index 0000000..d5ec5ae
--- /dev/null
+++ b/IOStreams/OutUnixFileBuf.test/0.cc
@@ -0,0 +1,28 @@
+static_assert( __cplusplus > 2020'99 );
+
+#include "../OutUnixFileBuf.h"
+
+#include
+#include
+
+#include
+#include
+
+#include
+
+auto init= Alepha::Utility::enroll <=[]
+{
+ using namespace Alepha::Testing::exports;
+
+ "Can we write to /dev/null?"_test <=[]
+ {
+ const auto fd= Alepha::AutoRAII
+ {
+ []{ return open( "/dev/null", O_WRONLY ); },
+ ::close
+ };
+
+ Alepha::IOStreams::OutUnixFileBuf buf{ fd };
+ std::ostream file{ &buf };
+ };
+};
diff --git a/IOStreams/OutUnixFileBuf.test/CMakeLists.txt b/IOStreams/OutUnixFileBuf.test/CMakeLists.txt
new file mode 100644
index 0000000..b099603
--- /dev/null
+++ b/IOStreams/OutUnixFileBuf.test/CMakeLists.txt
@@ -0,0 +1 @@
+unit_test( 0 )
diff --git a/Utility/StackableStreambuf.cc b/IOStreams/StackableStreambuf.cc
similarity index 83%
rename from Utility/StackableStreambuf.cc
rename to IOStreams/StackableStreambuf.cc
index 44e61bd..c7efdab 100644
--- a/Utility/StackableStreambuf.cc
+++ b/IOStreams/StackableStreambuf.cc
@@ -1,17 +1,25 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#include "StackableStreambuf.h"
-namespace Alepha::Hydrogen::Utility::detail::stackable_streambuf
+#include
+
+namespace Alepha::Hydrogen::IOStreams::detail::stackable_streambuf
{
namespace
{
const auto index= std::ios::xalloc();
- inline auto &
+ auto *&
+ getStackPtr( std::ios_base &ios )
+ {
+ return reinterpret_cast< std::stack< std::unique_ptr< StackableStreambuf > > *& >( ios.pword( index ) );
+ }
+
+ auto &
getStack( std::ios_base &ios )
{
- auto &ownership= reinterpret_cast< std::stack< std::unique_ptr< StackableStreambuf > > *& >( ios.pword( index ) );
+ auto &ownership= getStackPtr( ios );
if( not ownership ) ownership= new std::decay_t< decltype( *ownership ) >{};
return *ownership;
@@ -24,6 +32,7 @@ namespace Alepha::Hydrogen::Utility::detail::stackable_streambuf
{
auto &ownership= getStack( ios );
+ assert( not ownership.empty() );
// Since it's owned, delete happens at scope exit.
const std::unique_ptr top= std::move( ownership.top() );
ownership.pop();
@@ -48,9 +57,8 @@ namespace Alepha::Hydrogen::Utility::detail::stackable_streambuf
inline void
releaseStack( std::ios_base &ios )
{
- auto &os= dynamic_cast< std::ostream & >( ios );
-
- while( releaseTop( os ) );
+ delete getStackPtr( ios );
+ getStackPtr( ios )= nullptr;
}
void
diff --git a/Utility/StackableStreambuf.h b/IOStreams/StackableStreambuf.h
similarity index 85%
rename from Utility/StackableStreambuf.h
rename to IOStreams/StackableStreambuf.h
index eacf827..5dfd60f 100644
--- a/Utility/StackableStreambuf.h
+++ b/IOStreams/StackableStreambuf.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
@@ -10,7 +10,7 @@ static_assert( __cplusplus > 2020'00 );
#include
#include
-namespace Alepha::Hydrogen::Utility ::detail:: stackable_streambuf
+namespace Alepha::Hydrogen::IOStreams ::detail:: stackable_streambuf
{
inline namespace exports
{
@@ -63,7 +63,7 @@ namespace Alepha::Hydrogen::Utility ::detail:: stackable_streambuf
}
}
-namespace Alepha::Hydrogen::Utility::inline exports::inline stackable_streambuf
+namespace Alepha::Hydrogen::IOStreams::inline exports::inline stackable_streambuf
{
using namespace detail::stackable_streambuf::exports;
}
diff --git a/Utility/StackableStreambuf.test/0.cc b/IOStreams/StackableStreambuf.test/0.cc
similarity index 95%
rename from Utility/StackableStreambuf.test/0.cc
rename to IOStreams/StackableStreambuf.test/0.cc
index 388e2dc..69d6a37 100644
--- a/Utility/StackableStreambuf.test/0.cc
+++ b/IOStreams/StackableStreambuf.test/0.cc
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#include "../StackableStreambuf.h"
diff --git a/IOStreams/StackableStreambuf.test/CMakeLists.txt b/IOStreams/StackableStreambuf.test/CMakeLists.txt
new file mode 100644
index 0000000..b099603
--- /dev/null
+++ b/IOStreams/StackableStreambuf.test/CMakeLists.txt
@@ -0,0 +1 @@
+unit_test( 0 )
diff --git a/IOStreams/StreamState.h b/IOStreams/StreamState.h
index e2d7e77..f9d247a 100644
--- a/IOStreams/StreamState.h
+++ b/IOStreams/StreamState.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/IOStreams/String.h b/IOStreams/String.h
index 7bf8a96..6a51c80 100644
--- a/IOStreams/String.h
+++ b/IOStreams/String.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/IOStreams/delimiters.h b/IOStreams/delimiters.h
index 8cffc76..a2f8f49 100644
--- a/IOStreams/delimiters.h
+++ b/IOStreams/delimiters.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/IOStreams/streamable.h b/IOStreams/streamable.h
index 197dc4e..b30d9f1 100644
--- a/IOStreams/streamable.h
+++ b/IOStreams/streamable.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/IOStreams/streamable.test/0.cc b/IOStreams/streamable.test/0.cc
index 28f3269..53b56f2 100644
--- a/IOStreams/streamable.test/0.cc
+++ b/IOStreams/streamable.test/0.cc
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#include "../streamable.h"
diff --git a/LICENSE b/LICENSE
index 36b7cd9..41dc232 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,23 +1,3 @@
-Boost Software License - Version 1.0 - August 17th, 2003
-
-Permission is hereby granted, free of charge, to any person or organization
-obtaining a copy of the software and accompanying documentation covered by
-this license (the "Software") to use, reproduce, display, distribute,
-execute, and transmit the Software, and to prepare derivative works of the
-Software, and to permit third-parties to whom the Software is furnished to
-do so, all subject to the following:
-
-The copyright notices in the Software and this entire statement, including
-the above license grant, this restriction and the following disclaimer,
-must be included in all copies of the Software, in whole or in part, and
-all derivative works of the Software, unless such copies or derivative
-works are solely in the form of machine-executable object code generated by
-a source language processor.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
-SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
-FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
+This Source Code Form is subject to the terms of the Mozilla Public
+License, v. 2.0. If a copy of the MPL was not distributed with this
+file, You can obtain one at http://mozilla.org/MPL/2.0/.
diff --git a/MPL-LICENSE.txt b/MPL-LICENSE.txt
new file mode 100644
index 0000000..a612ad9
--- /dev/null
+++ b/MPL-LICENSE.txt
@@ -0,0 +1,373 @@
+Mozilla Public License Version 2.0
+==================================
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+ means each individual or legal entity that creates, contributes to
+ the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+ means the combination of the Contributions of others (if any) used
+ by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+ means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+ means Source Code Form to which the initial Contributor has attached
+ the notice in Exhibit A, the Executable Form of such Source Code
+ Form, and Modifications of such Source Code Form, in each case
+ including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+ means
+
+ (a) that the initial Contributor has attached the notice described
+ in Exhibit B to the Covered Software; or
+
+ (b) that the Covered Software was made available under the terms of
+ version 1.1 or earlier of the License, but not also under the
+ terms of a Secondary License.
+
+1.6. "Executable Form"
+ means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+ means a work that combines Covered Software with other material, in
+ a separate file or files, that is not Covered Software.
+
+1.8. "License"
+ means this document.
+
+1.9. "Licensable"
+ means having the right to grant, to the maximum extent possible,
+ whether at the time of the initial grant or subsequently, any and
+ all of the rights conveyed by this License.
+
+1.10. "Modifications"
+ means any of the following:
+
+ (a) any file in Source Code Form that results from an addition to,
+ deletion from, or modification of the contents of Covered
+ Software; or
+
+ (b) any new file in Source Code Form that contains any Covered
+ Software.
+
+1.11. "Patent Claims" of a Contributor
+ means any patent claim(s), including without limitation, method,
+ process, and apparatus claims, in any patent Licensable by such
+ Contributor that would be infringed, but for the grant of the
+ License, by the making, using, selling, offering for sale, having
+ made, import, or transfer of either its Contributions or its
+ Contributor Version.
+
+1.12. "Secondary License"
+ means either the GNU General Public License, Version 2.0, the GNU
+ Lesser General Public License, Version 2.1, the GNU Affero General
+ Public License, Version 3.0, or any later versions of those
+ licenses.
+
+1.13. "Source Code Form"
+ means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+ means an individual or a legal entity exercising rights under this
+ License. For legal entities, "You" includes any entity that
+ controls, is controlled by, or is under common control with You. For
+ purposes of this definition, "control" means (a) the power, direct
+ or indirect, to cause the direction or management of such entity,
+ whether by contract or otherwise, or (b) ownership of more than
+ fifty percent (50%) of the outstanding shares or beneficial
+ ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+ Licensable by such Contributor to use, reproduce, make available,
+ modify, display, perform, distribute, and otherwise exploit its
+ Contributions, either on an unmodified basis, with Modifications, or
+ as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+ for sale, have made, import, and otherwise transfer either its
+ Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+ or
+
+(b) for infringements caused by: (i) Your and any other third party's
+ modifications of Covered Software, or (ii) the combination of its
+ Contributions with other software (except as part of its Contributor
+ Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+ its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+ Form, as described in Section 3.1, and You must inform recipients of
+ the Executable Form how they can obtain a copy of such Source Code
+ Form by reasonable means in a timely manner, at a charge no more
+ than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+ License, or sublicense it under different terms, provided that the
+ license for the Executable Form does not attempt to limit or alter
+ the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+* *
+* 6. Disclaimer of Warranty *
+* ------------------------- *
+* *
+* Covered Software is provided under this License on an "as is" *
+* basis, without warranty of any kind, either expressed, implied, or *
+* statutory, including, without limitation, warranties that the *
+* Covered Software is free of defects, merchantable, fit for a *
+* particular purpose or non-infringing. The entire risk as to the *
+* quality and performance of the Covered Software is with You. *
+* Should any Covered Software prove defective in any respect, You *
+* (not any Contributor) assume the cost of any necessary servicing, *
+* repair, or correction. This disclaimer of warranty constitutes an *
+* essential part of this License. No use of any Covered Software is *
+* authorized under this License except under this disclaimer. *
+* *
+************************************************************************
+
+************************************************************************
+* *
+* 7. Limitation of Liability *
+* -------------------------- *
+* *
+* Under no circumstances and under no legal theory, whether tort *
+* (including negligence), contract, or otherwise, shall any *
+* Contributor, or anyone who distributes Covered Software as *
+* permitted above, be liable to You for any direct, indirect, *
+* special, incidental, or consequential damages of any character *
+* including, without limitation, damages for lost profits, loss of *
+* goodwill, work stoppage, computer failure or malfunction, or any *
+* and all other commercial damages or losses, even if such party *
+* shall have been informed of the possibility of such damages. This *
+* limitation of liability shall not apply to liability for death or *
+* personal injury resulting from such party's negligence to the *
+* extent applicable law prohibits such limitation. Some *
+* jurisdictions do not allow the exclusion or limitation of *
+* incidental or consequential damages, so this exclusion and *
+* limitation may not apply to You. *
+* *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+ This Source Code Form is "Incompatible With Secondary Licenses", as
+ defined by the Mozilla Public License, v. 2.0.
diff --git a/Meta/Container/string.h b/Meta/Container/string.h
index 368495e..d6e7360 100644
--- a/Meta/Container/string.h
+++ b/Meta/Container/string.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Meta/Container/vector.h b/Meta/Container/vector.h
index 860e4b1..16881ac 100644
--- a/Meta/Container/vector.h
+++ b/Meta/Container/vector.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
@@ -69,6 +69,7 @@ namespace Alepha::Hydrogen::Meta::Container
};
template< typename MetaFunction, typename Arg1, typename First, typename ... Members >
+ requires( sizeof...( Members ) != 0 )
constexpr decltype( auto )
invoke_call( MetaFunction func, type_value< Arg1 > arg1, dereferenced_iterator< vector< First, Members... > > deref )
{
diff --git a/Meta/Meta.test/traits.cc b/Meta/Meta.test/traits.cc
index cfc687a..6a03c81 100644
--- a/Meta/Meta.test/traits.cc
+++ b/Meta/Meta.test/traits.cc
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#include
#include
diff --git a/Meta/dep_value.h b/Meta/dep_value.h
index fbd1f55..f825a53 100644
--- a/Meta/dep_value.h
+++ b/Meta/dep_value.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Meta/find.h b/Meta/find.h
index 94e7da8..d83448d 100644
--- a/Meta/find.h
+++ b/Meta/find.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Meta/functional.h b/Meta/functional.h
index 7b0da36..78c30d1 100644
--- a/Meta/functional.h
+++ b/Meta/functional.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Meta/is_deque.h b/Meta/is_deque.h
index c3193a6..69f17a9 100644
--- a/Meta/is_deque.h
+++ b/Meta/is_deque.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Meta/is_forward_list.h b/Meta/is_forward_list.h
index 70b23e5..30b2518 100644
--- a/Meta/is_forward_list.h
+++ b/Meta/is_forward_list.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Meta/is_functional.h b/Meta/is_functional.h
index 09d7b8c..1715142 100644
--- a/Meta/is_functional.h
+++ b/Meta/is_functional.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Meta/is_list.h b/Meta/is_list.h
index 244eeaf..2d3fb88 100644
--- a/Meta/is_list.h
+++ b/Meta/is_list.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Meta/is_map.h b/Meta/is_map.h
index cf2f8be..98f1660 100644
--- a/Meta/is_map.h
+++ b/Meta/is_map.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Meta/is_optional.h b/Meta/is_optional.h
index 37e11ee..b7459c2 100644
--- a/Meta/is_optional.h
+++ b/Meta/is_optional.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
@@ -18,8 +18,8 @@ namespace Alepha::Hydrogen::Meta
template< typename T >
struct is_optional : std::false_type {};
- template< typename T, typename ... Args >
- struct is_optional< std::optional< T, Args... > > : std::true_type {};
+ template< typename T >
+ struct is_optional< std::optional< T > > : std::true_type {};
template< typename T >
constexpr bool is_optional_v= is_optional< T >::value;
diff --git a/Meta/is_pair.h b/Meta/is_pair.h
index 7a33c57..98c4521 100644
--- a/Meta/is_pair.h
+++ b/Meta/is_pair.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Meta/is_product_type.h b/Meta/is_product_type.h
index 949af2a..6458e60 100644
--- a/Meta/is_product_type.h
+++ b/Meta/is_product_type.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Meta/is_sequence.h b/Meta/is_sequence.h
index abe3f14..acb4ce9 100644
--- a/Meta/is_sequence.h
+++ b/Meta/is_sequence.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Meta/is_set.h b/Meta/is_set.h
index d852fc0..7a194f6 100644
--- a/Meta/is_set.h
+++ b/Meta/is_set.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Meta/is_streamable.h b/Meta/is_streamable.h
index d8a141e..3de33bf 100644
--- a/Meta/is_streamable.h
+++ b/Meta/is_streamable.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Meta/is_string.h b/Meta/is_string.h
index 81b4e3e..cb2267b 100644
--- a/Meta/is_string.h
+++ b/Meta/is_string.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Meta/is_tuple.h b/Meta/is_tuple.h
index 3857242..da82690 100644
--- a/Meta/is_tuple.h
+++ b/Meta/is_tuple.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Meta/is_vector.h b/Meta/is_vector.h
index 78c2961..8658f3c 100644
--- a/Meta/is_vector.h
+++ b/Meta/is_vector.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Meta/overload.h b/Meta/overload.h
index a83c5b8..a48feae 100644
--- a/Meta/overload.h
+++ b/Meta/overload.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Meta/pair_decay.h b/Meta/pair_decay.h
index 29eb8f7..5dd00f4 100644
--- a/Meta/pair_decay.h
+++ b/Meta/pair_decay.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Meta/product_type_decay.h b/Meta/product_type_decay.h
index ac5246e..0cf6196 100644
--- a/Meta/product_type_decay.h
+++ b/Meta/product_type_decay.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Meta/sequence_kind.h b/Meta/sequence_kind.h
index 4d0f5b6..d5206fa 100644
--- a/Meta/sequence_kind.h
+++ b/Meta/sequence_kind.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Meta/tuple_decay.h b/Meta/tuple_decay.h
index 0946f9d..102ad71 100644
--- a/Meta/tuple_decay.h
+++ b/Meta/tuple_decay.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Meta/type_algorithm.h b/Meta/type_algorithm.h
index 8a71530..e61b918 100644
--- a/Meta/type_algorithm.h
+++ b/Meta/type_algorithm.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Meta/type_traits.h b/Meta/type_traits.h
index 9aa3249..06680d7 100644
--- a/Meta/type_traits.h
+++ b/Meta/type_traits.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Meta/type_value.h b/Meta/type_value.h
index 3bfaf73..cf08ef1 100644
--- a/Meta/type_value.h
+++ b/Meta/type_value.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Mockination/MockCondition.h b/Mockination/MockCondition.h
index bf54321..5fa9325 100644
--- a/Mockination/MockCondition.h
+++ b/Mockination/MockCondition.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Mockination/MockFunction.h b/Mockination/MockFunction.h
index f9714cb..a589f5e 100644
--- a/Mockination/MockFunction.h
+++ b/Mockination/MockFunction.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Mockination/MockMutex.h b/Mockination/MockMutex.h
index 495bd43..cbaf325 100644
--- a/Mockination/MockMutex.h
+++ b/Mockination/MockMutex.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/ProgramOptions.cc b/ProgramOptions.cc
index 46b70a7..461d14b 100644
--- a/ProgramOptions.cc
+++ b/ProgramOptions.cc
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#include "ProgramOptions.h"
@@ -10,7 +10,9 @@ static_assert( __cplusplus > 2020'00 );
#include
#include
-namespace Alepha::Cavorite ::detail:: program_options
+#include
+
+namespace Alepha::Hydrogen ::detail:: ProgramOptions_m
{
namespace
{
@@ -205,9 +207,7 @@ namespace Alepha::Cavorite ::detail:: program_options
VariableMap substitutions=
{
- // This uses a GNU extension, but it's fine. We can always make this
- // portable, later.
- { "program-name"s, lambaste<=::program_invocation_short_name },
+ { "program-name"s, lambaste<=System::programName() },
{ "option-name"s, lambaste<=name },
{ "default"s, [&defaultBuilder= defaultBuilder, &name= name]
{
@@ -283,8 +283,7 @@ namespace Alepha::Cavorite ::detail:: program_options
{
VariableMap substitutions
{
- // Another use of the GNUism.
- { "program-name"s, lambaste<=::program_invocation_short_name },
+ { "program-name"s, lambaste<=System::programName() },
};
if( canonicalName.has_value() ) substitutions[ "canonical-name"s ]= lambaste<=canonicalName.value();
diff --git a/ProgramOptions.h b/ProgramOptions.h
index 0c35e5a..c6edb00 100644
--- a/ProgramOptions.h
+++ b/ProgramOptions.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
@@ -96,7 +96,7 @@ static_assert( __cplusplus > 2020'00 );
#include
-namespace Alepha::inline Cavorite ::detail:: program_options
+namespace Alepha::Hydrogen ::detail:: ProgramOptions_m
{
inline namespace exports {}
@@ -290,7 +290,7 @@ namespace Alepha::inline Cavorite ::detail:: program_options
// TODO: This should steal the impl from the vector form, above, and that should defer to this.
using parse_type= typename arg_type::value_type;
- auto handler= [handler, name= name]( std::optional< std::string > argument )
+ auto wrapped= [handler, name= name]( std::optional< std::string > argument )
{
impl::checkArgument( argument, name );
@@ -311,7 +311,7 @@ namespace Alepha::inline Cavorite ::detail:: program_options
};
handler( parsed );
};
- return registerHandler( handler );
+ return registerHandler( wrapped );
}
else
{
@@ -392,12 +392,12 @@ namespace Alepha::inline Cavorite ::detail:: program_options
}
}
-namespace Alepha::Cavorite::inline exports::inline program_options
+namespace Alepha::Hydrogen::inline exports::inline ProgramOptions_m
{
- using namespace detail::program_options::exports;
+ using namespace detail::ProgramOptions_m::exports;
}
-namespace Alepha::Cavorite::inline exports::inline literals::inline option_literals
+namespace Alepha::Hydrogen::inline exports::inline literals::inline option_literals
{
- using namespace detail::program_options::exports::literals;
+ using namespace ProgramOptions_m::literals;
}
diff --git a/Proof/Attestation.h b/Proof/Attestation.h
index 7a7cd63..23648c8 100644
--- a/Proof/Attestation.h
+++ b/Proof/Attestation.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
/*!
* @file
* @brief The `Attestation` framework permits code which can provide limited compiletime guarantees of conditions.
diff --git a/Reflection/aggregate_initializer_size.h b/Reflection/aggregate_initializer_size.h
index 247b5b2..6bb052f 100644
--- a/Reflection/aggregate_initializer_size.h
+++ b/Reflection/aggregate_initializer_size.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Reflection/aggregate_members.h b/Reflection/aggregate_members.h
index 6530cdc..263decc 100644
--- a/Reflection/aggregate_members.h
+++ b/Reflection/aggregate_members.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
@@ -72,14 +72,6 @@ namespace Alepha::Hydrogen::Reflection
// any initialization method.
struct argument { template< typename T > constexpr operator T (); };
- template< typename T >
- struct checker
- {
- using type= typename checker< T >::type;
- //using type= void;
- //static_assert( std::is_empty_v< T > );
- };
-
// Any empty-base-class argument.
template< typename Aggregate >
struct empty_base
@@ -88,7 +80,6 @@ namespace Alepha::Hydrogen::Reflection
requires
(
true
- //typename= typename checker< std::decay_t< T > >::type,
and EmptyType< std::decay_t< T > >
and not SameAs< std::decay_t< T >, Aggregate >
and DerivedFrom< Aggregate, std::decay_t< T > >
diff --git a/Reflection/detail/config.h b/Reflection/detail/config.h
index 84814d9..3fb0a99 100644
--- a/Reflection/detail/config.h
+++ b/Reflection/detail/config.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Reflection/tuplizeAggregate.h b/Reflection/tuplizeAggregate.h
index f959801..b966911 100644
--- a/Reflection/tuplizeAggregate.h
+++ b/Reflection/tuplizeAggregate.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Reflection/tuplizeAggregate.test/0.cc b/Reflection/tuplizeAggregate.test/0.cc
index b441772..b8d444c 100644
--- a/Reflection/tuplizeAggregate.test/0.cc
+++ b/Reflection/tuplizeAggregate.test/0.cc
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#include
diff --git a/StaticValue.h b/StaticValue.h
index 260966b..0b57cf7 100644
--- a/StaticValue.h
+++ b/StaticValue.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Stud/type_traits.h b/Stud/type_traits.h
index de049fe..d69db0d 100644
--- a/Stud/type_traits.h
+++ b/Stud/type_traits.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Style b/Style
new file mode 100644
index 0000000..30dd261
--- /dev/null
+++ b/Style
@@ -0,0 +1,223 @@
+Alepha library C++ style Guide
+
+ This guide shall follow the format of "Rule", then "Examples", then "Reasoning", then "Exceptions".
+Some of these clauses are omitted. No portion of the C++ language itself is off-limits, unless explicit mention
+is made banning that feature -- which is rare. The Alepha C++ philosophy sees C++ as a whole langauge and
+does not seek to provide a "safe subset". The few banned features tend to be those which are either deprecated
+or generally unused.
+
+ Note that this "style guide" covers the style of the code in the sense of how to write the code itself,
+not the rendering/format. For that guide, please see the "Format" file.
+
+General Coding and Language Rules:
+
+0. The Alepha C++ library shall only contain code written for ISO/ANSI C++23, compilation with any other language
+standard is incidental. Modifying a component of Alepha to compile with a non-compliant compiler (while still
+remaining compliant to the standard) is sometimes necessary. Deprecated features should be avoided, wherever
+possible. C++98 code (with precious few exceptions) will work unchanged in C++23.
+
+Reason: Alepha is a modern C++ library technology.
+
+Exceptions: None. ("#pragma once" is used, as if it were part of the standard. This is widely supported,
+and when C++20's modules become more prevalent, the library will move to those.)
+
+Note: Even among all "compliant" compilers, some quirks prevent perfect, and ideal C++23 code.
+Even in such cases, the code which does compile is still expected to be C++23 compliant.
+
+
+1. All names and user-defined tokens shall avoid C89, C99, C11, C++98, C++03, C++11, C++14, C++17, C++20, and C++23
+implementation reserved names, in all contexts.
+
+- Names shall not begin with an underscore.
+- Names shall not contain two consecutive underscores.
+- User defined names include: functions, macros, classes, constants, and header guards.
+- Be mindful of C++'s little known reserved words: compl or not and xor bit_and bit_or bit_xor
+
+Reason: Alepha is a portable C++ library, and thus must be able to compile with any C++23 compiler. The presence
+of implementation reserved names in code can cause potential issues. Even in a context where the name wouldn't
+be reserved, consider it to be reserved.
+
+Exceptions: NONE
+
+
+2. `#define`s of a keyword to anything else are prohibited.
+
+Reason: #defining a keyword yields non-portable behavior. On a fully conforming C++23 compiler, all features
+are present, and useful. Not all compilers behave in the same way, when it comes to violating the standard.
+
+Exceptions: None. For a while, it was necessary to supplement some compilers with `#define override` or
+similar hacks, for not-yet-supported C++11 features. By this point in time, the new features in C++20 and
+C++23 cannot be so easily "hacked in".
+
+
+3. No C++23 core-language features shall be proscribed from use, with the exception of avoiding the following
+idioms or features (some are banned outright):
+
+- digraphs are forbidden (Trigraphs were removed in 17)
+- prefer `new` over `malloc` (except in implementing `::operator new`)
+- prefer to use `()` instead of `( void )` in empty parameter functions
+- avoid `reinterpret_cast` (except in specific hardware-oriented contexts, or where otherwise necessary)
+- rarely use "old C style" casts. (E.g.: `(char *) variable`) -- this is widely known to be dangerous
+- never throw from a destructor -- this is not expected by any C++23 libraries
+- Avoid the use of general `mutable` members. No good can come from changing values behind const, even locks.
+ TOCTIOU bugs abound with observers of threadsafe mechanisms. Properly written Atomics primitives still need
+ to be members of classes as mutable.
+
+Reason: C++23 is a cohesive, well-reasoned language with many tightly-integrated features. Circumscription of
+a feature is ill-advised. The above prohibitions are mostly encouraged by choices of the standards committee
+over the past 20 or more years. Reinterpret cast, in particular, represents a standards-sanctioned form of
+platform specific behavior. Digraphs and trigraphs were a compatibility feature required by the standard, but
+are bizarre and unintuitive -- many compilers disabled them unless in strict compatibility modes, and they are
+removed in C++23.
+
+Exceptions: Malloc can be used in circumstances where that allocator is necessary. "( void )" parameter lists are
+usable in specific 'meant to be included in C' headers -- these must be in their own directory. C style casts
+are dangerous, in general, but '(float) 4 / 2' is more readable than: 'static_cast< float >( 4 ) /
+static_cast< float >( 2 )' or 'std::divide< float >{}( 4, 2 )' -- prefer function-style casting if possible though.
+Never cast with typedefs, or pointers. Destructor throwing is banned, because no C++ STL library can recover
+from it, nor can the exception system itself. Reinterpret_cast has platform dependent behavior, and should
+only be in platform-dependent code. Digraphs should only exist in obfuscation contests. Assume that
+destructors that throw always cause undefined behavior.
+
+
+4. No non-portable, undefined, implementation defined, or optional behaviors, functions and features shall be
+placed into any non-detail portions of the Alepha library. Detail portions shall push implementation-specifics
+into isolated files. Only the "Alepha::Windows", "Alepha::Posix", or similar namespaces shall contain any
+non-portable constructs.
+
+Reasons: Portability is a top concern for the Alepha library.
+
+Exceptions: Alepha has namespaces dedicated to specific platforms and systems -- those namespaces are suitable
+for non-portable code, but no core library portions may rely upon those. (Code must compile with a C++11
+compiler and be platform agnostic.) For non-portable namespaces, the code should remain compiler agnostic,
+where possible. Always use macro-guards to prevent accidental compilation of files intended for only one compiler.
+
+
+5. Avoid "pass-by-non-const-reference" for "out-params". Prefer tuples, pairs and aggregate types for multiple
+return values, and use exceptions to signal failures.
+
+Reason: "push-to-argument" style was useful in the 1990s when compilers were very bad at optimizing the
+class return path. In a post-C++11 world, with both NRV and move semantics, this caveat is no longer pertinent.
+Further, this out-parameter style was necessary when exceptions were avoided, as returning an aggregate is
+not conducive to error-checking. Alepha fully embraces the C++ exceptions mindset. Exceptions exist in the
+language to signal out-of-band errors -- return codes are to be avodied.
+
+Exceptions: Sometimes arguments have to be passed to be modified, like 'operator <<' on std::ostream. The
+'operator >>' std::istream operators are also examples of this, but technically fall under the "out-params" rule.
+Historically these iostream constructs exist, and we shall still support the iostreams idiom -- it's part of
+the standard.
+
+
+6. Manage all resources with objects and RAII. Prefer to manage only a single resource per RAII class. Do not
+ever allow unshepherded or unowned resources to exist. Directly callling "new" is strongly discouraged.
+
+Reason: It's not just a good idea -- all of C++23 is built around the RAII idiom. It's necessary for exception
+safety, which is a core part of the Alepha philosophy. An RAII class which has to manage several resources
+often represents a design bug. Some classes (like ref-counting-pointer constructs) may need to allocate some
+resources to manage another resource -- this is unavoidable, but the other resources are management metadata.
+
+Exceptions: None, whatsoever.
+
+
+7. Unless a class is intended to directly manage a resource, it ought to have a blank destructor, in the
+ideal situation. Prefer `AutoRAII` as the basis of a class designed to use resource management, in the general
+case.
+
+Reason: RAII is most effective when each class that actually manages a resource only manages a single resource,
+and concept abstraction classes don't have to have any explicit resource management.
+
+Exceptions: Sometimes a destructor of a class may need to call one of its own functions, like a "cancel"
+function. A network request, or a thread, for example. This is not entirely an exception, since that class
+models that concept as a kind of resource. Conversely, those "cancel" functions are merely an exposition of
+the dtor functionality for early voluntary cleanup.
+
+
+8. Naked `delete` and other resource release calls outside of destructors are banned. Naked `new` and other
+resource allocation calls that are assigned to raw unmanaged C types, outside of constructors or constructor
+parameters are banned. This rule works hand-in-hand with the previous three rules. Prefer scoped_ptr,
+unique_ptr, shared_ptr, weak_ptr, AutoRAII, vector, list, set, map, and other types over rolling your own
+resource management types, where possible. Calls to 'new' and 'delete' are bad code smells. Use of raw
+primitive pointers is also such a code smell. Consider using `single_ptr` or a native reference when working
+with items held at a distance. Alepha::single_ptr is only constructible from an owned resource, or another
+single_ptr.
+
+Reason: C++ RAII is the only safe way to manage resources. By limiting the resource release code to only
+exist in dtors, it limits the scope of code needed to audit for leak-safety. Even RAII classes should only
+handle resource release through the dtor path. Resource release outside of this path should be viewed as a
+glaring bug. Resource acquisition outside of a ctor, or an argument to a ctor should likewise be seen as a bug.
+Raw C types are unsafe for resource management in the face of changing resources.
+
+Exceptions: None. Although passing a lambda calling a release function to an AutoRAII object is technically
+an exception, it should be thought of as writing an inline dtor. Note that std::make_unique and
+std::make_shared are suitable replacements for new in almost all situations.
+
+
+9. Avoid the use of #ifdef/else/endif for conditional compilation of code -- prefer to include headers with
+well defined code for these purposes.
+
+Reason: Conditional compilation makes code multiply architecturally dependent not independent.
+
+Exceptions: Some small blocks of code (particularly preprocessor control macros) can be if-else-endif for
+small specific variances.
+
+
+
+
+Basic Environment Rules:
+
+0. Alepha shall compile on any C++23 compliant compilers. At present gcc-13 is a supported minimum,
+and is also the "reference compiler".
+
+Exception: Compiler specific code in detail sections need only work with that compiler. Platform specific code,
+in Alepha:: namespaces need only work on that target platform, but should be portable across compilers
+for that platform. Alepha::Posix sections should try to also work in Cygwin, where possible.
+
+1. Header files shall be named with a trailing ".h". Headers shall be entirely compatible with C++.
+
+2. Header files which are safe to include from "C" language parsers shall be organized into a specific
+"c_interface" subdirectory of the appropriate domain. Such headers are rarely expected.
+
+
+Basic Naming Rules:
+
+0. Use good judgement in naming things. This cannot be codified as a set of mechanical rules.
+
+1. Follow this general naming style (there's some room for flexibility within):
+
+ * ClassesAndTypedefsLikeThis
+ * PublicNamespaceAlsoLikeThis
+ * functionsAndVariablesLikeThis
+ * ALEPHA_PATH_TO_MACROS_GET_NAMED_IN_VERY_LONG_STYLE_AND_OBNOXIOUS_WITH_CAPS_AND_UNDERSCORES
+ * meta_programming_things_like_this
+ * TCP_IP_Socket or connectToTCP_IP_Socket-- If the entity name contains abbreviations, separate the abbreviation by `_`,
+ don't case flatten things like TCP to Tcp.
+ * Follow STL and related C++ Standards names, where appropriate (`push_back` not `pushBack`).
+
+2. Follow the general module-namespace technique defined in the NAMESPACES document.
+
+3. Name the private module namespace within files as ` ::detail:: FileName_m`, such that `FileName.h`
+provides the namespace. This provides a simple transform:
+Alepha/IOStream/String.h -> Alepha::IOStream::String_m
+
+4. General file naming is case-sensitive. Every major modern operating system supports a case-sensitive
+filesystem. Use it. Windows' default filesystem is case-preserving. This case-preserving property
+should suffice for most file naming situations; however, if `foo.h` and `Foo.h` both exist, it might
+cause a problem. That problem is more easily remedied by using a case-sensitive filesystem than by
+putting an onus for name mangling onto a project.
+
+5. Name files after the primary component in them, if the file makes a single component available.
+
+Example: `class Alepha::Foobar::Quux` should be defined in `Alepha/Foobar/Quux.h`, if defined in a single
+file. The full public name of that class would be `Alepha::Foobar::exports::Quux_m::Quux`. The private
+name can be anything, of course, but would typically be `Alepha::Foobar::detail::Quux_m::exports::Quux`.
+
+6. Name files which provide families of facilities without leading capitals. Those names shouldn't be
+confused for classes.
+
+7. Name functions with a verb, where appropriate. Don't name observers with a `get` verb.
+
+8. Avoid names with `do`, `run`, `compute`, or `execute` in them for functions. Remember functions
+`do`, they aren't things.
+
+9. Avoid names with `-er`, `Manager`, `Owner`, or `Holder` in them for classes. Remember that
+classes don't `do`, they're not functions.
diff --git a/System/programName.h b/System/programName.h
new file mode 100644
index 0000000..c7e5a2f
--- /dev/null
+++ b/System/programName.h
@@ -0,0 +1,46 @@
+static_assert( __cplusplus > 2020'99 );
+
+#pragma once
+
+#include
+
+#if defined( __FreeBSD__ )
+# include
+# include
+#elif defined( _GNU_SOURCE )
+#endif
+
+#include
+
+namespace Alepha::Hydrogen::System ::detail:: programName_m
+{
+ inline namespace exports
+ {
+ inline std::string
+ programName()
+ {
+ #if defined( __FreeBSD__ )
+ int mib[]
+ {
+ CTL_KERN,
+ KERN_PROC,
+ KERN_PROC_PATHNAME,
+ -1,
+ };
+ char buf[ 4096 ];
+ std::size_t cb = sizeof( buf );
+ ::sysctl( mib, 4, buf, &cb, nullptr, 0);
+ return buf;
+ #elif defined( _GNU_SOURCE )
+ return ::program_invocation_short_name;
+ #else
+ #error "Not supported."
+ #endif
+ }
+ }
+}
+
+namespace Alepha::Hydrogen::System::inline exports::inline programName_m
+{
+ using namespace detail::programName_m::exports;
+}
diff --git a/Testing/TableTest.h b/Testing/TableTest.h
index d8f9348..7d714db 100644
--- a/Testing/TableTest.h
+++ b/Testing/TableTest.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
@@ -17,81 +17,32 @@ static_assert( __cplusplus > 2020'00 );
#include
#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-
#include
#include
#include
#include
+#include
#include
-#include
#include
#include "colors.h"
+#include "printDebugging.h"
namespace Alepha::Hydrogen::Testing ::detail:: table_test
{
- inline namespace exports
- {
- enum class OutputMode { All, Relaxed };
- }
+ inline namespace exports {}
inline void breakpoint() {}
- namespace C
+ namespace C:: inline Colors
{
- inline namespace Colors
- {
- using namespace testing_colors::C::Colors;
- }
+ using namespace testing_colors::C::Colors;
}
- template< OutputMode outputMode, typename T >
- void printDebugging( const T &witness, const T &expected );
-
- template< Aggregate Agg, TypeListType >
- struct TupleSneak;
-
- template< Aggregate Agg >
- struct TupleSneak< Agg, Nil >
- : Agg
- {
- TupleSneak() { std::cerr << "The inherited default ctor was called." << std::endl; }
-
- protected:
- void set( Agg agg ) { static_cast< Agg & >( *this )= agg; }
- };
-
- template< Aggregate Agg, typename ... Args >
- struct TupleSneak< Agg, TypeList< Args... > >
- : TupleSneak< Agg, cdr_t< TypeList< Args... > > >
- {
- using Parent= TupleSneak< Agg, cdr_t< TypeList< Args... > > >;
- using Parent::Parent;
-
- TupleSneak( Args ... args )
- {
- std::cerr << "I was the ctor called, with " << sizeof...( Args ) << " arguments." << std::endl;
- tuple_for_each( std::tuple{ args... } ) <=
- []( const auto element )
- {
- std::cerr << "Element: " << element << std::endl;
- };
- this->set( { args... } );
- }
- };
-
enum class TestResult { Passed, Failed };
struct BlankBase {};
@@ -100,7 +51,7 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
static consteval auto
compute_base_f() noexcept
{
- if constexpr ( Aggregate< T > ) return std::type_identity< TupleSneak< T, list_from_tuple_t< Reflection::aggregate_tuple_t< T > > > >{};
+ if constexpr ( Aggregate< T > ) return std::type_identity< Utility::TupleAdapter< T > >{};
else if constexpr( std::is_class_v< T > ) return std::type_identity< T >{};
else return std::type_identity< BlankBase >{};
}
@@ -109,24 +60,17 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
using compute_base_t= typename decltype( compute_base_f< std::decay_t< T > >() )::type;
template< typename return_type, OutputMode outputMode >
- struct BasicUniversalHandler;
-
- template< Primitive return_type, OutputMode outputMode >
- struct BasicUniversalHandler< return_type, outputMode >
+ struct BasicUniversalHandler
+ : compute_base_t< return_type >
{
+ using ComputedBase= compute_base_t< return_type >;
+ using ComputedBase::ComputedBase;
+
using Invoker= std::function< return_type () >;
+
std::function< TestResult ( Invoker, const std::string & ) > impl;
- TestResult
- operator() ( Invoker invoker, const std::string &comment ) const
- {
- return impl( invoker, comment );
- //if constexpr( std::is_base_of_v< std::decay_t< return_type >, ComputedBase > )
- }
-
-#if 1
- BasicUniversalHandler( const return_type expected )
- : impl
+ BasicUniversalHandler( const return_type expected ) : impl
{
[expected]( Invoker invoker, const std::string &comment )
{
@@ -142,7 +86,7 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
}
};
const auto result= witness == expected ? TestResult::Passed : TestResult::Failed;
-
+
if( result == TestResult::Failed )
{
if( witness.has_value() )
@@ -157,108 +101,12 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
}
}
{}
-#endif
-
-#if 0
- template< typename ... Args >
- requires ConstructibleFrom< return_type, std::decay_t< Args >... >
- BasicUniversalHandler( Args &&... expected_init )
- : BasicUniversalHandler( return_type{ std::forward< Args >( expected_init )... } )
- {}
-#endif
-
-#if 1
- template< typename T >
- requires( not SameAs< T, void > )
- BasicUniversalHandler( std::type_identity< T > ) : impl
- {
- []( Invoker invoker, const std::string &comment )
- {
- try
- {
- std::ignore= invoker();
- std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
- return TestResult::Failed;
- }
- catch( const T & )
- {
- std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl;
- return TestResult::Passed;
- }
- }
- }
- {}
-
- template< typename T >
- requires( SameAs< T, std::type_identity< void > > or SameAs< T, std::nothrow_t > )
- BasicUniversalHandler( T ) : impl
- {
- []( Invoker invoker, const std::string &comment )
- {
- try
- {
- std::ignore= invoker();
- std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl;
- return TestResult::Passed;
- }
- catch( ... )
- {
- std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
- return TestResult::Failed;
- }
- }
- }
- {}
-
- template< DerivedFrom< std::exception > T >
- BasicUniversalHandler( const T exemplar ) : impl
- {
- [expected= std::string{ exemplar.what() }]( Invoker invoker, const std::string &comment )
- {
- try
- {
- invoker();
- std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
- std::cout << " " << C::testInfo << "NOTE" << resetStyle << ": expected exception `"
- << typeid( T ).name()
- << "` wasn't thrown." << std::endl;
- return TestResult::Failed;
- }
- catch( const T &ex )
- {
- const std::string witness= ex.what();
- const TestResult rv= witness == expected ? TestResult::Passed : TestResult::Failed;
- if( rv == TestResult::Failed )
- {
- std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
- std::cout << " " << C::testInfo << "NOTE" << resetStyle << ": expected message did not match." << std::endl;
- printDebugging< outputMode >( witness, expected );
- }
- return rv;
- }
- }
- }
- {}
-#endif
- };
-
- template< Aggregate return_type, OutputMode outputMode >
- struct BasicUniversalHandler< return_type, outputMode >
- : compute_base_t< return_type >
- {
- using ComputedBase= compute_base_t< return_type >;
- using ComputedBase::ComputedBase;
-
- using Invoker= std::function< return_type () >;
-
- std::function< TestResult ( Invoker, const std::string & ) > impl;
TestResult
operator() ( Invoker invoker, const std::string &comment ) const
{
if( impl != nullptr ) return impl( invoker, comment );
- //if constexpr( std::is_base_of_v< std::decay_t< return_type >, ComputedBase > )
- if constexpr( true )
+ if constexpr( std::is_base_of_v< std::decay_t< return_type >, ComputedBase > )
{
const return_type *const expected_p= this;
const auto expected= *expected_p;
@@ -291,39 +139,6 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
else throw std::logic_error( "Somehow we didn't setup impl, and it's not an adapted case!" );
}
-#if 0
- template< typename T_= return_type, typename= std::enable_if_t< not std::is_class_v< std::decay_t< T_ > > > >
- BasicUniversalHandler( const T_ expected )
- : impl
- {
- [expected]( Invoker invoker, const std::string &comment )
- {
- static_assert( not Aggregate< T_ > );
- static_assert( not std::is_class_v< T_ > );
- const return_type witness= invoker();
- const auto result= witness == expected ? TestResult::Passed : TestResult::Failed;
-
- if( result == TestResult::Failed )
- {
- std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
- printDebugging< outputMode >( witness, expected );
- }
- else std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl;
- return result;
- }
- }
- {}
-#endif
-
-#if 0
- template< typename ... Args >
- requires ConstructibleFrom< return_type, std::decay_t< Args >... >
- BasicUniversalHandler( Args &&... expected_init )
- : BasicUniversalHandler( return_type{ std::forward< Args >( expected_init )... } )
- {}
-#endif
-
-#if 1
template< typename T >
requires( not SameAs< T, void > )
BasicUniversalHandler( std::type_identity< T > ) : impl
@@ -396,167 +211,8 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
}
}
{}
-#endif
};
- template< typename return_type, OutputMode outputMode >
- struct BasicUniversalHandler
- : return_type
- {
- using return_type::return_type;
-
- BasicUniversalHandler( return_type rt ) : return_type( rt ) {}
-
- using Invoker= std::function< return_type () >;
-
- std::function< TestResult ( Invoker, const std::string & ) > impl;
-
- TestResult
- operator() ( Invoker invoker, const std::string &comment ) const
- {
- if( impl != nullptr ) return impl( invoker, comment );
- //if constexpr( std::is_base_of_v< std::decay_t< return_type >, ComputedBase > )
- if constexpr( true )
- {
- const return_type *const expected_p= this;
- const auto expected= *expected_p;
- const auto witness= Utility::evaluate <=[&]() -> std::optional< return_type >
- {
- try
- {
- return invoker();
- }
- catch( ... )
- {
- return std::nullopt;
- }
- };
- const auto result= witness == expected ? TestResult::Passed : TestResult::Failed;
-
- if( result == TestResult::Failed )
- {
- if( witness.has_value() )
- {
- std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
- printDebugging< outputMode >( witness.value(), expected );
- }
- else std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": Unexpected exception in \"" << comment << '"' << std::endl;
- }
- else std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl;
- return result;
- }
- else throw std::logic_error( "Somehow we didn't setup impl, and it's not an adapted case!" );
- }
-
-#if 0
- template< typename T_= return_type, typename= std::enable_if_t< not std::is_class_v< std::decay_t< T_ > > > >
- BasicUniversalHandler( const T_ expected )
- : impl
- {
- [expected]( Invoker invoker, const std::string &comment )
- {
- static_assert( not Aggregate< T_ > );
- static_assert( not std::is_class_v< T_ > );
- const return_type witness= invoker();
- const auto result= witness == expected ? TestResult::Passed : TestResult::Failed;
-
- if( result == TestResult::Failed )
- {
- std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
- printDebugging< outputMode >( witness, expected );
- }
- else std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl;
- return result;
- }
- }
- {}
-#endif
-
-#if 0
- template< typename ... Args >
- requires ConstructibleFrom< return_type, std::decay_t< Args >... >
- BasicUniversalHandler( Args &&... expected_init )
- : BasicUniversalHandler( return_type{ std::forward< Args >( expected_init )... } )
- {}
-#endif
-
-#if 1
- template< typename T >
- requires( not SameAs< T, void > )
- BasicUniversalHandler( std::type_identity< T > ) : impl
- {
- []( Invoker invoker, const std::string &comment )
- {
- try
- {
- std::ignore= invoker();
- std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
- return TestResult::Failed;
- }
- catch( const T & )
- {
- std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl;
- return TestResult::Passed;
- }
- }
- }
- {}
-
- template< typename T >
- requires( SameAs< T, std::type_identity< void > > or SameAs< T, std::nothrow_t > )
- BasicUniversalHandler( T ) : impl
- {
- []( Invoker invoker, const std::string &comment )
- {
- try
- {
- std::ignore= invoker();
- std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl;
- return TestResult::Passed;
- }
- catch( ... )
- {
- std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
- return TestResult::Failed;
- }
- }
- }
- {}
-
- template< DerivedFrom< std::exception > T >
- BasicUniversalHandler( const T exemplar ) : impl
- {
- [expected= std::string{ exemplar.what() }]( Invoker invoker, const std::string &comment )
- {
- try
- {
- invoker();
- std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
- std::cout << " " << C::testInfo << "NOTE" << resetStyle << ": expected exception `"
- << typeid( T ).name()
- << "` wasn't thrown." << std::endl;
- return TestResult::Failed;
- }
- catch( const T &ex )
- {
- const std::string witness= ex.what();
- const TestResult rv= witness == expected ? TestResult::Passed : TestResult::Failed;
- if( rv == TestResult::Failed )
- {
- std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
- std::cout << " " << C::testInfo << "NOTE" << resetStyle << ": expected message did not match." << std::endl;
- printDebugging< outputMode >( witness, expected );
- }
- return rv;
- }
- }
- }
- {}
-#endif
- };
-
-
-
template< typename F >
concept FunctionVariable=
requires( const F &f )
@@ -564,7 +220,6 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
{ std::function{ f } };
};
-
namespace exports
{
template< FunctionVariable auto, OutputMode outputMode= OutputMode::All > struct TableTest;
@@ -578,7 +233,6 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
using std::begin, std::end;
using namespace Utility::exports::evaluation_helpers;
- using namespace std::literals::string_literals;
template< template< typename, typename... > class Sequence, typename ... TupleArgs >
auto
@@ -598,151 +252,6 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
return rv;
}
- template< OutputMode outputMode, typename T >
- std::string
- stringifyValue( const T &v )
- {
- std::ostringstream oss;
- if constexpr( false ) ; // To keep the rest of the clauses regular
- else if constexpr( std::is_same_v< std::uint8_t, std::decay_t< T > > )
- {
- oss << std::hex << std::setw( 2 ) << std::setfill( '0' ) << int( v );
- }
- else if constexpr( std::is_same_v< bool, std::decay_t< T > > )
- {
- oss << std::boolalpha << v;
- }
- else if constexpr( std::is_same_v< std::string, std::decay_t< T > > )
- {
- oss << "(String with " << v.size() << " chars)";
- oss << '\n' << R"(""")" << '\n';
- for( const char ch: v )
- {
- if( ch == '\n' ) oss << "\n";
- else if( std::isalnum( ch ) or std::ispunct( ch ) or ( ch == ' ' ) ) oss << ch;
- else oss << "<\\0x" << std::hex << std::setw( 2 ) << std::setfill( '0' ) << unsigned( ch ) << '>';
- }
- oss << '\n' << R"(""")";
- }
- else if constexpr( Meta::is_ostreamable_v< T > )
- {
- return IOStreams::stringify( v );
- }
- else if constexpr( Meta::is_optional_v< T > )
- {
- return v.has_value() ? stringifyValue< outputMode >( v.value() ) : ""s;
- }
- else if constexpr( Meta::is_sequence_v< T > )
- {
- if constexpr( outputMode == OutputMode::Relaxed and not Meta::is_ostreamable_v< typename T::value_type > )
- {
- oss << "";
- }
- else
- {
- oss << Meta::sequence_kind_v< T > << "(" << v.size() << " elements):\n{" << std::endl;
-
- int index= 0;
- for( const auto &elem: v ) oss << "\t" << index++ << ": " << stringifyValue< outputMode >( elem ) << "," << std::endl;
- oss << "}" << std::endl;
- }
- }
- else if constexpr( Meta::is_pair_v< T > )
- {
- const auto &[ first, second ]= v;
- return stringifyValue< outputMode >( std::tie( first, second ) );
- }
- else if constexpr( Meta::is_tuple_v< T > )
- {
- oss << '[';
- tuple_for_each( v ) <=[&oss, first= true]( const auto &elem ) mutable
- {
- if( not first ) oss << ", ";
- first= false;
- oss << std::endl << stringifyValue< outputMode >( elem );
- };
- oss << std::endl << ']' << std::endl;
- }
- else if constexpr( std::is_same_v< T, TotalOrder > )
- {
- if( false ) ; // For alignment
- else if( v == TotalOrder::less ) oss << "less";
- else if ( v == TotalOrder::equal ) oss << "equal";
- else if( v == TotalOrder::greater ) oss << "greater";
- else throw std::logic_error( "Impossible `TotalOrder` condition." );
- }
- else
- {
- static_assert( dependent_value< false, T >, "One of the types used in the testing table does not support stringification." );
- }
- return std::move( oss ).str();
- }
-
- inline void
- printDebuggingForStrings( const std::string &witness, const std::string &expected )
- {
- const std::size_t amount= std::min( witness.size(), expected.size() );
- if( witness.size() != expected.size() )
- {
- std::cout << "Witness string size did not match the expected string size. Only mismatches found in the first "
- << amount << " characters will be printed." << std::endl;
- }
-
- for( int i= 0; i < amount; ++i )
- {
- if( witness.at( i ) == expected.at( i ) ) continue;
- std::cout << "Mismatch at index: " << i << std::endl;
- std::cout << "witness: " << witness.at( i ) << std::endl;
- std::cout << "expected: " << expected.at( i ) << std::endl;
- }
- }
-
- template< OutputMode outputMode, typename T >
- void
- printDebugging( const T &witness, const T &expected )
- {
- if constexpr( std::is_same_v< std::string, std::decay_t< T > > )
- {
- printDebuggingForStrings( witness, expected );
- }
- else if constexpr( Meta::is_sequence_v< T > )
- {
- if constexpr( std::is_same_v< std::string, typename T::value_type > )
- {
- if( witness.size() == expected.size() ) for( std::size_t i= 0; i < witness.size(); ++i )
- {
- if( witness.at( i ) != expected.at( i ) ) printDebuggingForStrings( witness.at( i ), expected.at( i ) );
- }
- }
- else
- {
- if( witness.size() != expected.size() )
- {
- std::cout << "Witness sequence size of " << witness.size() << " did not match the expected sequence size of "
- << expected.size() << std::endl;
- }
-
- auto next= std::make_pair( begin( witness ), begin( expected ) );
- bool first= true;
- while( next.first != end( witness ) and next.second != end( expected ) )
- {
- if( not first )
- {
- std::cout << "Mismatch at witness index " << std::distance( begin( witness ), next.first ) << " and "
- << "expected index " << std::distance( begin( expected ), next.second ) << std::endl;
- ++next.first; ++next.second;
- }
- first= false;
- next= std::mismatch( next.first, end( witness ), next.second, end( expected ) );
- }
- }
- }
-
- std::cout << std::endl
- << "computed: " << stringifyValue< outputMode >( witness ) << std::endl
- << "expected: " << stringifyValue< outputMode >( expected ) << std::endl << std::endl;
- }
-
template< FunctionVariable auto function, OutputMode outputMode >
struct exports::TableTest
{
@@ -751,175 +260,6 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
using args_type= Meta::product_type_decay_t< typename function_traits_type::args_type >;
using return_type= typename function_traits_type::return_type;
- // The classic table-test engine would only support `Cases` which were run-and-test-value
- // without the ability to test exceptions. The `ExceptionCases` construct was used to
- // test throwing cases.
- //
- // A unified `Cases` type is forthcoming, and thus `ExecutionCases` exists for backwards
- // compatibility.
- struct ExecutionCases
- {
- using TestDescription= std::tuple< std::string, args_type, return_type >;
-
- std::vector< TestDescription > tests;
-
- explicit
- ExecutionCases( std::initializer_list< TestDescription > initList )
- : tests( initList ) {}
-
- int
- operator() () const
- {
- int failureCount= 0;
- for( const auto &[ comment, params, expected ]: tests )
- {
- if( C::debugCaseTypes ) std::cerr << boost::core::demangle( typeid( params ).name() ) << std::endl;
- breakpoint();
- const auto witness= std::apply( function, params );
- const auto result= witness == expected;
- if( not result )
- {
- std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
- ++failureCount;
- printDebugging< outputMode >( witness, expected );
- }
- else std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl;
- }
-
- return failureCount;
- }
- };
-
- struct ExceptionCases_real
- {
- using Invoker= std::function< void () >;
- struct ExceptionHandler
- {
- std::function< bool ( Invoker ) > impl;
-
- bool operator() ( Invoker invoker ) const { return impl( invoker ); }
-
- ExceptionHandler() : impl
- {
- []( Invoker invoker )
- {
- try
- {
- invoker();
- return true;
- }
- catch( ... ) { return false; }
- }
- }
- {}
-
- template< typename T >
- requires( SameAs< T, std::type_identity< void > > or SameAs< T, std::nothrow_t > )
- ExceptionHandler( T ) : impl
- {
- []( Invoker invoker )
- {
- try
- {
- invoker();
- return true;
- }
- catch( ... ) { return false; }
- }
- }
- {}
-
- template< typename T >
- requires( not SameAs< T, void > )
- ExceptionHandler( std::type_identity< T > ) : impl
- {
- []( Invoker invoker )
- {
- try
- {
- invoker();
- return false;
- }
- catch( const T & ) { return true; }
- }
- }
- {}
-
- template< typename T >
- ExceptionHandler( const T exemplar ) : impl
- {
- [expected= std::string{ exemplar.what() }]( Invoker invoker )
- {
- try
- {
- invoker();
- std::cerr << " " << C::testInfo << "NOTE" << resetStyle << ": expected exception `"<< typeid( T ).name()
- << "` wasn't thrown." << std::endl;
- return false;
- }
- catch( const T &ex )
- {
- const std::string witness= ex.what();
- const bool rv= witness == expected;
- if( not rv )
- {
- std::cerr << " " << C::testInfo << "NOTE" << resetStyle << ": expected exception `"<< typeid( T ).name()
- << "` wasn't thrown." << std::endl;
- printDebugging< outputMode >( witness, expected );
- }
- return rv;
- }
- }
- }
- {}
-
- // This checker is invoked during `catch( ... )`
- ExceptionHandler( const std::function< bool () > checker ) : impl
- {
- [=]( Invoker invoker )
- {
- try
- {
- invoker();
- return false;
- }
- catch( ... ) { checker(); } // The `checker` can use `throw` to run any complex checks it needs.
- }
- }
- {}
- };
-
- using TestDescription= std::tuple< std::string, args_type, ExceptionHandler >;
-
- std::vector< TestDescription > tests;
-
-
- explicit
- ExceptionCases_real( std::initializer_list< TestDescription > initList )
- : tests( initList ) {}
-
- int
- operator() () const
- {
- int failureCount= 0;
- for( const auto &[ comment, params, checker ]: tests )
- {
- if( C::debugCaseTypes ) std::cerr << boost::core::demangle( typeid( params ).name() ) << std::endl;
- breakpoint();
- auto invoker= [&]{ std::apply( function, params ); };
- const auto result= checker( invoker );
- if( not result )
- {
- std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
- ++failureCount;
- }
- else std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl;
- }
-
- return failureCount;
- }
- };
-
using ComputedBase= compute_base_t< return_type >;
struct UniversalCases
@@ -929,21 +269,17 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
using UniversalHandler= BasicUniversalHandler< return_type, outputMode >;
- using TestDescription= std::tuple< std::string, args_type, UniversalHandler >;
+ struct TestDescription
+ {
+ std::string comment;
+ args_type args;
+ UniversalHandler handler;
+ };
std::vector< TestDescription > tests;
UniversalCases( std::initializer_list< TestDescription > initList )
+ : tests( initList )
{
- for( const auto &desc: initList )
- {
- if constexpr( Aggregate< return_type > )
- {
- std::cerr << "Case: " << std::get< 0 >( desc );
- const return_type &v= std::get< 2 >( desc );
- std::cerr << " (" << v << ")" << std::endl;
- tests.push_back( desc );
- }
- }
}
int
@@ -958,7 +294,7 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
breakpoint();
return std::apply( function, params );
};
- const TestResult result= checker( invoker, comment );
+ const auto result= checker( invoker, comment );
if( result == TestResult::Failed )
{
std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
@@ -980,58 +316,6 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
//using ExceptionCases= ExceptionCases_real;
using ExceptionCases= UniversalCases;
};
-
-#ifdef DISABLED
- template< typename RetVal, typename ... Args, RetVal (*function)( Args... ) >
- struct TableTest< function >::VectorCases
- {
- static_assert( sizeof...( Args ) == 1 );
- static_assert( Meta::is_vector_v< RetVal > );
- static_assert( Meta::is_vector_v< std::tuple_element_t< 0, std::tuple< Args... > > > );
-
- using TestDescription= std::tuple< std::string,
- std::vector< std::pair< typename std::tuple_element_t< 0, std::tuple< Args... > >::value_type, typename RetVal::value_type > > >;
-
- std::vector< TestDescription > tests;
-
- explicit
- VectorCases( std::initializer_list< TestDescription > initList )
- : tests( initList ) {}
-
- int
- operator() () const
- {
- int failureCount= 0;
- for( const auto &[ comment, productions ]: tests )
- {
- const auto expected= evaluate <=[&]
- {
- std::vector< RetVal > rv;
- std::transform( begin( productions ), end( productions ), back_inserter( rv ),
- []( const auto &prod ) { return prod.second; } );
- return rv;
- };
-
- const auto params= evaluate <=[&]
- {
- std::vector< RetVal > rv;
- std::transform( begin( productions ), end( productions ), back_inserter( rv ),
- []( const auto &prod ) { return prod.first; } );
- return rv;
- };
-
- if( std::apply( function, std::tuple{ params } ) != expected )
- {
- std::cout << " FAILURE: " << comment << std::endl;
- ++failureCount;
- }
- else std::cout << " SUCCESS: " << comment << std::endl;
- }
-
- return failureCount;
- }
- };
-#endif
}
namespace Alepha::Hydrogen::Testing::inline exports::inline table_test
diff --git a/Testing/TableTest.test/test.cc b/Testing/TableTest.test/test.cc
index 306f94e..fa1922a 100644
--- a/Testing/TableTest.test/test.cc
+++ b/Testing/TableTest.test/test.cc
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#include
#include
diff --git a/Testing/TableTest.test/test2.cc b/Testing/TableTest.test/test2.cc
index ecad23a..40de87e 100644
--- a/Testing/TableTest.test/test2.cc
+++ b/Testing/TableTest.test/test2.cc
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#include
diff --git a/Testing/colors.h b/Testing/colors.h
index 804188c..cb32624 100644
--- a/Testing/colors.h
+++ b/Testing/colors.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Testing/printDebugging.h b/Testing/printDebugging.h
new file mode 100644
index 0000000..694b1a1
--- /dev/null
+++ b/Testing/printDebugging.h
@@ -0,0 +1,207 @@
+static_assert( __cplusplus > 2020'99 );
+
+#pragma once
+
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+namespace Alepha::Hydrogen::Testing ::detail:: printDebugging_m
+{
+ inline namespace exports
+ {
+ enum class OutputMode { All, Relaxed };
+
+ template< OutputMode outputMode, typename T >
+ void printDebugging( const T &witness, const T &expected );
+ }
+
+ using namespace std::literals::string_literals;
+
+ template< OutputMode outputMode, typename T >
+ void
+ streamValue( const T &v, std::ostream &oss )
+ {
+ if constexpr( false ) ; // To keep the rest of the clauses regular
+ else if constexpr( std::is_same_v< std::uint8_t, std::decay_t< T > > )
+ {
+ oss << std::hex << std::setw( 2 ) << std::setfill( '0' ) << int( v );
+ }
+ else if constexpr( std::is_same_v< bool, std::decay_t< T > > )
+ {
+ oss << std::boolalpha << v;
+ }
+ else if constexpr( std::is_same_v< std::string, std::decay_t< T > > )
+ {
+ oss << "(String with " << v.size() << " chars)";
+ oss << '\n' << R"(""")" << '\n';
+ for( const char ch: v )
+ {
+ if( ch == '\n' ) oss << "\n";
+ else if( std::isalnum( ch ) or std::ispunct( ch ) or ( ch == ' ' ) ) oss << ch;
+ else oss << "<\\0x" << std::hex << std::setw( 2 ) << std::setfill( '0' ) << unsigned( ch ) << '>';
+ }
+ oss << '\n' << R"(""")";
+ }
+ else if constexpr( Meta::is_ostreamable_v< T > )
+ {
+ oss << v;
+ }
+ else if constexpr( Meta::is_optional_v< T > )
+ {
+ if( not v.has_value() ) oss << "";
+ else streamValue< outputMode >( v.value(), oss );
+ }
+ else if constexpr( Meta::is_sequence_v< T > )
+ {
+ if constexpr( outputMode == OutputMode::Relaxed and not Meta::is_ostreamable_v< typename T::value_type > )
+ {
+ oss << "";
+ }
+ else
+ {
+ oss << Meta::sequence_kind_v< T > << "(" << v.size() << " elements):\n{" << std::endl;
+
+ int index= 0;
+ for( const auto &elem: v )
+ {
+ oss << "\t" << index++ << ": ";
+ streamValue< outputMode >( elem, oss );
+ oss << "," << std::endl;
+ }
+ oss << "}" << std::endl;
+ }
+ }
+ else if constexpr( Meta::is_pair_v< T > )
+ {
+ const auto &[ first, second ]= v;
+ streamValue< outputMode >( std::tie( first, second ), oss );
+ }
+ else if constexpr( Meta::is_tuple_v< T > )
+ {
+ oss << '[';
+ tuple_for_each( v ) <=[&oss, first= true]( const auto &elem ) mutable
+ {
+ if( not first ) oss << ", ";
+ first= false;
+ oss << std::endl;
+ streamValue< outputMode >( elem, oss );
+ };
+ oss << std::endl << ']' << std::endl;
+ }
+ else if constexpr( std::is_same_v< T, TotalOrder > )
+ {
+ if( false ) ; // For alignment
+ else if( v == TotalOrder::less ) oss << "less";
+ else if( v == TotalOrder::equal ) oss << "equal";
+ else if( v == TotalOrder::greater ) oss << "greater";
+ else throw std::logic_error( "Impossible `TotalOrder` condition." );
+ }
+ else
+ {
+ static_assert( dependent_value< false, T >, "One of the types used in the testing table does not support stringification." );
+ }
+ }
+
+ template< OutputMode outputMode, typename T >
+ std::string
+ stringifyValue( const T &v )
+ {
+ std::ostringstream oss;
+ streamValue< outputMode >( v, oss );
+ return std::move( oss ).str();
+ }
+
+ inline void
+ printDebuggingForStrings( const std::string &witness, const std::string &expected )
+ {
+ const std::size_t amount= std::min( witness.size(), expected.size() );
+ if( witness.size() != expected.size() )
+ {
+ std::cout << "Witness string size did not match the expected string size. Only mismatches found in the first "
+ << amount << " characters will be printed." << std::endl;
+ }
+
+ for( int i= 0; i < amount; ++i )
+ {
+ if( witness.at( i ) == expected.at( i ) ) continue;
+ std::cout << "Mismatch at index: " << i << std::endl;
+ std::cout << "witness: " << witness.at( i ) << std::endl;
+ std::cout << "expected: " << expected.at( i ) << std::endl;
+ }
+ }
+
+ template< OutputMode outputMode, typename T >
+ void
+ exports::printDebugging( const T &witness, const T &expected )
+ {
+ if constexpr( std::is_same_v< std::string, std::decay_t< T > > )
+ {
+ printDebuggingForStrings( witness, expected );
+ }
+ else if constexpr( Meta::is_sequence_v< T > )
+ {
+ if constexpr( std::is_same_v< std::string, typename T::value_type > )
+ {
+ if( witness.size() == expected.size() ) for( std::size_t i= 0; i < witness.size(); ++i )
+ {
+ if( witness.at( i ) != expected.at( i ) ) printDebuggingForStrings( witness.at( i ), expected.at( i ) );
+ }
+ }
+ else
+ {
+ if( witness.size() != expected.size() )
+ {
+ std::cout << "Witness sequence size of " << witness.size() << " did not match the expected sequence size of "
+ << expected.size() << std::endl;
+ }
+
+ auto next= std::make_pair( begin( witness ), begin( expected ) );
+ bool first= true;
+ while( next.first != end( witness ) and next.second != end( expected ) )
+ {
+ if( not first )
+ {
+ std::cout << "Mismatch at witness index " << std::distance( begin( witness ), next.first ) << " and "
+ << "expected index " << std::distance( begin( expected ), next.second ) << std::endl;
+ ++next.first; ++next.second;
+ }
+ first= false;
+ next= std::mismatch( next.first, end( witness ), next.second, end( expected ) );
+ }
+ }
+ }
+
+ std::cout << std::endl
+ << "computed: " << stringifyValue< outputMode >( witness ) << std::endl
+ << "expected: " << stringifyValue< outputMode >( expected ) << std::endl << std::endl;
+ }
+
+}
+
+namespace Alepha::Hydrogen::Testing::inline exports::inline printDebugging_m
+{
+ using namespace detail::printDebugging_m::exports;
+}
diff --git a/Testing/test.cc b/Testing/test.cc
index d940fbf..9d7b596 100644
--- a/Testing/test.cc
+++ b/Testing/test.cc
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#include "test.h"
diff --git a/Testing/test.h b/Testing/test.h
index d5c1739..48dbf8a 100644
--- a/Testing/test.h
+++ b/Testing/test.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Testing/testlib.cc b/Testing/testlib.cc
index b3d989c..a8abd86 100644
--- a/Testing/testlib.cc
+++ b/Testing/testlib.cc
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#include
#include
diff --git a/Thread.h b/Thread.h
index 4b4d196..240c4c9 100644
--- a/Thread.h
+++ b/Thread.h
@@ -1,12 +1,12 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
#include
-#include
-#include
-#include
+#include
+#include
+#include
#include
@@ -42,7 +42,7 @@ namespace Alepha::Hydrogen
{
callable();
}
- catch( const boost_ns::thread_interrupted & )
+ catch( const boost::thread_interrupted & )
{
std::lock_guard lock( access );
if( not notification ) throw;
@@ -63,7 +63,7 @@ namespace Alepha::Hydrogen
namespace exports
{
class ConditionVariable
- : private boost_ns::condition_variable
+ : private boost::condition_variable
{
public:
using condition_variable::notify_all;
@@ -89,17 +89,17 @@ namespace Alepha::Hydrogen
{
template< typename Clock, typename Duration >
void
- sleep_until( const boost_ns::chrono::time_point< Clock, Duration > &abs_time )
+ sleep_until( const boost::chrono::time_point< Clock, Duration > &abs_time )
{
- notification.check_interrupt( [&]{ boost_ns::this_thread::sleep_until( abs_time ); } );
+ notification.check_interrupt( [&]{ boost::this_thread::sleep_until( abs_time ); } );
}
#if 0
template< typename Rep, typename Period >
void
- sleep_for( const boost_ns::chrono::duration< Rep, Period > &rel_time )
+ sleep_for( const boost::chrono::duration< Rep, Period > &rel_time )
{
- notification.check_interrupt( [&]( boost_ns::this_thread::sleep_until( rel_time ); } );
+ notification.check_interrupt( [&]( boost::this_thread::sleep_until( rel_time ); } );
}
#endif
}
@@ -113,7 +113,7 @@ namespace Alepha::Hydrogen
namespace exports
{
class Thread
- : ThreadNotification, boost_ns::thread
+ : ThreadNotification, boost::thread
{
public:
template< typename Callable >
@@ -153,10 +153,10 @@ namespace Alepha::Hydrogen
}
};
- using Mutex= boost_ns::mutex;
- using boost_ns::mutex;
- using boost_ns::unique_lock;
- using boost_ns::lock_guard;
+ using Mutex= boost::mutex;
+ using boost::mutex;
+ using boost::unique_lock;
+ using boost::lock_guard;
}
}
diff --git a/Thread.test/CMakeLists.txt b/Thread.test/CMakeLists.txt
new file mode 100644
index 0000000..b72b72c
--- /dev/null
+++ b/Thread.test/CMakeLists.txt
@@ -0,0 +1,3 @@
+link_libraries( boost_thread )
+
+unit_test( thread )
diff --git a/Thread.test/Makefile b/Thread.test/Makefile
deleted file mode 100644
index f2f34d1..0000000
--- a/Thread.test/Makefile
+++ /dev/null
@@ -1,10 +0,0 @@
-CXXFLAGS+= -std=c++2a -I ../
-CXXFLAGS+= -g -O0
-CXX=clang++-12
-
-CXXFLAGS+= -Wno-inline-namespace-reopened-noninline
-CXXFLAGS+= -Wno-unused-comparison
-
-LDLIBS+= -lboost_thread -lpthread
-
-all: thread
diff --git a/Thread.test/thread.cc b/Thread.test/thread.cc
index 0bb9eb4..f7f7544 100644
--- a/Thread.test/thread.cc
+++ b/Thread.test/thread.cc
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#include
@@ -6,7 +6,7 @@ static_assert( __cplusplus > 2020'00 );
#include
#include
-#include
+#include
namespace
{
@@ -36,7 +36,7 @@ namespace
}
catch( const boost::thread_interrupted & )
{
- std::cerr << "SHIT! We didn't get intercepted!" << std::endl;
+ std::cerr << "OOPS! We didn't get intercepted!" << std::endl;
throw;
}
catch( const MyNotification &n )
diff --git a/TotalOrder.h b/TotalOrder.h
index e18f722..7bf3fb3 100644
--- a/TotalOrder.h
+++ b/TotalOrder.h
@@ -1,12 +1,14 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
+#include
+
#include
#include "Concepts.h"
-namespace Alepha::inline Cavorite ::detail:: total_order
+namespace Alepha::Hydrogen ::detail:: TotalOrder_m
{
inline namespace exports
{
@@ -21,7 +23,7 @@ namespace Alepha::inline Cavorite ::detail:: total_order
}
}
-namespace Alepha::Cavorite::inline exports::inline total_order
+namespace Alepha::Hydrogen::inline exports::inline TotalOrder_m
{
- using namespace detail::total_order::exports;
+ using namespace detail::TotalOrder_m::exports;
}
diff --git a/Truss/basetypes.h b/Truss/basetypes.h
index 5e28ab7..1767b8e 100644
--- a/Truss/basetypes.h
+++ b/Truss/basetypes.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Truss/condition_variable.h b/Truss/condition_variable.h
index d459310..67bfb9c 100644
--- a/Truss/condition_variable.h
+++ b/Truss/condition_variable.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Truss/function.h b/Truss/function.h
index d3e40af..6023a86 100644
--- a/Truss/function.h
+++ b/Truss/function.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Truss/m2.h b/Truss/m2.h
index 7a83673..e08a171 100644
--- a/Truss/m2.h
+++ b/Truss/m2.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Truss/memory.h b/Truss/memory.h
index 8bee8ad..151b7fb 100644
--- a/Truss/memory.h
+++ b/Truss/memory.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Truss/mutex.h b/Truss/mutex.h
index 18bebaa..58cb5a8 100644
--- a/Truss/mutex.h
+++ b/Truss/mutex.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Truss/thread.h b/Truss/thread.h
index 61918db..6ef3fed 100644
--- a/Truss/thread.h
+++ b/Truss/thread.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Truss/thread_common.h b/Truss/thread_common.h
index 9c7f82f..65501be 100644
--- a/Truss/thread_common.h
+++ b/Truss/thread_common.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Utility/CMakeLists.txt b/Utility/CMakeLists.txt
index af16b8e..e69de29 100644
--- a/Utility/CMakeLists.txt
+++ b/Utility/CMakeLists.txt
@@ -1,5 +0,0 @@
-target_sources( alepha PRIVATE
- StackableStreambuf.cc
-)
-
-add_subdirectory( StackableStreambuf.test )
diff --git a/Utility/StaticValue.h b/Utility/StaticValue.h
index 54daa91..1af28c2 100644
--- a/Utility/StaticValue.h
+++ b/Utility/StaticValue.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/Utility/TupleAdapter.h b/Utility/TupleAdapter.h
new file mode 100644
index 0000000..24f1ed2
--- /dev/null
+++ b/Utility/TupleAdapter.h
@@ -0,0 +1,55 @@
+static_assert( __cplusplus > 2020'99 );
+
+#pragma once
+
+#include
+
+#include
+
+#include
+
+namespace Alepha::Hydrogen::Utility ::detail:: TupleAdapter_m
+{
+ inline namespace exports {}
+
+ template< Aggregate, TypeListType >
+ struct TupleAdapter_impl;
+
+ namespace exports
+ {
+ template< Aggregate Agg >
+ using TupleAdapter= TupleAdapter_impl
+ <
+ Agg,
+ list_from_tuple_t< Reflection::aggregate_tuple_t< Agg > >
+ >;
+ }
+
+ template< Aggregate Agg >
+ struct TupleAdapter_impl< Agg, Nil >
+ : Agg
+ {
+ TupleAdapter_impl()= default;
+
+ protected:
+ void set( Agg agg ) { static_cast< Agg & >( *this )= agg; }
+ };
+
+ template< Aggregate Agg, typename ... Args >
+ struct TupleAdapter_impl< Agg, TypeList< Args... > >
+ : TupleAdapter_impl< Agg, cdr_t< TypeList< Args... > > >
+ {
+ using Parent= TupleAdapter_impl< Agg, cdr_t< TypeList< Args... > > >;
+ using Parent::Parent;
+
+ TupleAdapter_impl( Args ... args )
+ {
+ this->set( { args... } );
+ }
+ };
+}
+
+namespace Alepha::Hydrogen::Utility::inline exports::inline TupleAdapter_m
+{
+ using namespace detail::TupleAdapter_m::exports;
+}
diff --git a/Utility/evaluation_helpers.h b/Utility/evaluation_helpers.h
index b3039e8..bf0b94b 100644
--- a/Utility/evaluation_helpers.h
+++ b/Utility/evaluation_helpers.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
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 )
diff --git a/auto_comparable.h b/auto_comparable.h
index 42bc821..aee0388 100644
--- a/auto_comparable.h
+++ b/auto_comparable.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/cmake/rules.cmake b/cmake/rules.cmake
index ccb603e..897a0c8 100644
--- a/cmake/rules.cmake
+++ b/cmake/rules.cmake
@@ -1,8 +1,23 @@
#cmake_policy( SET CMP0002 OLD )
-add_compile_options( -std=c++20 )
+# C++23 isn't widely supported among gnu-ish compilers yet... so 2b will have to do, for now.
+add_compile_options( -std=c++2b )
+
+if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" )
add_compile_options( -fdiagnostics-column-unit=byte )
add_compile_options( -fconcepts-diagnostics-depth=0 )
+endif()
+
+if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )
+add_compile_options( -Wno-inline-namespace-reopened-noninline )
+add_compile_options( -Wno-unknown-warning-option )
+add_compile_options( -Wno-unused-comparison )
+add_compile_options( -Wno-inconsistent-missing-override )
+add_compile_options( -DBOOST_NO_CXX98_FUNCTION_BASE )
+endif()
+
+
+
include_directories( ${CMAKE_BINARY_DIR} . )
if( "${CMAKE_BUILD_TYPE}" STREQUAL "Debug" )
diff --git a/comparisons.h b/comparisons.h
index a98385a..c5147b0 100644
--- a/comparisons.h
+++ b/comparisons.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/comparisons.test/0.cc b/comparisons.test/0.cc
index 0738ffd..ce94355 100644
--- a/comparisons.test/0.cc
+++ b/comparisons.test/0.cc
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#include
diff --git a/dumbhash.test/0.cc b/dumbhash.test/0.cc
index fd30012..3eef334 100644
--- a/dumbhash.test/0.cc
+++ b/dumbhash.test/0.cc
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#include
#include
diff --git a/error.h b/error.h
index c115d52..0213168 100644
--- a/error.h
+++ b/error.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/example.cc b/example.cc
index 77da993..f7cf926 100644
--- a/example.cc
+++ b/example.cc
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#include "ProgramOptions.h"
diff --git a/function_traits.h b/function_traits.h
index 62bca0c..dc4f215 100644
--- a/function_traits.h
+++ b/function_traits.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
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;
+}
diff --git a/meta.h b/meta.h
index 8e8ab38..fa09083 100644
--- a/meta.h
+++ b/meta.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/string_algorithms.cc b/string_algorithms.cc
index fb69e01..8a49451 100644
--- a/string_algorithms.cc
+++ b/string_algorithms.cc
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#include "string_algorithms.h"
@@ -22,7 +22,7 @@ namespace Alepha::Hydrogen ::detail:: string_algorithms
}
struct VariableExpansionStreambuf
- : public Utility::StackableStreambuf
+ : public IOStreams::StackableStreambuf
{
public:
VarMap substitutions;
diff --git a/string_algorithms.h b/string_algorithms.h
index 1039c6f..978d529 100644
--- a/string_algorithms.h
+++ b/string_algorithms.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
@@ -18,7 +18,7 @@ static_assert( __cplusplus > 2020'00 );
#include
-#include
+#include
namespace Alepha::Hydrogen ::detail:: string_algorithms
{
@@ -51,11 +51,11 @@ namespace Alepha::Hydrogen ::detail:: string_algorithms
{}
};
- using StartSubstitutions= Utility::PushStack< StartSubstitutions_params >;
+ using StartSubstitutions= IOStreams::PushStack< StartSubstitutions_params >;
// Note that these function as a stack -- so `EndSubstitutions` will
// terminate the top of that stack.
- constexpr Utility::PopStack EndSubstitutions;
+ constexpr IOStreams::PopStack EndSubstitutions;
inline namespace impl
{
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;
+}
diff --git a/template_for_each.h b/template_for_each.h
index b20528d..79822da 100644
--- a/template_for_each.h
+++ b/template_for_each.h
@@ -1,12 +1,14 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
+#include
+
#include
#include "Concepts.h"
-namespace Alepha::inline Cavorite ::detail:: template_for_each_module
+namespace Alepha::Hydrogen ::detail:: template_for_each_m
{
inline namespace exports
{
@@ -80,7 +82,7 @@ namespace Alepha::inline Cavorite ::detail:: template_for_each_module
}
}
-namespace Alepha::Cavorite::inline exports::inline template_for_each_module
+namespace Alepha::Hydrogen::inline exports::inline template_for_each_m
{
- using namespace detail::template_for_each_module::exports;
+ using namespace detail::template_for_each_m::exports;
}
diff --git a/tuplize_args.h b/tuplize_args.h
index 7bd2e3a..5be55c5 100644
--- a/tuplize_args.h
+++ b/tuplize_args.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/type_lisp.h b/type_lisp.h
index d64a8ef..115b075 100644
--- a/type_lisp.h
+++ b/type_lisp.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
@@ -6,7 +6,7 @@ static_assert( __cplusplus > 2020'00 );
#include
-namespace Alepha::Hydrogen ::detail:: type_lisp
+namespace Alepha::Hydrogen ::detail:: type_lisp_m
{
inline namespace exports {}
@@ -117,8 +117,8 @@ namespace Alepha::Hydrogen ::detail:: type_lisp
};
}
-namespace Alepha::Hydrogen::inline exports::inline type_lisp
+namespace Alepha::Hydrogen::inline exports::inline type_lisp_m
{
- using namespace detail::type_lisp::exports;
+ using namespace detail::type_lisp_m::exports;
}
diff --git a/types.h b/types.h
index fcde434..79dfc58 100644
--- a/types.h
+++ b/types.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
diff --git a/word_wrap.cc b/word_wrap.cc
index dc3c27c..4f6bf5c 100644
--- a/word_wrap.cc
+++ b/word_wrap.cc
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020 );
+static_assert( __cplusplus > 2020'99 );
#include "word_wrap.h"
@@ -12,7 +12,7 @@ static_assert( __cplusplus > 2020 );
#include
-namespace Alepha::Hydrogen ::detail:: word_wrap
+namespace Alepha::Hydrogen ::detail:: word_wrap_m
{
namespace
{
@@ -41,7 +41,7 @@ namespace Alepha::Hydrogen ::detail:: word_wrap
}
struct WordWrapStreambuf
- : public Utility::StackableStreambuf
+ : public IOStreams::StackableStreambuf
{
public:
std::size_t maximumWidth= 0;
diff --git a/word_wrap.h b/word_wrap.h
index f2a23d7..d8c7b8d 100644
--- a/word_wrap.h
+++ b/word_wrap.h
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#pragma once
@@ -9,9 +9,9 @@ static_assert( __cplusplus > 2020'00 );
#include
#include
-#include
+#include
-namespace Alepha::Hydrogen ::detail:: word_wrap
+namespace Alepha::Hydrogen ::detail:: word_wrap_m
{
inline namespace exports
{
@@ -25,9 +25,9 @@ namespace Alepha::Hydrogen ::detail:: word_wrap
explicit StartWrap_params( const std::size_t width, const std::size_t nextLineOffset= 0 ) : width( width ), nextLineOffset( nextLineOffset ) {}
};
- using StartWrap= Utility::PushStack< StartWrap_params >;
+ using StartWrap= IOStreams::PushStack< StartWrap_params >;
- constexpr Utility::PopStack EndWrap;
+ constexpr IOStreams::PopStack EndWrap;
}
inline namespace impl
@@ -36,7 +36,7 @@ namespace Alepha::Hydrogen ::detail:: word_wrap
}
}
-namespace Alepha::Hydrogen::inline exports::inline word_wrap
+namespace Alepha::Hydrogen::inline exports::inline word_wrap_m
{
- using namespace detail::word_wrap::exports;
+ using namespace detail::word_wrap_m::exports;
}
diff --git a/word_wrap.test/0.cc b/word_wrap.test/0.cc
index 10bbaa1..1ffe0a0 100644
--- a/word_wrap.test/0.cc
+++ b/word_wrap.test/0.cc
@@ -1,4 +1,4 @@
-static_assert( __cplusplus > 2020'00 );
+static_assert( __cplusplus > 2020'99 );
#include "../word_wrap.h"