From c74737d01d316648906133e76e8bd6cd66958371 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Sun, 5 Nov 2023 00:18:35 -0400 Subject: [PATCH 01/38] Tests weren't running. They didn't get registered. --- Testing/TableTest.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Testing/TableTest.h b/Testing/TableTest.h index d8f9348..70b125c 100644 --- a/Testing/TableTest.h +++ b/Testing/TableTest.h @@ -933,6 +933,7 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test std::vector< TestDescription > tests; UniversalCases( std::initializer_list< TestDescription > initList ) + : tests( initList ) { for( const auto &desc: initList ) { From 353ab9afba4bc47697a6112e66da7abd51bc88cd Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Sun, 5 Nov 2023 00:22:09 -0400 Subject: [PATCH 02/38] More module namespaces. --- type_lisp.h | 6 +++--- word_wrap.cc | 2 +- word_wrap.h | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/type_lisp.h b/type_lisp.h index d64a8ef..371bfa1 100644 --- a/type_lisp.h +++ b/type_lisp.h @@ -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/word_wrap.cc b/word_wrap.cc index dc3c27c..c1237f7 100644 --- a/word_wrap.cc +++ b/word_wrap.cc @@ -12,7 +12,7 @@ static_assert( __cplusplus > 2020 ); #include -namespace Alepha::Hydrogen ::detail:: word_wrap +namespace Alepha::Hydrogen ::detail:: word_wrap_m { namespace { diff --git a/word_wrap.h b/word_wrap.h index f2a23d7..0edd707 100644 --- a/word_wrap.h +++ b/word_wrap.h @@ -11,7 +11,7 @@ static_assert( __cplusplus > 2020'00 ); #include -namespace Alepha::Hydrogen ::detail:: word_wrap +namespace Alepha::Hydrogen ::detail:: word_wrap_m { inline namespace exports { @@ -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; } From e0e99ead461ed5d52e2e7520a58ff1a8ae47f3f1 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Sun, 5 Nov 2023 00:30:15 -0400 Subject: [PATCH 03/38] Split out the tuple adapter from the test framework. Part of the larger testing framework simplification. --- Testing/TableTest.h | 35 ++------------------------- Utility/TupleAdapter.h | 55 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 33 deletions(-) create mode 100644 Utility/TupleAdapter.h diff --git a/Testing/TableTest.h b/Testing/TableTest.h index 70b125c..4d96289 100644 --- a/Testing/TableTest.h +++ b/Testing/TableTest.h @@ -32,6 +32,7 @@ static_assert( __cplusplus > 2020'00 ); #include #include +#include #include @@ -60,38 +61,6 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test 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 +69,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 >{}; } diff --git a/Utility/TupleAdapter.h b/Utility/TupleAdapter.h new file mode 100644 index 0000000..6caa139 --- /dev/null +++ b/Utility/TupleAdapter.h @@ -0,0 +1,55 @@ +static_assert( __cplusplus > 2020'00 ); + +#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; +} From 17456f9af4640a3d60558154b54985a6042469a6 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Sun, 5 Nov 2023 03:04:04 -0500 Subject: [PATCH 04/38] Drop some old, disabled code. --- Testing/TableTest.h | 124 -------------------------------------------- 1 file changed, 124 deletions(-) diff --git a/Testing/TableTest.h b/Testing/TableTest.h index 4d96289..aa93486 100644 --- a/Testing/TableTest.h +++ b/Testing/TableTest.h @@ -128,14 +128,6 @@ 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 > ) @@ -260,38 +252,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 > ) @@ -417,38 +377,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 > ) @@ -950,58 +878,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 From 226e0723226f9be34d858fdd12c48b99cfe3ac71 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Sun, 5 Nov 2023 23:58:44 -0500 Subject: [PATCH 05/38] Retire the non-universal legacy cases. --- Testing/TableTest.h | 169 -------------------------------------------- 1 file changed, 169 deletions(-) diff --git a/Testing/TableTest.h b/Testing/TableTest.h index aa93486..1dd5c53 100644 --- a/Testing/TableTest.h +++ b/Testing/TableTest.h @@ -648,175 +648,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 From ceb87f118f0a5d6ef212cf45ce8e6d52b33af4db Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Mon, 6 Nov 2023 00:11:31 -0500 Subject: [PATCH 06/38] Split out the debug printing helpers for tests. --- Testing/TableTest.h | 172 +--------------------------------- Testing/printDebugging.h | 193 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 197 insertions(+), 168 deletions(-) create mode 100644 Testing/printDebugging.h diff --git a/Testing/TableTest.h b/Testing/TableTest.h index 1dd5c53..f3377e7 100644 --- a/Testing/TableTest.h +++ b/Testing/TableTest.h @@ -17,15 +17,6 @@ static_assert( __cplusplus > 2020'00 ); #include #include -#include -#include -#include -#include -#include - -#include -#include - #include #include @@ -36,31 +27,22 @@ static_assert( __cplusplus > 2020'00 ); #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 ); - enum class TestResult { Passed, Failed }; struct BlankBase {}; @@ -475,7 +457,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 @@ -495,151 +476,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 { diff --git a/Testing/printDebugging.h b/Testing/printDebugging.h new file mode 100644 index 0000000..9610a3f --- /dev/null +++ b/Testing/printDebugging.h @@ -0,0 +1,193 @@ +static_assert( __cplusplus > 2020'00 ); + +#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 > + 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 + 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; +} From 3942d98684ca64d4e4fc0f856b0aa6b77ef3d706 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Mon, 6 Nov 2023 22:42:30 -0500 Subject: [PATCH 07/38] Unify the Universal Handler forms for the Table test. --- Testing/TableTest.h | 244 ++------------------------------------------ 1 file changed, 10 insertions(+), 234 deletions(-) diff --git a/Testing/TableTest.h b/Testing/TableTest.h index f3377e7..5834ad7 100644 --- a/Testing/TableTest.h +++ b/Testing/TableTest.h @@ -60,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 ) { @@ -93,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() ) @@ -108,100 +101,12 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test } } {} -#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; @@ -234,7 +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 1 template< typename T > requires( not SameAs< T, void > ) BasicUniversalHandler( std::type_identity< T > ) : impl @@ -307,135 +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 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 ) @@ -443,7 +220,6 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test { std::function{ f } }; }; - namespace exports { template< FunctionVariable auto, OutputMode outputMode= OutputMode::All > struct TableTest; @@ -523,7 +299,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; From ab13e7b58d5438104ba8930af55a34a561cd7104 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Mon, 6 Nov 2023 22:58:31 -0500 Subject: [PATCH 08/38] Started work towards stream based output for debug printing. This will let me fix the double-case printing in the universal test case handler. --- Testing/printDebugging.h | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/Testing/printDebugging.h b/Testing/printDebugging.h index 9610a3f..c6f86de 100644 --- a/Testing/printDebugging.h +++ b/Testing/printDebugging.h @@ -41,10 +41,9 @@ namespace Alepha::Hydrogen::Testing ::detail:: printDebugging_m using namespace std::literals::string_literals; template< OutputMode outputMode, typename T > - std::string - stringifyValue( const T &v ) + void + streamValue( const T &v, std::ostream &oss ) { - 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 > > ) { @@ -68,11 +67,12 @@ namespace Alepha::Hydrogen::Testing ::detail:: printDebugging_m } else if constexpr( Meta::is_ostreamable_v< T > ) { - return IOStreams::stringify( v ); + oss << v; } else if constexpr( Meta::is_optional_v< T > ) { - return v.has_value() ? stringifyValue< outputMode >( v.value() ) : ""s; + if( not v.has_value() ) oss << ""; + else streamValue< outputMode >( v.value(), oss ); } else if constexpr( Meta::is_sequence_v< T > ) { @@ -85,14 +85,19 @@ namespace Alepha::Hydrogen::Testing ::detail:: printDebugging_m 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; + 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; - return stringifyValue< outputMode >( std::tie( first, second ) ); + streamValue< outputMode >( std::tie( first, second ), oss ); } else if constexpr( Meta::is_tuple_v< T > ) { @@ -101,7 +106,8 @@ namespace Alepha::Hydrogen::Testing ::detail:: printDebugging_m { if( not first ) oss << ", "; first= false; - oss << std::endl << stringifyValue< outputMode >( elem ); + oss << std::endl; + streamValue< outputMode >( elem, oss ); }; oss << std::endl << ']' << std::endl; } @@ -109,7 +115,7 @@ namespace Alepha::Hydrogen::Testing ::detail:: printDebugging_m { if( false ) ; // For alignment else if( v == TotalOrder::less ) oss << "less"; - else if ( v == TotalOrder::equal ) oss << "equal"; + else if( v == TotalOrder::equal ) oss << "equal"; else if( v == TotalOrder::greater ) oss << "greater"; else throw std::logic_error( "Impossible `TotalOrder` condition." ); } @@ -117,6 +123,14 @@ namespace Alepha::Hydrogen::Testing ::detail:: printDebugging_m { 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(); } From 3b9fb24eec478441c97ec84a0913463d2566ab78 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Tue, 7 Nov 2023 04:15:31 -0500 Subject: [PATCH 09/38] Adopt the MPL-2.0 license. --- LICENSE | 26 +--- MPL-LICENSE.txt | 373 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 376 insertions(+), 23 deletions(-) create mode 100644 MPL-LICENSE.txt 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. From d1157cabf7bbe9d700054a4de2d6b51b23c6e564 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Tue, 7 Nov 2023 05:30:50 -0500 Subject: [PATCH 10/38] Start the C++23 adoption. --- Alepha.h | 2 +- AutoRAII.h | 2 +- AutoRAII.test/0.cc | 2 +- Blob.h | 2 +- Buffer.h | 2 +- Capabilities.h | 2 +- Concepts.h | 2 +- Console.cc | 2 +- Console.h | 2 +- ConstexprString.h | 2 +- DataChain.h | 2 +- Enum.h | 2 +- Exception.h | 2 +- Exception.test/exception.cc | 2 +- IOStreams/IStreamable.h | 2 +- IOStreams/IStreamable.test/0.cc | 2 +- IOStreams/OStreamable.h | 2 +- IOStreams/OStreamable.test/0.cc | 2 +- IOStreams/StreamState.h | 2 +- IOStreams/String.h | 2 +- IOStreams/delimiters.h | 2 +- IOStreams/streamable.h | 2 +- IOStreams/streamable.test/0.cc | 2 +- Meta/Container/string.h | 2 +- Meta/Container/vector.h | 2 +- Meta/Meta.test/traits.cc | 2 +- Meta/dep_value.h | 2 +- Meta/find.h | 2 +- Meta/functional.h | 2 +- Meta/is_deque.h | 2 +- Meta/is_forward_list.h | 2 +- Meta/is_functional.h | 2 +- Meta/is_list.h | 2 +- Meta/is_map.h | 2 +- Meta/is_optional.h | 2 +- Meta/is_pair.h | 2 +- Meta/is_product_type.h | 2 +- Meta/is_sequence.h | 2 +- Meta/is_set.h | 2 +- Meta/is_streamable.h | 2 +- Meta/is_string.h | 2 +- Meta/is_tuple.h | 2 +- Meta/is_vector.h | 2 +- Meta/overload.h | 2 +- Meta/pair_decay.h | 2 +- Meta/product_type_decay.h | 2 +- Meta/sequence_kind.h | 2 +- Meta/tuple_decay.h | 2 +- Meta/type_algorithm.h | 2 +- Meta/type_traits.h | 2 +- Meta/type_value.h | 2 +- Mockination/MockCondition.h | 2 +- Mockination/MockFunction.h | 2 +- Mockination/MockMutex.h | 2 +- ProgramOptions.cc | 2 +- ProgramOptions.h | 2 +- Proof/Attestation.h | 2 +- Reflection/aggregate_initializer_size.h | 2 +- Reflection/aggregate_members.h | 2 +- Reflection/detail/config.h | 2 +- Reflection/tuplizeAggregate.h | 2 +- Reflection/tuplizeAggregate.test/0.cc | 2 +- StaticValue.h | 2 +- Stud/type_traits.h | 2 +- Testing/TableTest.h | 2 +- Testing/TableTest.test/test.cc | 2 +- Testing/TableTest.test/test2.cc | 2 +- Testing/colors.h | 2 +- Testing/printDebugging.h | 2 +- Testing/test.cc | 2 +- Testing/test.h | 2 +- Testing/testlib.cc | 2 +- Thread.h | 2 +- Thread.test/thread.cc | 2 +- TotalOrder.h | 2 +- Truss/basetypes.h | 2 +- Truss/condition_variable.h | 2 +- Truss/function.h | 2 +- Truss/m2.h | 2 +- Truss/memory.h | 2 +- Truss/mutex.h | 2 +- Truss/thread.h | 2 +- Truss/thread_common.h | 2 +- Utility/StackableStreambuf.cc | 2 +- Utility/StackableStreambuf.h | 2 +- Utility/StackableStreambuf.test/0.cc | 2 +- Utility/StaticValue.h | 2 +- Utility/TupleAdapter.h | 2 +- Utility/evaluation_helpers.h | 2 +- auto_comparable.h | 2 +- cmake/rules.cmake | 2 +- comparisons.h | 2 +- comparisons.test/0.cc | 2 +- dumbhash.test/0.cc | 2 +- error.h | 2 +- example.cc | 2 +- function_traits.h | 2 +- meta.h | 2 +- string_algorithms.cc | 2 +- string_algorithms.h | 2 +- template_for_each.h | 2 +- tuplize_args.h | 2 +- type_lisp.h | 2 +- types.h | 2 +- word_wrap.cc | 2 +- word_wrap.h | 2 +- word_wrap.test/0.cc | 2 +- 107 files changed, 107 insertions(+), 107 deletions(-) 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/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..987bba1 100644 --- a/Blob.h +++ b/Blob.h @@ -1,4 +1,4 @@ -static_assert( __cplusplus > 2020'00 ); +static_assert( __cplusplus > 2020'99 ); #pragma once diff --git a/Buffer.h b/Buffer.h index bce25ca..aae3bf6 100644 --- a/Buffer.h +++ b/Buffer.h @@ -1,4 +1,4 @@ -static_assert( __cplusplus > 2020'00 ); +static_assert( __cplusplus > 2020'99 ); #pragma once diff --git a/Capabilities.h b/Capabilities.h index ad7944d..6b29093 100644 --- a/Capabilities.h +++ b/Capabilities.h @@ -1,4 +1,4 @@ -static_assert( __cplusplus > 2020'00 ); +static_assert( __cplusplus > 2020'99 ); #pragma once diff --git a/Concepts.h b/Concepts.h index 2804055..04716ef 100644 --- a/Concepts.h +++ b/Concepts.h @@ -1,4 +1,4 @@ -static_assert( __cplusplus > 2020'00 ); +static_assert( __cplusplus > 2020'99 ); #pragma once diff --git a/Console.cc b/Console.cc index 9110a9c..b7fd0e1 100644 --- a/Console.cc +++ b/Console.cc @@ -1,4 +1,4 @@ -static_assert( __cplusplus > 2020'00 ); +static_assert( __cplusplus > 2020'99 ); #include "Console.h" 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..69bec6f 100644 --- a/ConstexprString.h +++ b/ConstexprString.h @@ -1,4 +1,4 @@ -static_assert( __cplusplus > 2020'00 ); +static_assert( __cplusplus > 2020'99 ); #pragma once 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..5a2ccc6 100644 --- a/Enum.h +++ b/Enum.h @@ -1,4 +1,4 @@ -static_assert( __cplusplus > 2020'00 ); +static_assert( __cplusplus > 2020'99 ); #pragma once diff --git a/Exception.h b/Exception.h index 37a9de7..8692a2a 100644 --- a/Exception.h +++ b/Exception.h @@ -1,4 +1,4 @@ -static_assert( __cplusplus > 2020'00 ); +static_assert( __cplusplus > 2020'99 ); #pragma once 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/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/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/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..6e93eec 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 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..b9db009 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 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..1b684c6 100644 --- a/ProgramOptions.cc +++ b/ProgramOptions.cc @@ -1,4 +1,4 @@ -static_assert( __cplusplus > 2020'00 ); +static_assert( __cplusplus > 2020'99 ); #include "ProgramOptions.h" diff --git a/ProgramOptions.h b/ProgramOptions.h index 0c35e5a..148c299 100644 --- a/ProgramOptions.h +++ b/ProgramOptions.h @@ -1,4 +1,4 @@ -static_assert( __cplusplus > 2020'00 ); +static_assert( __cplusplus > 2020'99 ); #pragma once 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..768eabf 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 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/Testing/TableTest.h b/Testing/TableTest.h index 5834ad7..a9ebf19 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 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 index c6f86de..694b1a1 100644 --- a/Testing/printDebugging.h +++ b/Testing/printDebugging.h @@ -1,4 +1,4 @@ -static_assert( __cplusplus > 2020'00 ); +static_assert( __cplusplus > 2020'99 ); #pragma once 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..2bfde46 100644 --- a/Thread.h +++ b/Thread.h @@ -1,4 +1,4 @@ -static_assert( __cplusplus > 2020'00 ); +static_assert( __cplusplus > 2020'99 ); #pragma once diff --git a/Thread.test/thread.cc b/Thread.test/thread.cc index 0bb9eb4..c692a8e 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 diff --git a/TotalOrder.h b/TotalOrder.h index e18f722..f7b1f01 100644 --- a/TotalOrder.h +++ b/TotalOrder.h @@ -1,4 +1,4 @@ -static_assert( __cplusplus > 2020'00 ); +static_assert( __cplusplus > 2020'99 ); #pragma once 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/StackableStreambuf.cc b/Utility/StackableStreambuf.cc index 44e61bd..8ff763b 100644 --- a/Utility/StackableStreambuf.cc +++ b/Utility/StackableStreambuf.cc @@ -1,4 +1,4 @@ -static_assert( __cplusplus > 2020'00 ); +static_assert( __cplusplus > 2020'99 ); #include "StackableStreambuf.h" diff --git a/Utility/StackableStreambuf.h b/Utility/StackableStreambuf.h index eacf827..1d94284 100644 --- a/Utility/StackableStreambuf.h +++ b/Utility/StackableStreambuf.h @@ -1,4 +1,4 @@ -static_assert( __cplusplus > 2020'00 ); +static_assert( __cplusplus > 2020'99 ); #pragma once diff --git a/Utility/StackableStreambuf.test/0.cc b/Utility/StackableStreambuf.test/0.cc index 388e2dc..69d6a37 100644 --- a/Utility/StackableStreambuf.test/0.cc +++ b/Utility/StackableStreambuf.test/0.cc @@ -1,4 +1,4 @@ -static_assert( __cplusplus > 2020'00 ); +static_assert( __cplusplus > 2020'99 ); #include "../StackableStreambuf.h" 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 index 6caa139..24f1ed2 100644 --- a/Utility/TupleAdapter.h +++ b/Utility/TupleAdapter.h @@ -1,4 +1,4 @@ -static_assert( __cplusplus > 2020'00 ); +static_assert( __cplusplus > 2020'99 ); #pragma once 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/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..988c2fb 100644 --- a/cmake/rules.cmake +++ b/cmake/rules.cmake @@ -1,6 +1,6 @@ #cmake_policy( SET CMP0002 OLD ) -add_compile_options( -std=c++20 ) +add_compile_options( -std=c++23 ) add_compile_options( -fdiagnostics-column-unit=byte ) add_compile_options( -fconcepts-diagnostics-depth=0 ) include_directories( ${CMAKE_BINARY_DIR} . ) 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/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..164ef9d 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" diff --git a/string_algorithms.h b/string_algorithms.h index 1039c6f..d281893 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 diff --git a/template_for_each.h b/template_for_each.h index b20528d..8da41fe 100644 --- a/template_for_each.h +++ b/template_for_each.h @@ -1,4 +1,4 @@ -static_assert( __cplusplus > 2020'00 ); +static_assert( __cplusplus > 2020'99 ); #pragma once 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 371bfa1..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 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 c1237f7..9a63f76 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" diff --git a/word_wrap.h b/word_wrap.h index 0edd707..d4a4606 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 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" From adbbdf940fa6e5bf7eba4cf2ac932a51cbe28381 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Wed, 8 Nov 2023 00:22:06 -0500 Subject: [PATCH 11/38] I found the original `Mailbox` implementation from over a decade ago. --- Atomic/Mailbox.h | 193 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 Atomic/Mailbox.h 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 + */ From 5a415bdf3a423677dc5853e9e052d8ad2217b2ac Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Wed, 8 Nov 2023 04:24:09 -0500 Subject: [PATCH 12/38] Total order namespace update. --- TotalOrder.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/TotalOrder.h b/TotalOrder.h index f7b1f01..7bf3fb3 100644 --- a/TotalOrder.h +++ b/TotalOrder.h @@ -2,11 +2,13 @@ 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; } From eb2ac15e65dca8345e63832be0e2b6c56457f58e Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Wed, 8 Nov 2023 04:31:24 -0500 Subject: [PATCH 13/38] More namespace hydrogenation. --- ConstexprString.h | 14 +++++++------- template_for_each.h | 8 +++++--- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/ConstexprString.h b/ConstexprString.h index 69bec6f..ab12897 100644 --- a/ConstexprString.h +++ b/ConstexprString.h @@ -2,6 +2,8 @@ static_assert( __cplusplus > 2020'99 ); #pragma once +#include + #include #include @@ -9,9 +11,7 @@ static_assert( __cplusplus > 2020'99 ); #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/template_for_each.h b/template_for_each.h index 8da41fe..79822da 100644 --- a/template_for_each.h +++ b/template_for_each.h @@ -2,11 +2,13 @@ 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; } From 8f96c9e888cd9dd2e2ab56e76ebbcc20adae4fa3 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Wed, 8 Nov 2023 04:33:09 -0500 Subject: [PATCH 14/38] Rehabilitate the `Thread` type and test. --- CMakeLists.txt | 1 + Thread.h | 28 ++++++++++++++-------------- Thread.test/CMakeLists.txt | 3 +++ Thread.test/Makefile | 10 ---------- Thread.test/thread.cc | 4 ++-- 5 files changed, 20 insertions(+), 26 deletions(-) create mode 100644 Thread.test/CMakeLists.txt delete mode 100644 Thread.test/Makefile diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d24654..afd6717 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,7 @@ add_subdirectory( Exception.test ) add_subdirectory( word_wrap.test ) add_subdirectory( string_algorithms.test ) add_subdirectory( tuplize_args.test ) +add_subdirectory( Thread.test ) # Sample applications add_executable( example example.cc ) diff --git a/Thread.h b/Thread.h index 2bfde46..240c4c9 100644 --- a/Thread.h +++ b/Thread.h @@ -4,9 +4,9 @@ static_assert( __cplusplus > 2020'99 ); #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 c692a8e..f7f7544 100644 --- a/Thread.test/thread.cc +++ b/Thread.test/thread.cc @@ -6,7 +6,7 @@ static_assert( __cplusplus > 2020'99 ); #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 ) From 25b9cf0ac8c7f8d8e499dcbb822bfe5b31d91283 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Wed, 8 Nov 2023 04:42:17 -0500 Subject: [PATCH 15/38] More hydrogenation. --- Enum.h | 12 +++++++----- ProgramOptions.cc | 2 +- ProgramOptions.h | 10 +++++----- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/Enum.h b/Enum.h index 5a2ccc6..ab3ce67 100644 --- a/Enum.h +++ b/Enum.h @@ -2,6 +2,8 @@ static_assert( __cplusplus > 2020'99 ); #pragma once +#include + #include #include @@ -17,7 +19,7 @@ static_assert( __cplusplus > 2020'99 ); #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/ProgramOptions.cc b/ProgramOptions.cc index 1b684c6..ef247cb 100644 --- a/ProgramOptions.cc +++ b/ProgramOptions.cc @@ -10,7 +10,7 @@ static_assert( __cplusplus > 2020'99 ); #include #include -namespace Alepha::Cavorite ::detail:: program_options +namespace Alepha::Hydrogen ::detail:: ProgramOptions_m { namespace { diff --git a/ProgramOptions.h b/ProgramOptions.h index 148c299..b50426c 100644 --- a/ProgramOptions.h +++ b/ProgramOptions.h @@ -96,7 +96,7 @@ static_assert( __cplusplus > 2020'99 ); #include -namespace Alepha::inline Cavorite ::detail:: program_options +namespace Alepha::Hydrogen ::detail:: ProgramOptions_m { inline namespace exports {} @@ -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; } From 6a77318df87ef2c2a0ee59c84e30e466da9015b1 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Thu, 9 Nov 2023 14:28:52 -0500 Subject: [PATCH 16/38] Fix typo in options code. --- ProgramOptions.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ProgramOptions.h b/ProgramOptions.h index b50426c..c6edb00 100644 --- a/ProgramOptions.h +++ b/ProgramOptions.h @@ -290,7 +290,7 @@ namespace Alepha::Hydrogen ::detail:: ProgramOptions_m // 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::Hydrogen ::detail:: ProgramOptions_m }; handler( parsed ); }; - return registerHandler( handler ); + return registerHandler( wrapped ); } else { From 352bf22a687a8322759855463bbc839f8d172fd7 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Thu, 9 Nov 2023 14:29:40 -0500 Subject: [PATCH 17/38] Fix problems in the stackable streambuf cleanup. --- Utility/StackableStreambuf.cc | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/Utility/StackableStreambuf.cc b/Utility/StackableStreambuf.cc index 8ff763b..fc9fba2 100644 --- a/Utility/StackableStreambuf.cc +++ b/Utility/StackableStreambuf.cc @@ -2,16 +2,24 @@ static_assert( __cplusplus > 2020'99 ); #include "StackableStreambuf.h" +#include + namespace Alepha::Hydrogen::Utility::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 From e3ceed0233bb7d68c677cea4daa7585634d06d5b Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 10 Nov 2023 01:49:42 -0500 Subject: [PATCH 18/38] A unix file descriptor streambuf for writing. --- IOStreams/CMakeLists.txt | 1 + IOStreams/OutUnixFileBuf.h | 47 ++++++++++++++++++++ IOStreams/OutUnixFileBuf.test/0.cc | 28 ++++++++++++ IOStreams/OutUnixFileBuf.test/CMakeLists.txt | 1 + 4 files changed, 77 insertions(+) create mode 100644 IOStreams/OutUnixFileBuf.h create mode 100644 IOStreams/OutUnixFileBuf.test/0.cc create mode 100644 IOStreams/OutUnixFileBuf.test/CMakeLists.txt diff --git a/IOStreams/CMakeLists.txt b/IOStreams/CMakeLists.txt index c3ed932..ade9e39 100644 --- a/IOStreams/CMakeLists.txt +++ b/IOStreams/CMakeLists.txt @@ -1,3 +1,4 @@ add_subdirectory( IStreamable.test ) add_subdirectory( OStreamable.test ) add_subdirectory( streamable.test ) +add_subdirectory( OutUnixFileBuf.test ) 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 ) From 2d45eab99cd7ea56c116a1fba83129a55d724629 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 10 Nov 2023 01:50:37 -0500 Subject: [PATCH 19/38] Remove gnu dependency from `Console`. --- Console.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Console.cc b/Console.cc index b7fd0e1..254ab6d 100644 --- a/Console.cc +++ b/Console.cc @@ -13,9 +13,9 @@ static_assert( __cplusplus > 2020'99 ); #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 ) {} }; From 85762533820a3f10253012a5f36dee9a557effed Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 10 Nov 2023 04:11:01 -0500 Subject: [PATCH 20/38] Cleanup some of the table test code. --- Testing/TableTest.h | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/Testing/TableTest.h b/Testing/TableTest.h index a9ebf19..7d714db 100644 --- a/Testing/TableTest.h +++ b/Testing/TableTest.h @@ -269,22 +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 From a2af64e37e23797ab9949668a2d0afae5f3a0fe4 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 10 Nov 2023 11:49:03 -0500 Subject: [PATCH 21/38] Clang seemed to think this was necessary. I'm not certain if it is, but it's pretty harmless to keep on the template. --- Meta/Container/vector.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Meta/Container/vector.h b/Meta/Container/vector.h index 6e93eec..16881ac 100644 --- a/Meta/Container/vector.h +++ b/Meta/Container/vector.h @@ -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 ) { From 914c7c7ab329381da91ec404a227f65f47f8a99b Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 10 Nov 2023 11:51:18 -0500 Subject: [PATCH 22/38] Clang also complained about this... --- Capabilities.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Capabilities.h b/Capabilities.h index 6b29093..92ffcb3 100644 --- a/Capabilities.h +++ b/Capabilities.h @@ -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 > From 0ef618e977f2edf53a4ea82f9da265c2c039cf43 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 10 Nov 2023 11:51:29 -0500 Subject: [PATCH 23/38] Optional only ever takes one argument. Clang warned about this -- Clang's variadic template argument handling seems to significantly differ from GCC's. I should test this out on godbolt sometime. --- Meta/is_optional.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Meta/is_optional.h b/Meta/is_optional.h index b9db009..b7459c2 100644 --- a/Meta/is_optional.h +++ b/Meta/is_optional.h @@ -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; From 13a1ed321a3a719b1e02ab0494d5244a261a1810 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 10 Nov 2023 11:55:18 -0500 Subject: [PATCH 24/38] Discard some of the dead old bits of Reflection. --- Reflection/aggregate_members.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/Reflection/aggregate_members.h b/Reflection/aggregate_members.h index 768eabf..263decc 100644 --- a/Reflection/aggregate_members.h +++ b/Reflection/aggregate_members.h @@ -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 > > From 03e2f06eb8da0360e2963f31464465e9cd7a4289 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 10 Nov 2023 11:55:49 -0500 Subject: [PATCH 25/38] Some stuff seems to be able to build with clang. So here's some of the necessary options. --- cmake/rules.cmake | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/cmake/rules.cmake b/cmake/rules.cmake index 988c2fb..897a0c8 100644 --- a/cmake/rules.cmake +++ b/cmake/rules.cmake @@ -1,8 +1,23 @@ #cmake_policy( SET CMP0002 OLD ) -add_compile_options( -std=c++23 ) +# 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" ) From b8f294b6403414258a61520b2e8b6bcbb358c469 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 10 Nov 2023 12:06:28 -0500 Subject: [PATCH 26/38] Adding a format style guide. --- Format | 388 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 388 insertions(+) create mode 100644 Format 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. From 6d8e503562553907f22c14868db4581b4a59ed36 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 10 Nov 2023 13:19:19 -0500 Subject: [PATCH 27/38] Initial style guide. --- Style | 223 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 Style 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. From db70cb3b08103377ffae17cd38bac89298de30e2 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 10 Nov 2023 13:30:08 -0500 Subject: [PATCH 28/38] Relocate `StackableStreambuf` to IOStreams. --- IOStreams/CMakeLists.txt | 6 ++++++ {Utility => IOStreams}/StackableStreambuf.cc | 2 +- {Utility => IOStreams}/StackableStreambuf.h | 4 ++-- {Utility => IOStreams}/StackableStreambuf.test/0.cc | 0 .../StackableStreambuf.test/CMakeLists.txt | 0 Utility/CMakeLists.txt | 5 ----- string_algorithms.cc | 2 +- string_algorithms.h | 6 +++--- word_wrap.cc | 2 +- word_wrap.h | 6 +++--- 10 files changed, 17 insertions(+), 16 deletions(-) rename {Utility => IOStreams}/StackableStreambuf.cc (97%) rename {Utility => IOStreams}/StackableStreambuf.h (88%) rename {Utility => IOStreams}/StackableStreambuf.test/0.cc (100%) rename {Utility => IOStreams}/StackableStreambuf.test/CMakeLists.txt (100%) diff --git a/IOStreams/CMakeLists.txt b/IOStreams/CMakeLists.txt index ade9e39..21d3f4d 100644 --- a/IOStreams/CMakeLists.txt +++ b/IOStreams/CMakeLists.txt @@ -2,3 +2,9 @@ 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/Utility/StackableStreambuf.cc b/IOStreams/StackableStreambuf.cc similarity index 97% rename from Utility/StackableStreambuf.cc rename to IOStreams/StackableStreambuf.cc index fc9fba2..c7efdab 100644 --- a/Utility/StackableStreambuf.cc +++ b/IOStreams/StackableStreambuf.cc @@ -4,7 +4,7 @@ static_assert( __cplusplus > 2020'99 ); #include -namespace Alepha::Hydrogen::Utility::detail::stackable_streambuf +namespace Alepha::Hydrogen::IOStreams::detail::stackable_streambuf { namespace { diff --git a/Utility/StackableStreambuf.h b/IOStreams/StackableStreambuf.h similarity index 88% rename from Utility/StackableStreambuf.h rename to IOStreams/StackableStreambuf.h index 1d94284..5dfd60f 100644 --- a/Utility/StackableStreambuf.h +++ b/IOStreams/StackableStreambuf.h @@ -10,7 +10,7 @@ static_assert( __cplusplus > 2020'99 ); #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 100% rename from Utility/StackableStreambuf.test/0.cc rename to IOStreams/StackableStreambuf.test/0.cc diff --git a/Utility/StackableStreambuf.test/CMakeLists.txt b/IOStreams/StackableStreambuf.test/CMakeLists.txt similarity index 100% rename from Utility/StackableStreambuf.test/CMakeLists.txt rename to IOStreams/StackableStreambuf.test/CMakeLists.txt 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/string_algorithms.cc b/string_algorithms.cc index 164ef9d..8a49451 100644 --- a/string_algorithms.cc +++ b/string_algorithms.cc @@ -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 d281893..978d529 100644 --- a/string_algorithms.h +++ b/string_algorithms.h @@ -18,7 +18,7 @@ static_assert( __cplusplus > 2020'99 ); #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/word_wrap.cc b/word_wrap.cc index 9a63f76..4f6bf5c 100644 --- a/word_wrap.cc +++ b/word_wrap.cc @@ -41,7 +41,7 @@ namespace Alepha::Hydrogen ::detail:: word_wrap_m } 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 d4a4606..d8c7b8d 100644 --- a/word_wrap.h +++ b/word_wrap.h @@ -9,7 +9,7 @@ static_assert( __cplusplus > 2020'99 ); #include #include -#include +#include namespace Alepha::Hydrogen ::detail:: word_wrap_m { @@ -25,9 +25,9 @@ namespace Alepha::Hydrogen ::detail:: word_wrap_m 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 From b21e8818e774d7d16f799866b5e1e3768d769572 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 10 Nov 2023 22:51:03 -0500 Subject: [PATCH 29/38] Make a portable hook for getting program name. --- ProgramOptions.cc | 9 ++++----- System/programName.h | 46 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 System/programName.h diff --git a/ProgramOptions.cc b/ProgramOptions.cc index ef247cb..461d14b 100644 --- a/ProgramOptions.cc +++ b/ProgramOptions.cc @@ -10,6 +10,8 @@ static_assert( __cplusplus > 2020'99 ); #include #include +#include + namespace Alepha::Hydrogen ::detail:: ProgramOptions_m { namespace @@ -205,9 +207,7 @@ namespace Alepha::Hydrogen ::detail:: ProgramOptions_m 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::Hydrogen ::detail:: ProgramOptions_m { 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/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; +} From 0d66b5a135f3e56041e0252e1f6d7cf8f598c8c2 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 10 Nov 2023 23:15:37 -0500 Subject: [PATCH 30/38] This should be in C++23, but some compilers/libraries still lack it. --- lifetime.h | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 lifetime.h diff --git a/lifetime.h b/lifetime.h new file mode 100644 index 0000000..aa44f0e --- /dev/null +++ b/lifetime.h @@ -0,0 +1,39 @@ +static_assert( __cplusplus > 2020'00 ); + +#pragma once + +#include + +#include + +#include + +namespace Alepha::Hydrogen ::detail:: lifetime_m +{ + inline namespace exports + { + // When C++23 arrives, just turn these into `using` statements. + + template< typename T > + std::add_pointer_t< T > + start_lifetime_as( void *const mp ) noexcept + { + const auto raw= new( mp ) std::byte[ sizeof( T ) ]; + const auto data= reinterpret_cast< std::add_pointer_t< T > >( raw ); + return std::launder( data ); + } + + template< typename T > + std::add_pointer_t< std::add_const_t< T > > + start_lifetime_as( const void *const p ) noexcept + { + const auto mp= const_cast< void * >( p ); + return start_lifetime_as< std::add_const_t< T > >( mp ); + } + } +} + +namespace Alepha::Hydrogen::inline exports::inline lifetime_m +{ + using namespace detail::lifetime_m::exports; +} From 0728478f246a36afb3891ee11b7eddbc31257d29 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 10 Nov 2023 23:18:07 -0500 Subject: [PATCH 31/38] Assertion facility. --- CMakeLists.txt | 1 + assertion.h | 30 ++++++++++++++++++++++++++++++ assertion.test/0.cc | 4 ++++ assertion.test/CMakeLists.txt | 1 + 4 files changed, 36 insertions(+) create mode 100644 assertion.h create mode 100644 assertion.test/0.cc create mode 100644 assertion.test/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index afd6717..039b94d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,7 @@ add_subdirectory( word_wrap.test ) add_subdirectory( string_algorithms.test ) add_subdirectory( tuplize_args.test ) add_subdirectory( Thread.test ) +add_subdirectory( assertion.test ) # Sample applications add_executable( example example.cc ) diff --git a/assertion.h b/assertion.h new file mode 100644 index 0000000..17f4b07 --- /dev/null +++ b/assertion.h @@ -0,0 +1,30 @@ +static_assert( __cplusplus > 2020'99 ); + +#pragma once + +#include + +#include + +namespace Alepha::Hydrogen ::detail:: assertion_m +{ + inline namespace exports + { + class ProgrammerExpectationViolation : public virtual Violation {}; + + inline void + assertion( const bool b ) + { + if( not b ) + { + throw build_exception< ProgrammerExpectationViolation >( + "Expectation violated." ); + } + } + } +} + +namespace Alepha::Hydrogen::inline exports::inline assertion_m +{ + using namespace detail::assertion_m::exports; +} diff --git a/assertion.test/0.cc b/assertion.test/0.cc new file mode 100644 index 0000000..8603305 --- /dev/null +++ b/assertion.test/0.cc @@ -0,0 +1,4 @@ +static_assert( __cplusplus > 2020'99 ); + +#include "../assertion.h" + diff --git a/assertion.test/CMakeLists.txt b/assertion.test/CMakeLists.txt new file mode 100644 index 0000000..b099603 --- /dev/null +++ b/assertion.test/CMakeLists.txt @@ -0,0 +1 @@ +unit_test( 0 ) From e61e754d561d5914b1a3298774c63ac41c0a37e8 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 10 Nov 2023 23:24:37 -0500 Subject: [PATCH 32/38] Concept hydrogenation. --- Concepts.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Concepts.h b/Concepts.h index 04716ef..6c2c19c 100644 --- a/Concepts.h +++ b/Concepts.h @@ -11,7 +11,7 @@ static_assert( __cplusplus > 2020'99 ); #include "meta.h" #include "function_traits.h" -namespace Alepha::Hydrogen ::detail:: core_concepts +namespace Alepha::Hydrogen ::detail:: Concepts_m { inline namespace exports { @@ -282,7 +282,7 @@ namespace Alepha::Hydrogen ::detail:: core_concepts } } -namespace Alepha::Hydrogen::inline exports::inline core_concepts +namespace Alepha::Hydrogen::inline exports::inline Concepts_m::inline Concepts { - using namespace detail::core_concepts::exports; + using namespace detail::Concepts_m::exports; } From e2c5d28c5b48050b004f14cfd768310592b4bff7 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 10 Nov 2023 23:25:12 -0500 Subject: [PATCH 33/38] Constness. --- CMakeLists.txt | 1 + Constness.h | 152 ++++++++++++++++++++++++++++++++++ Constness.test/0.cc | 3 + Constness.test/CMakeLists.txt | 1 + 4 files changed, 157 insertions(+) create mode 100644 Constness.h create mode 100644 Constness.test/0.cc create mode 100644 Constness.test/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 039b94d..4b46b65 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ add_subdirectory( string_algorithms.test ) add_subdirectory( tuplize_args.test ) add_subdirectory( Thread.test ) add_subdirectory( assertion.test ) +add_subdirectory( Constness.test ) # Sample applications add_executable( example example.cc ) diff --git a/Constness.h b/Constness.h new file mode 100644 index 0000000..3d58d64 --- /dev/null +++ b/Constness.h @@ -0,0 +1,152 @@ +static_assert( __cplusplus > 2020'00 ); + +#pragma once + +#include + +#include +#include + +#include "meta.h" + +namespace Alepha::Hydrogen ::detail:: Constness_m +{ + inline namespace exports + { + /*! + * Conditional constness primitive. + * + * C++17 and beyond have many conditional attributes on functions. Conditional `noexcept`, + * conditional `explicit`, and even conditional instantiation. The `noexcept` and `explicit` + * are conditional on a boolean condition. This enum permits making templates which + * are conditionally const, depending upon instantiation. + * + * Simple example: + * + * ``` + * template< Constness constness > + * struct IntWrapper + * { + * private: + * int variable; + * + * public: + * maybe_const_t< int &, constness > // This is sort of like if one could write `const( constness ) int &` + * view() { return variable; } + * + * const int & + * view() const { return variable; } + * }; + * + * using MutableIntWrapper= IntWrapper< Mutable >; + * using ConstIntWrapper= IntWrapper< Const >; + * ``` + * + * Now `Constness< Mutable >` has a member `view` which returns `int &` and `Constness< Const >` has a member + * `view` which returns `const int &`. This facility can be useful in implementing pairs of `const`/non-`const` + * iterators, and other gadgets. + */ + enum Constness : bool + { + Const= true, + Mutable= false, + }; + + + /*! + * Apply the `Constness` requested to the specified type. + * + * If the type isn't `const`, but `Constness` is set, then + * it evaluates to `const T`. + */ + template< typename Type, Constness > struct maybe_const; + + template< typename Type > + struct maybe_const< Type, Const > + { + using type= std::add_const_t< Type >; + }; + + template< typename Type > + struct maybe_const< Type, Mutable > + { + using type= Type; + }; + + template< typename Type > + struct maybe_const< Type &, Const > + { + using type= std::add_const_t< Type > &; + }; + + template< typename Type > + struct maybe_const< Type &, Mutable > + { + using type= Type &; + }; + + /*! + * Conditionally make `Type` `const`. + */ + template< typename Type, Constness constness > + using maybe_const_t= typename maybe_const< Type, constness >::type; + + /*! + * Conditionally make `Type` into `const Type *`. + */ + template< typename Type, Constness constness > + using maybe_const_ptr_t= std::add_pointer_t< maybe_const_t< Type, constness > >; + + /*! + * Conditionally call `std::as_const`. + * + * Sometimes `std::as_const` is appropriate to call, and sometimes it isn't. + * In some cases, one might want to have type deduction driven by an expression + * where, in a `const` "branch" `std::as_const` is called, but in a non-`const` + * branch, it isn't. This facilitates that: + * + * ``` + * template< Constness constness > + * struct IntWrapper + * { + * private: + * int variable; + * + * public: + * // `std::as_const` will be called when `constness` is true, + * // and will be skipped otherwise. This permits the right + * // client overload to be called + * template< typename Function > + * decltype( auto ) + * apply( Function func ) + * { + * return func( maybe_as_const< constness >( variable ) ); + * } + * }; + * ``` + */ + template< Constness constness, typename T > + constexpr auto & + maybe_as_const( T &t ) noexcept + { + if constexpr( constness ) return std::as_const( t ); + else return t; + } + + template< Constness constness, typename T > + constexpr decltype( auto ) + maybe_as_const( const T &t ) noexcept + { + if constexpr( constness ) return std::as_const( t ); + else static_assert( dependent_value< false, T > ); + } + + template< Constness constness, typename T > + void maybe_as_const( T && )= delete; + } +} + +namespace Alepha::Hydrogen::inline exports::inline Constness_m +{ + using namespace detail::Constness_m::exports; +} diff --git a/Constness.test/0.cc b/Constness.test/0.cc new file mode 100644 index 0000000..013b3f8 --- /dev/null +++ b/Constness.test/0.cc @@ -0,0 +1,3 @@ +static_assert( __cplusplus > 2020'99 ); + +#include "../Constness.h" diff --git a/Constness.test/CMakeLists.txt b/Constness.test/CMakeLists.txt new file mode 100644 index 0000000..b099603 --- /dev/null +++ b/Constness.test/CMakeLists.txt @@ -0,0 +1 @@ +unit_test( 0 ) From 96d09e3df23d3016c88c550dfbbbe3935aa0697d Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 10 Nov 2023 23:26:44 -0500 Subject: [PATCH 34/38] Missing ctor. --- Exception.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Exception.h b/Exception.h index 8692a2a..10f3f79 100644 --- a/Exception.h +++ b/Exception.h @@ -277,6 +277,8 @@ namespace Alepha::Hydrogen ::detail:: exceptions ~Violation() override { if( not active ) abort(); } + Violation()= default; + Violation( const Violation © )= delete; Violation( Violation © ) : active( copy.active ) { copy.active= false; } }; From 59a969da84bbc80e2dd54a81e5532bd9f4cb968c Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 10 Nov 2023 23:27:06 -0500 Subject: [PATCH 35/38] Swappable facility. --- swappable.h | 173 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 swappable.h diff --git a/swappable.h b/swappable.h new file mode 100644 index 0000000..36152aa --- /dev/null +++ b/swappable.h @@ -0,0 +1,173 @@ +static_assert( __cplusplus > 2020'00 ); + +#pragma once + +#include + +#include + +#include "Capabilities.h" +#include "Concepts.h" + +namespace Alepha::Hydrogen ::detail:: swappable_m +{ + inline namespace exports + { + /*! + * Swappable capability hook. + * + * While it is possible to build `swap` in terms of `std::move` operations, it is often the case + * that `std::move` operations are built in terms of `std::swap`. This isn't a major problem, + * but when writing classes which manage a lot of state, and one has to write the lifecycle + * methods, it can become cumbersome to maintain the list of members that track through lifecycle + * transformations. + * + * Instead, similar to how `Alepha::comparable` works, `Alepha::swappable` is a hook-`Capability` + * which permits the user to write `swap_lens` as a member and the swap operation will be + * built in terms of that lens. This permits the list of members that need to be swapped to be + * written exactly once, in one place, with no repetition of members. + * + * Because `std::tie` objects are not swappable, the `Alepha::swap_magma` binding has been + * provided. + * + * Example: + * ``` + * class Employee : Alepha::swappable + * { + * private: + * std::string firstName; + * std::string lastName; + * + * public: + * // With this member, `Employee` now has a fully built `swap` operation, which will + * // swap all of the listed members. + * auto swap_lens() noexcept { return Alepha::swap_magma( firstName, lastName ); } + * + * // The lifecycle methods of this class could now be implemented in terms of + * // `swap`, instead of manually listing large numbers of members to swap, or + * // calling a swap member which would have to be written. + * }; + *``` + * The primary benefit of using `swap_lens` is that the list of members to swap is not doubled-up, + * and the function signature is simpler. Otherwise this would be the normal approach: + * ``` + * void + * swap( Employee &that ) noexcept + * { + * swap( this->firstName, that.firstName ); + * swap( this->lastName, that.lastName ); + * } + * ``` + * + * The above approach is prone to error, especially when multiple members are of the same type. + * `swap( this->firstName, that.lastName )` is a common mistake, for example. Instead, the + * `swap_lens` permits the members to be listed only once, in a single canonical place. + * + * @note Due to a quirk of template metaprogramming and SFINAE with ADL, `swap_lens` must appear + * before any ADL usage of `swap` in the class which defines it. If `operator =( Self && )` will + * be defined in terms of this swap, `swap_lens` must be defined before `operator =( Self && )`. + */ + + struct swappable {}; + } + + template< typename T > + concept HasMemberSwapLens= + requires( T t ) + { + { std::move( t ).swap_lens() }; + }; + + template< typename T > + concept MemberSwapLensable= Capability< T, swappable > or HasMemberSwapLens< T >; + + template< MemberSwapLensable T > + constexpr decltype( auto ) + swap_lens( T &&item ) noexcept( noexcept( std::forward< T >( item ).swap_lens() ) ) + { + return std::forward< T >( item ).swap_lens(); + } + + template< typename T > + concept SupportsSwapLens= + requires( T t ) + { + { swap_lens( std::move( t ) ) }; + }; + + template< typename T > + concept SwapLensable= Capability< T, swappable > and SupportsSwapLens< T >; + + + // To compute the noexceptness of a swap expression, we + // have to render it as it would be called via ADL. + // + // Thus we make a namespace to guard all this mess + // and then expose the noexceptness of that expression + // as the noexceptness of a different function we can + // name. + namespace check_noexcept + { + using std::swap; + + template< typename T > + constexpr void swap_check( T &&a, T &&b ) noexcept( noexcept( swap( std::forward< T >( a ), std::forward< T >( b ) ) ) ); + } + + template< typename T > + // requires( SwapLensable< T > ) + constexpr void + swap( T &&a, T &&b ) noexcept //noexcept( noexcept( check_noexcept::swap_check( swap_lens( std::forward< T >( a ) ), swap_lens( std::forward< T >( b ) ) ) ) ) + { + using std::swap; + return swap( swap_lens( std::forward< T >( a ) ), swap_lens( std::forward< T >( b ) ) ); + } + + // The swap binding and magma system allows one to specify a group of members (objects) which are suitable for the swap operation. + template< typename ... Args > + struct binding + { + std::tuple< Args... > data; + }; + + // Bindings have a complex swap implementation that recursively calls swap on those elements. + // This is necessary, since `std::tie` built tuples don't have a functioning swap operation. + template< std::size_t depth, typename ... Args > + constexpr void + swap_impl( std::tuple< Args... > &a, std::tuple< Args... > &b ) + noexcept + ( + ( ... & noexcept( check_noexcept::swap_check( std::declval< Args & >(), std::declval< Args & >() ) ) ) + ) + { + using std::swap; + if constexpr( sizeof...( Args ) == depth ) return; + else + { + swap( std::get< depth >( a ), std::get< depth >( b ) ); + return swap_impl< depth + 1 >( a, b ); + } + } + + template< typename ... Args > + constexpr void + swap( binding< Args... > a, binding< Args... > b ) noexcept( noexcept( swap_impl< 0 >( a.data, b.data ) ) ) + { + return swap_impl< 0 >( a.data, b.data ); + } + + namespace exports + { + template< typename ... Args > + constexpr auto + swap_magma( Args && ... args ) noexcept + { + return binding< Args... >{ std::tie( std::forward< Args >( args )... ) }; + } + } +} + +namespace Alepha::Hydrogen::inline exports::inline swappable_m +{ + using namespace detail::swappable_m::exports; +} From 3458c6af9ac0455616ed67c6768da530b96d0470 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 10 Nov 2023 23:27:29 -0500 Subject: [PATCH 36/38] A special case for capabilities. I keep putting of a rewrite here... I need to get to it. --- Capabilities.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Capabilities.h b/Capabilities.h index 92ffcb3..de7a75c 100644 --- a/Capabilities.h +++ b/Capabilities.h @@ -101,6 +101,13 @@ namespace Alepha::Hydrogen return has_cap( Meta::Container::vector< TParams... >{}, cap ); } + template< typename Type, typename Cap > + consteval bool + has_cap( const Meta::type_value< Type > &, Meta::type_value< Cap > ) + { + return false; + } + namespace exports { template< typename T, typename cap > From c78237e8442945dd0515d3bf544e36f72b524610 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 10 Nov 2023 23:29:20 -0500 Subject: [PATCH 37/38] Get Blob and Buffer building and a bit tested --- Blob.h | 82 ++++++++++++++++++++++------------------ Blob.test/0.cc | 25 ++++++++++++ Blob.test/CMakeLists.txt | 1 + Buffer.h | 82 +++++++++++++++++++--------------------- CMakeLists.txt | 1 + 5 files changed, 111 insertions(+), 80 deletions(-) create mode 100644 Blob.test/0.cc create mode 100644 Blob.test/CMakeLists.txt diff --git a/Blob.h b/Blob.h index 987bba1..876cfac 100644 --- a/Blob.h +++ b/Blob.h @@ -2,17 +2,22 @@ static_assert( __cplusplus > 2020'99 ); #pragma once +#include + +#include + #include #include "Buffer.h" #include "swappable.h" -#include "stringify.h" -#include "Exceptions.h" -#include "evaluation_helpers.h" -#include "threading.h" +#include "Exception.h" +//#include "threading.h" #include "error.h" -namespace Alepha::inline Cavorite ::detail:: blob +#include +#include + +namespace Alepha::Hydrogen ::detail:: Blob_m { inline namespace exports { @@ -34,28 +39,29 @@ namespace Alepha::inline Cavorite ::detail:: blob } using std::begin, std::end; + using IOStreams::stringify; class exports::DataCarveTooLargeError - : public virtual OutOfRangeError + : public virtual Buffer_m::OutOfRangeError { public: explicit DataCarveTooLargeError( const void *const location, const std::size_t request, const std::size_t available ) : std::out_of_range( "Tried to carve " + stringify( request ) + " bytes from `Blob` object at location " - + stringify( location ) + " which only has " + stringify( avail ) + " bytes allocated." ), + + stringify( location ) + " which only has " + stringify( available ) + " bytes allocated." ), OutOfRangeError( location, request, available ) {} }; class exports::DataCarveOutOfRangeError - : public virtual OutOfRangeError + : public virtual Buffer_m::OutOfRangeError { public: explicit - DataCarveOutOfRanceError( const void *const location, const std::size_t request, const std::size_t available ) + DataCarveOutOfRangeError( const void *const location, const std::size_t request, const std::size_t available ) : std::out_of_range( "Tried to carve " + stringify( request ) + " bytes from `Blob` object at location " - + stringify( location ) + " which only has " + stringify( avail ) + " bytes allocated." ), + + stringify( location ) + " which only has " + stringify( available ) + " bytes allocated." ), OutOfRangeError( location, request, available ) {} }; @@ -66,25 +72,27 @@ namespace Alepha::inline Cavorite ::detail:: blob private: using IndirectStorage= std::shared_ptr< std::shared_ptr< Blob > >; IndirectStorage storage; // If this is empty, then this `Blob` object doesn't share ownership. This references the shared pool. - Buffer buffer; + Buffer< Mutable > buffer; std::size_t viewLimit= 0; // TODO: Consider allowing for unrooted sub-buffer views? // TODO: Take the `storage` parameter and make it not increment when this ctor is called -- only when the dice roll passes. explicit - Blob( IndirectStorage storage, Buffer buffer ) noexcept - : storage( evaluate <=[storage= std::move( storage )] () -> IndirectBacking + Blob( IndirectStorage storage, Buffer< Mutable > buffer ) noexcept + : storage( Utility::evaluate <=[storage= std::move( storage )] () -> IndirectStorage { - if( fastRandomBits( C::storageSplitRandomBitDepth ) ) return std::move( storage ); - if( C::debugSplitSharing ) error() << "Observed a use count of " << storage.use_count() << " when we failed the dice roll." << std::endl; - auto split= std::make_shared< std::shared_ptr< Blob > >( *storage ); - if( C:: + //if( fastRandomBits( C::storageSplitRandomBitDepth ) ) + return std::move( storage ); + //if( C::debugSplitSharing ) error() << "Observed a use count of " << storage.use_count() << " when we failed the dice roll." << std::endl; + //auto split= std::make_shared< std::shared_ptr< Blob > >( *storage ); + //if( C:: + //return split; }), - buffer( buffer ) + buffer( buffer ), viewLimit( buffer.size() ) {} public: - ~Buffer() { reset(); } + ~Blob() { reset(); } auto swap_lens() noexcept @@ -106,7 +114,7 @@ namespace Alepha::inline Cavorite ::detail:: blob void reset() noexcept { - if( not storage ) delete buffer; + if( not storage ) delete [] buffer.byte_data(); else storage.reset(); buffer= {}; @@ -125,7 +133,7 @@ namespace Alepha::inline Cavorite ::detail:: blob * @note: No data are copied. */ void - reset( const std::size_t ) + reset( const std::size_t size ) { Blob tmp{ size }; swap( tmp, *this ); @@ -133,8 +141,8 @@ namespace Alepha::inline Cavorite ::detail:: blob // Copy deep copies the data. Blob( const Blob © ) - : buffer( new std::byte[ copy.buffer.size() ] ), - viewLimit( copy.viewLimit ) + : buffer( new std::byte[ copy.size() ], copy.size() ), + viewLimit( copy.size() ) { if( C::debugCtors ) error() << "Blob copy invoked." << std::endl; copyData( *this, copy ); @@ -178,13 +186,13 @@ namespace Alepha::inline Cavorite ::detail:: blob explicit Blob( const std::size_t amount ) - : buffer( new std::byte[ amount ]{} ), // The data must be 0'ed upon allocation. + : buffer( new std::byte[ amount ]{}, amount ), // The data must be 0'ed upon allocation. viewLimit( amount ) {} explicit Blob( const Buffer< Const > b ) - : Buffer( b.size() ) + : Blob( b.size() ) { copyData( buffer, b ); } @@ -228,7 +236,7 @@ namespace Alepha::inline Cavorite ::detail:: blob // Now we assume that there's a two-layer scheme, so we operate based upon that. - Blob rv{ storage, Buffer< Mutable >{ buffer, amount } } + Blob rv{ storage, Buffer< Mutable >{ buffer, amount } }; buffer= buffer + amount; viewLimit-= amount; @@ -260,7 +268,7 @@ namespace Alepha::inline Cavorite ::detail:: blob template< typename T > void operator []( T ) const= delete; template< typename T > void operator []( T )= delete; - constexpr std::size_t capacity() const noecept { return buffer.size(); } + constexpr std::size_t capacity() const noexcept { return buffer.size(); } bool isContiguousWith( const Blob &other ) const & noexcept @@ -269,7 +277,7 @@ namespace Alepha::inline Cavorite ::detail:: blob ( storage != nullptr and - *storage == *other.backing + *storage == *other.storage and byte_data() + size() == other.byte_data() ); @@ -331,7 +339,7 @@ namespace Alepha::inline Cavorite ::detail:: blob bool couldConcatenate( const Buffer< Const > buffer ) const noexcept { - return data.size() <= ( capacity() - size() ); + return buffer.size() <= ( capacity() - size() ); } /*! @@ -372,9 +380,9 @@ namespace Alepha::inline Cavorite ::detail:: blob [[nodiscard]] Buffer< constness > concatenate( const Buffer< constness > data ) noexcept { - const auto amount= std::min( capacity - size(), data.size() ); - const DataWindow< const > fitted{ data, amount }; - copyData( buffer, + size(), fitted ); + const auto amount= std::min( capacity() - size(), data.size() ); + const Buffer< Const > fitted{ data, amount }; + copyData( buffer + size(), fitted ); setSize( size() + amount ); return data + amount; } @@ -406,7 +414,7 @@ namespace Alepha::inline Cavorite ::detail:: blob } else { - const auto amount= concatenate( Buffer< Const >{ blob } ).size() + const auto amount= concatenate( Buffer< Const >{ blob } ).size(); const auto rv= blob.carveTail( amount ); blob.reset(); return rv; @@ -482,11 +490,11 @@ namespace Alepha::inline Cavorite ::detail:: blob } }; - static_assert( HasCapability< Blob, swappable > ); - static_assert( detail::swaps::SwapLensable< Blob > ); + //static_assert( Capability< Blob, swappable > ); + //static_assert( detail::swaps::SwapLensable< Blob > ); } -namespace Alepha::Cavorite::inline exports::inline blob +namespace Alepha::Hydrogen::inline exports::inline Blob_m { - using namespace detail::blob::exports; + using namespace detail::Blob_m::exports; } diff --git a/Blob.test/0.cc b/Blob.test/0.cc new file mode 100644 index 0000000..afa887d --- /dev/null +++ b/Blob.test/0.cc @@ -0,0 +1,25 @@ +static_assert( __cplusplus > 2020'99 ); + +#include "../Blob.h" + +#include + +#include + +static auto init= Alepha::Utility::enroll <=[] +{ + using namespace Alepha::Testing::literals::test_literals; + "Simple carve head test"_test <=[] + { + Alepha::Blob b{ 1024 }; + + auto b2= b.carveHead( 256 ); + + assert( b.size() == 768 ); + assert( b2.size() == 256 ); + + std::string h= "Hello world"; + + copyData( b2, Alepha::make_buffer( h ) ); + }; +}; diff --git a/Blob.test/CMakeLists.txt b/Blob.test/CMakeLists.txt new file mode 100644 index 0000000..b099603 --- /dev/null +++ b/Blob.test/CMakeLists.txt @@ -0,0 +1 @@ +unit_test( 0 ) diff --git a/Buffer.h b/Buffer.h index aae3bf6..c3d450d 100644 --- a/Buffer.h +++ b/Buffer.h @@ -2,6 +2,11 @@ static_assert( __cplusplus > 2020'99 ); #pragma once +#include + +#include +#include + #include #include #include @@ -10,19 +15,22 @@ static_assert( __cplusplus > 2020'99 ); #include #include -#include +#include +#include + +#include #include "Concepts.h" #include "assertion.h" #include "Capabilities.h" -#include "lifetime.h" -namespace Alepha::inline Cavorite ::detail:: buffer +namespace Alepha::Hydrogen ::detail:: Buffer_m { inline namespace exports {} using namespace std::literals::string_literals; + using IOStreams::stringify; namespace exports { @@ -42,7 +50,7 @@ namespace Alepha::inline Cavorite ::detail:: buffer : baseAddress( address ), requestedSize( requestedSize ), availableSize( availableSize ) {} - public + public: const void *getAddress() const noexcept { return baseAddress; } const std::size_t getRequestedSize() const noexcept { return requestedSize; } const std::size_t getAvailableSize() const noexcept { return availableSize; } @@ -62,20 +70,20 @@ namespace Alepha::inline Cavorite ::detail:: buffer : std::out_of_range( "Tried to access an object of type "s + type.name() + " which is " + stringify( requestedSize ) + " bytes in size. " + "The request was at location " + stringify( location ) + " which only has " + stringify( availableSize ) + " bytes allocated" ), - OutOfRangeError( location, requestedSIze, availableSize ), + OutOfRangeError( location, requestedSize, availableSize ), typeID( type ) {} }; class OutOfRangeSizeError - : virtual public OutOfRangeException + : virtual public OutOfRangeError { public: explicit - OutOfRangeSizeException( const void *const location, const std::ptrdiff_t requestedOffset, const std::size_t availableSpace ) + OutOfRangeSizeError( const void *const location, const std::ptrdiff_t requestedOffset, const std::size_t availableSpace ) : std::out_of_range( "Tried to view a byte offset of " + stringify( requestedOffset ) + " into location " + stringify( location ) + " which is " + stringify( availableSpace ) + " bytes in size." ), - OutOfRangeException( location, requestedOffset, availableSpace ) + OutOfRangeError( location, requestedOffset, availableSpace ) {} }; @@ -106,26 +114,31 @@ namespace Alepha::inline Cavorite ::detail:: buffer constexpr Buffer( const pointer_type ptr, const std::size_t bytes ) noexcept - : ptr( static_cast< byte_pointer_type >( ptr ), bytes( bytes ) + : ptr( static_cast< byte_pointer_type >( ptr ) ), bytes( bytes ) {} constexpr Buffer( const Buffer< Mutable > © ) noexcept : ptr( copy.byte_data() ), bytes( copy.size() ) {} + template< Constness constness_= constness > + requires( constness_ == Mutable ) constexpr - Buffer( const Buffer< Const > © ) noexcept requires( Constness == Mutable )= delete; + Buffer( const Buffer< Const > © ) noexcept = delete; constexpr byte_pointer_type byte_data() const noexcept { return ptr; } constexpr pointer_type data() const noexcept { return ptr; } - constexpr const_byte_pointer_type byte_data() const noexcept { return ptr; } - constexpr const_pointer_type data() const noexcept { return ptr; } + constexpr const_byte_pointer_type const_byte_data() const noexcept { return ptr; } + constexpr const_pointer_type const_data() const noexcept { return ptr; } constexpr std::size_t size() const noexcept { return bytes; } constexpr bool empty() const noexcept { return size() == 0; } + constexpr byte_pointer_type begin() const noexcept { return byte_data(); } + constexpr byte_pointer_type end() const noexcept { return begin() + size(); } + constexpr const_byte_pointer_type cbegin() const noexcept { return begin(); } constexpr const_byte_pointer_type cend() const noexcept { return end(); } @@ -137,7 +150,7 @@ namespace Alepha::inline Cavorite ::detail:: buffer as( std::nothrow_t ) const noexcept { assertion( sizeof( T ) <= bytes ); - return *start_lifetime_as< std::add_lvalue_reference_t< maybe_const_t< T, constness > > >( ptr ); + return *Alepha::start_lifetime_as< std::add_lvalue_reference_t< maybe_const_t< T, constness > > >( ptr ); } template< typename T > @@ -153,7 +166,7 @@ namespace Alepha::inline Cavorite ::detail:: buffer const_as( std::nothrow_t ) const noexcept { assertion( sizeof( T ) <= bytes ); - return *start_lifetime_as< std::add_const_t< T > >( ptr ); + return *Alepha::start_lifetime_as< std::add_const_t< T > >( ptr ); } template< typename T > @@ -222,7 +235,7 @@ namespace Alepha::inline Cavorite ::detail:: buffer struct BufferModel_capability {}; template< typename T > - concept UndecayedBufferModelable= HasCapability< T, BufferModel_capability >; + concept UndecayedBufferModelable= Capability< T, BufferModel_capability >; template< typename T > concept BufferModelable= UndecayedBufferModelable< std::decay_t< T > >; @@ -234,8 +247,8 @@ namespace Alepha::inline Cavorite ::detail:: buffer constexpr auto &crtp() noexcept { return static_cast< Derived & >( *this ); } constexpr const auto &crtp() const noexcept { return static_cast< const Derived & >( *this ); } - constexpr auto &buffer() { return static_cast< Buffer< Mutable > >( crtp() ); } - constexpr const auto &buffer() const { return static_cast< Buffer< Const > >( crtp() ); } + constexpr auto buffer() { return static_cast< Buffer< Mutable > >( crtp() ); } + constexpr auto buffer() const { return static_cast< Buffer< Const > >( crtp() ); } public: constexpr auto byte_data() { return buffer().byte_data(); } @@ -273,8 +286,8 @@ namespace Alepha::inline Cavorite ::detail:: buffer template< typename T > extern Constness constness_of_v; - template< UndecayedBufferModelble T > - constexpr Constness constness_of_v= std::is_const_v< T >; + template< UndecayedBufferModelable T > + constexpr Constness constness_of_v< T >{ std::is_const_v< T > }; template< Constness constness > constexpr Constness constness_of_v< Buffer< constness > >{ constness }; @@ -283,7 +296,7 @@ namespace Alepha::inline Cavorite ::detail:: buffer constexpr auto operator + ( const Buffer< constness > buffer, const std::size_t offset ) { - if( offset > buffer.size() ) throw OutOfRangeError{ buffer.data(), offset, buffer.size() }; + if( offset > buffer.size() ) throw OutOfRangeSizeError{ buffer.data(), std::ptrdiff_t( offset ), buffer.size() }; return Buffer< constness >{ buffer.byte_data() + offset, buffer.size() - offset }; } @@ -372,23 +385,6 @@ namespace Alepha::inline Cavorite ::detail:: buffer } - template< Concepts::StandardLayout T, std::size_t size > - constexpr Buffer< Mutable > - make_buffer( T array[ size ] ) noexcept - { - // TODO: Do we need to consider overflow here? - return { array, sizeof( array ) }; - } - - template< Concepts::StandardLayout T, std::size_t size > - constexpr Buffer< Const > - make_buffer( const T array[ size ] ) noexcept - { - // TODO: Do we need to consider overflow here? - return { array, sizeof( array ) }; - } - - inline Buffer< Mutable > make_buffer( std::string &string ) noexcept { @@ -407,7 +403,7 @@ namespace Alepha::inline Cavorite ::detail:: buffer { if( source.size() > destination.size() ) throw InsufficientSizeError{ destination.data(), source.size(), destination.size(), typeid( std::byte ) }; - ::memcpy( destination, source, source.size() ); + std::memcpy( destination, source, source.size() ); return { destination, source.size() }; } @@ -419,13 +415,13 @@ namespace Alepha::inline Cavorite ::detail:: buffer namespace exports { - using detail::buffer::make_buffer; + using detail::Buffer_m::make_buffer; } } -namespace Alepha::Cavorite::inline exports::inline buffer +namespace Alepha::Hydrogen::inline exports::inline Buffer_m { - using namespace detail::buffer::exports; + using namespace detail::Buffer_m::exports; } /* @@ -453,8 +449,8 @@ namespace Alepha::Cavorite::inline exports::inline buffer template<> constexpr auto -std::cbegin( const ::Alepha::Cavorite::Buffer< Alepha::Cavorite::Mutable > &range ) -> decltype( range.begin() )= delete; +std::cbegin( const ::Alepha::Hydrogen::Buffer< Alepha::Hydrogen::Mutable > &range ) -> decltype( range.begin() )= delete; template<> constexpr auto -std::cend( const ::Alepha::Cavorite::Buffer< Alepha::Cavorite::Mutable > &range ) -> decltype( range.end() )= delete; +std::cend( const ::Alepha::Hydrogen::Buffer< Alepha::Hydrogen::Mutable > &range ) -> decltype( range.end() )= delete; diff --git a/CMakeLists.txt b/CMakeLists.txt index 4b46b65..e873c3c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,7 @@ add_subdirectory( tuplize_args.test ) add_subdirectory( Thread.test ) add_subdirectory( assertion.test ) add_subdirectory( Constness.test ) +add_subdirectory( Blob.test ) # Sample applications add_executable( example example.cc ) From 868723ea573185943b4957de3cd578ff7fb0f6ef Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 10 Nov 2023 23:56:28 -0500 Subject: [PATCH 38/38] Pthread might also be needed. --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index afd6717..268f80e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,7 @@ project( Alepha ) include( cmake/rules.cmake ) +link_libraries( pthread ) # The core alepha library: