1
0
forked from Alepha/Alepha

Merge branch 'master' of coruscant:/Sources/Repositories/Alepha

This commit is contained in:
2023-11-11 15:01:26 -05:00
132 changed files with 2337 additions and 1050 deletions

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

193
Atomic/Mailbox.h Normal file
View File

@ -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. <BR>
* @author ADAM David Alan Martin
*/
#ifndef ALEPHA_MAILBOX_HEADER
#define ALEPHA_MAILBOX_HEADER
#include <vector>
#include <boost/thread.hpp>
#include <Alepha/ScopedUsage.h>
#include <Alepha/Exceptions.h>
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
*/

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#include <Alepha/AutoRAII.h>

84
Blob.h
View File

@ -1,18 +1,23 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once
#include <Alepha/Alepha.h>
#include <cassert>
#include <memory>
#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 <Alepha/IOStreams/String.h>
#include <Alepha/Utility/evaluation_helpers.h>
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 &copy )
: 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;
}

25
Blob.test/0.cc Normal file
View File

@ -0,0 +1,25 @@
static_assert( __cplusplus > 2020'99 );
#include "../Blob.h"
#include <Alepha/Testing/test.h>
#include <Alepha/Utility/evaluation_helpers.h>
static auto init= Alepha::Utility::enroll <=[]
{
using namespace Alepha::Testing::literals::test_literals;
"Simple carve head test"_test <=[]
{
Alepha::Blob b{ 1024 };
auto b2= b.carveHead( 256 );
assert( b.size() == 768 );
assert( b2.size() == 256 );
std::string h= "Hello world";
copyData( b2, Alepha::make_buffer( h ) );
};
};

View File

@ -1,7 +1,12 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once
#include <Alepha.h>
#include <cstddef>
#include <cstring>
#include <vector>
#include <string>
#include <array>
@ -10,19 +15,22 @@ static_assert( __cplusplus > 2020'00 );
#include <exception>
#include <stdexcept>
#include <boost/lexical_cast.hpp>
#include <Alepha/Constness.h>
#include <Alepha/lifetime.h>
#include <Alepha/IOStreams/String.h>
#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 > &copy ) noexcept
: ptr( copy.byte_data() ), bytes( copy.size() ) {}
template< Constness constness_= constness >
requires( constness_ == Mutable )
constexpr
Buffer( const Buffer< Const > &copy ) noexcept requires( Constness == Mutable )= delete;
Buffer( const Buffer< Const > &copy ) 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;

View File

@ -3,6 +3,7 @@ project( Alepha )
include( cmake/rules.cmake )
link_libraries( pthread )
# The core alepha library:
@ -31,6 +32,10 @@ add_subdirectory( Exception.test )
add_subdirectory( word_wrap.test )
add_subdirectory( string_algorithms.test )
add_subdirectory( tuplize_args.test )
add_subdirectory( Thread.test )
add_subdirectory( assertion.test )
add_subdirectory( Constness.test )
add_subdirectory( Blob.test )
# Sample applications
add_executable( example example.cc )

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once
@ -78,6 +78,7 @@ namespace Alepha::Hydrogen
has_cap_in_capability_base( const Left &, Meta::type_value< Cap > cap )
{
throw "Unevaluated";
return false;
}
template< typename Cap, typename ... TParams >
@ -100,6 +101,13 @@ namespace Alepha::Hydrogen
return has_cap( Meta::Container::vector< TParams... >{}, cap );
}
template< typename Type, typename Cap >
consteval bool
has_cap( const Meta::type_value< Type > &, Meta::type_value< Cap > )
{
return false;
}
namespace exports
{
template< typename T, typename cap >

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once
@ -11,7 +11,7 @@ static_assert( __cplusplus > 2020'00 );
#include "meta.h"
#include "function_traits.h"
namespace Alepha::Hydrogen ::detail:: core_concepts
namespace Alepha::Hydrogen ::detail:: Concepts_m
{
inline namespace exports
{
@ -282,7 +282,7 @@ namespace Alepha::Hydrogen ::detail:: core_concepts
}
}
namespace Alepha::Hydrogen::inline exports::inline core_concepts
namespace Alepha::Hydrogen::inline exports::inline Concepts_m::inline Concepts
{
using namespace detail::core_concepts::exports;
using namespace detail::Concepts_m::exports;
}

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#include "Console.h"
@ -13,9 +13,9 @@ static_assert( __cplusplus > 2020'00 );
#include <sstream>
#include <iostream>
#include <ext/stdio_filebuf.h>
#include <Alepha/Utility/evaluation_helpers.h>
#include <Alepha/IOStreams/OutUnixFileBuf.h>
#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 )
{}
};

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,7 +1,9 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once
#include <Alepha/Alepha.h>
#include <cstddef>
#include <array>
@ -9,9 +11,7 @@ static_assert( __cplusplus > 2020'00 );
#include <algorithm>
#include <iostream>
#include <Alepha/Alepha.h>
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;
}

152
Constness.h Normal file
View File

@ -0,0 +1,152 @@
static_assert( __cplusplus > 2020'00 );
#pragma once
#include <Alepha/Alepha.h>
#include <type_traits>
#include <utility>
#include "meta.h"
namespace Alepha::Hydrogen ::detail:: Constness_m
{
inline namespace exports
{
/*!
* Conditional constness primitive.
*
* C++17 and beyond have many conditional attributes on functions. Conditional `noexcept`,
* conditional `explicit`, and even conditional instantiation. The `noexcept` and `explicit`
* are conditional on a boolean condition. This enum permits making templates which
* are conditionally const, depending upon instantiation.
*
* Simple example:
*
* ```
* template< Constness constness >
* struct IntWrapper
* {
* private:
* int variable;
*
* public:
* maybe_const_t< int &, constness > // This is sort of like if one could write `const( constness ) int &`
* view() { return variable; }
*
* const int &
* view() const { return variable; }
* };
*
* using MutableIntWrapper= IntWrapper< Mutable >;
* using ConstIntWrapper= IntWrapper< Const >;
* ```
*
* Now `Constness< Mutable >` has a member `view` which returns `int &` and `Constness< Const >` has a member
* `view` which returns `const int &`. This facility can be useful in implementing pairs of `const`/non-`const`
* iterators, and other gadgets.
*/
enum Constness : bool
{
Const= true,
Mutable= false,
};
/*!
* Apply the `Constness` requested to the specified type.
*
* If the type isn't `const`, but `Constness` is set, then
* it evaluates to `const T`.
*/
template< typename Type, Constness > struct maybe_const;
template< typename Type >
struct maybe_const< Type, Const >
{
using type= std::add_const_t< Type >;
};
template< typename Type >
struct maybe_const< Type, Mutable >
{
using type= Type;
};
template< typename Type >
struct maybe_const< Type &, Const >
{
using type= std::add_const_t< Type > &;
};
template< typename Type >
struct maybe_const< Type &, Mutable >
{
using type= Type &;
};
/*!
* Conditionally make `Type` `const`.
*/
template< typename Type, Constness constness >
using maybe_const_t= typename maybe_const< Type, constness >::type;
/*!
* Conditionally make `Type` into `const Type *`.
*/
template< typename Type, Constness constness >
using maybe_const_ptr_t= std::add_pointer_t< maybe_const_t< Type, constness > >;
/*!
* Conditionally call `std::as_const`.
*
* Sometimes `std::as_const` is appropriate to call, and sometimes it isn't.
* In some cases, one might want to have type deduction driven by an expression
* where, in a `const` "branch" `std::as_const` is called, but in a non-`const`
* branch, it isn't. This facilitates that:
*
* ```
* template< Constness constness >
* struct IntWrapper
* {
* private:
* int variable;
*
* public:
* // `std::as_const` will be called when `constness` is true,
* // and will be skipped otherwise. This permits the right
* // client overload to be called
* template< typename Function >
* decltype( auto )
* apply( Function func )
* {
* return func( maybe_as_const< constness >( variable ) );
* }
* };
* ```
*/
template< Constness constness, typename T >
constexpr auto &
maybe_as_const( T &t ) noexcept
{
if constexpr( constness ) return std::as_const( t );
else return t;
}
template< Constness constness, typename T >
constexpr decltype( auto )
maybe_as_const( const T &t ) noexcept
{
if constexpr( constness ) return std::as_const( t );
else static_assert( dependent_value< false, T > );
}
template< Constness constness, typename T >
void maybe_as_const( T && )= delete;
}
}
namespace Alepha::Hydrogen::inline exports::inline Constness_m
{
using namespace detail::Constness_m::exports;
}

3
Constness.test/0.cc Normal file
View File

@ -0,0 +1,3 @@
static_assert( __cplusplus > 2020'99 );
#include "../Constness.h"

View File

@ -0,0 +1 @@
unit_test( 0 )

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

14
Enum.h
View File

@ -1,7 +1,9 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once
#include <Alepha/Alepha.h>
#include <cassert>
#include <tuple>
@ -17,7 +19,7 @@ static_assert( __cplusplus > 2020'00 );
#include "ConstexprString.h"
#include "meta.h"
namespace Alepha::inline Cavorite ::detail:: enhanced_enum
namespace Alepha::Hydrogen ::detail:: Enum_m
{
inline namespace exports {}
@ -246,12 +248,12 @@ namespace Alepha::inline Cavorite ::detail:: enhanced_enum
}
}
namespace Alepha::Cavorite::inline exports::inline enhanced_enum
namespace Alepha::Hydrogen::inline exports::inline Enum_m
{
using namespace detail::enhanced_enum::exports;
using namespace detail::Enum_m::exports;
}
namespace Alepha::Cavorite::inline exports::inline literals::inline enum_literals
namespace Alepha::Hydrogen::inline exports::inline literals::inline enum_literals
{
using namespace detail::enhanced_enum::exports::literals;
using namespace Enum_m::literals;
}

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once
@ -277,6 +277,8 @@ namespace Alepha::Hydrogen ::detail:: Exception_m
~Violation() override { if( not active ) abort(); }
Violation()= default;
Violation( const Violation &copy )= delete;
Violation( Violation &copy ) : active( copy.active ) { copy.active= false; }
};

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#include <Alepha/Exception.h>

388
Format Normal file
View File

@ -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 <TYPE>
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.

View File

@ -1,3 +1,10 @@
add_subdirectory( IStreamable.test )
add_subdirectory( OStreamable.test )
add_subdirectory( streamable.test )
add_subdirectory( OutUnixFileBuf.test )
add_subdirectory( StackableStreambuf.test )
target_sources( alepha PRIVATE
StackableStreambuf.cc
)

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#include "../IStreamable.h"

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#include "../OStreamable.h"

View File

@ -0,0 +1,47 @@
static_assert( __cplusplus > 2020'99 );
#pragma once
#include <Alepha/Alepha.h>
#include <unistd.h>
#include <streambuf>
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;
}

View File

@ -0,0 +1,28 @@
static_assert( __cplusplus > 2020'99 );
#include "../OutUnixFileBuf.h"
#include <Alepha/Testing/test.h>
#include <Alepha/AutoRAII.h>
#include <unistd.h>
#include <fcntl.h>
#include <Alepha/Utility/evaluation_helpers.h>
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 };
};
};

View File

@ -0,0 +1 @@
unit_test( 0 )

View File

@ -1,17 +1,25 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#include "StackableStreambuf.h"
namespace Alepha::Hydrogen::Utility::detail::stackable_streambuf
#include <cassert>
namespace Alepha::Hydrogen::IOStreams::detail::stackable_streambuf
{
namespace
{
const auto index= std::ios::xalloc();
inline auto &
auto *&
getStackPtr( std::ios_base &ios )
{
return reinterpret_cast< std::stack< std::unique_ptr< StackableStreambuf > > *& >( ios.pword( index ) );
}
auto &
getStack( std::ios_base &ios )
{
auto &ownership= reinterpret_cast< std::stack< std::unique_ptr< StackableStreambuf > > *& >( ios.pword( index ) );
auto &ownership= getStackPtr( ios );
if( not ownership ) ownership= new std::decay_t< decltype( *ownership ) >{};
return *ownership;
@ -24,6 +32,7 @@ namespace Alepha::Hydrogen::Utility::detail::stackable_streambuf
{
auto &ownership= getStack( ios );
assert( not ownership.empty() );
// Since it's owned, delete happens at scope exit.
const std::unique_ptr top= std::move( ownership.top() );
ownership.pop();
@ -48,9 +57,8 @@ namespace Alepha::Hydrogen::Utility::detail::stackable_streambuf
inline void
releaseStack( std::ios_base &ios )
{
auto &os= dynamic_cast< std::ostream & >( ios );
while( releaseTop( os ) );
delete getStackPtr( ios );
getStackPtr( ios )= nullptr;
}
void

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once
@ -10,7 +10,7 @@ static_assert( __cplusplus > 2020'00 );
#include <memory>
#include <stack>
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;
}

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#include "../StackableStreambuf.h"

View File

@ -0,0 +1 @@
unit_test( 0 )

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#include "../streamable.h"

26
LICENSE
View File

@ -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/.

373
MPL-LICENSE.txt Normal file
View File

@ -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.

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once
@ -69,6 +69,7 @@ namespace Alepha::Hydrogen::Meta::Container
};
template< typename MetaFunction, typename Arg1, typename First, typename ... Members >
requires( sizeof...( Members ) != 0 )
constexpr decltype( auto )
invoke_call( MetaFunction func, type_value< Arg1 > arg1, dereferenced_iterator< vector< First, Members... > > deref )
{

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#include <Alepha/Meta/is_sequence.h>
#include <Alepha/Meta/is_streamable.h>

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once
@ -18,8 +18,8 @@ namespace Alepha::Hydrogen::Meta
template< typename T >
struct is_optional : std::false_type {};
template< typename T, typename ... Args >
struct is_optional< std::optional< T, Args... > > : std::true_type {};
template< typename T >
struct is_optional< std::optional< T > > : std::true_type {};
template< typename T >
constexpr bool is_optional_v= is_optional< T >::value;

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#include "ProgramOptions.h"
@ -10,7 +10,9 @@ static_assert( __cplusplus > 2020'00 );
#include <Alepha/StaticValue.h>
#include <Alepha/error.h>
namespace Alepha::Cavorite ::detail:: program_options
#include <Alepha/System/programName.h>
namespace Alepha::Hydrogen ::detail:: ProgramOptions_m
{
namespace
{
@ -205,9 +207,7 @@ namespace Alepha::Cavorite ::detail:: program_options
VariableMap substitutions=
{
// This uses a GNU extension, but it's fine. We can always make this
// portable, later.
{ "program-name"s, lambaste<=::program_invocation_short_name },
{ "program-name"s, lambaste<=System::programName() },
{ "option-name"s, lambaste<=name },
{ "default"s, [&defaultBuilder= defaultBuilder, &name= name]
{
@ -283,8 +283,7 @@ namespace Alepha::Cavorite ::detail:: program_options
{
VariableMap substitutions
{
// Another use of the GNUism.
{ "program-name"s, lambaste<=::program_invocation_short_name },
{ "program-name"s, lambaste<=System::programName() },
};
if( canonicalName.has_value() ) substitutions[ "canonical-name"s ]= lambaste<=canonicalName.value();

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once
@ -96,7 +96,7 @@ static_assert( __cplusplus > 2020'00 );
#include <Alepha/Utility/evaluation_helpers.h>
namespace Alepha::inline Cavorite ::detail:: program_options
namespace Alepha::Hydrogen ::detail:: ProgramOptions_m
{
inline namespace exports {}
@ -290,7 +290,7 @@ namespace Alepha::inline Cavorite ::detail:: program_options
// TODO: This should steal the impl from the vector form, above, and that should defer to this.
using parse_type= typename arg_type::value_type;
auto handler= [handler, name= name]( std::optional< std::string > argument )
auto wrapped= [handler, name= name]( std::optional< std::string > argument )
{
impl::checkArgument( argument, name );
@ -311,7 +311,7 @@ namespace Alepha::inline Cavorite ::detail:: program_options
};
handler( parsed );
};
return registerHandler( handler );
return registerHandler( wrapped );
}
else
{
@ -392,12 +392,12 @@ namespace Alepha::inline Cavorite ::detail:: program_options
}
}
namespace Alepha::Cavorite::inline exports::inline program_options
namespace Alepha::Hydrogen::inline exports::inline ProgramOptions_m
{
using namespace detail::program_options::exports;
using namespace detail::ProgramOptions_m::exports;
}
namespace Alepha::Cavorite::inline exports::inline literals::inline option_literals
namespace Alepha::Hydrogen::inline exports::inline literals::inline option_literals
{
using namespace detail::program_options::exports::literals;
using namespace ProgramOptions_m::literals;
}

View File

@ -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.

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once
@ -72,14 +72,6 @@ namespace Alepha::Hydrogen::Reflection
// any initialization method.
struct argument { template< typename T > constexpr operator T (); };
template< typename T >
struct checker
{
using type= typename checker< T >::type;
//using type= void;
//static_assert( std::is_empty_v< T > );
};
// Any empty-base-class argument.
template< typename Aggregate >
struct empty_base
@ -88,7 +80,6 @@ namespace Alepha::Hydrogen::Reflection
requires
(
true
//typename= typename checker< std::decay_t< T > >::type,
and EmptyType< std::decay_t< T > >
and not SameAs< std::decay_t< T >, Aggregate >
and DerivedFrom< Aggregate, std::decay_t< T > >

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#include <Alepha/Reflection/tuplizeAggregate.h>

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

223
Style Normal file
View File

@ -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::<Platform> 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.

46
System/programName.h Normal file
View File

@ -0,0 +1,46 @@
static_assert( __cplusplus > 2020'99 );
#pragma once
#include <Alepha/Alepha.h>
#if defined( __FreeBSD__ )
# include <sys/types.h>
# include <sys/sysctl.h>
#elif defined( _GNU_SOURCE )
#endif
#include <string>
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;
}

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once
@ -17,81 +17,32 @@ static_assert( __cplusplus > 2020'00 );
#include <boost/core/demangle.hpp>
#include <boost/lexical_cast.hpp>
#include <Alepha/Meta/is_vector.h>
#include <Alepha/Meta/is_optional.h>
#include <Alepha/Meta/is_streamable.h>
#include <Alepha/Meta/is_sequence.h>
#include <Alepha/Meta/is_product_type.h>
#include <Alepha/Meta/product_type_decay.h>
#include <Alepha/Meta/sequence_kind.h>
#include <Alepha/function_traits.h>
#include <Alepha/template_for_each.h>
#include <Alepha/IOStreams/String.h>
#include <Alepha/Utility/evaluation_helpers.h>
#include <Alepha/Utility/TupleAdapter.h>
#include <Alepha/Reflection/tuplizeAggregate.h>
#include <Alepha/TotalOrder.h>
#include <Alepha/Console.h>
#include "colors.h"
#include "printDebugging.h"
namespace Alepha::Hydrogen::Testing ::detail:: table_test
{
inline namespace exports
{
enum class OutputMode { All, Relaxed };
}
inline namespace exports {}
inline void breakpoint() {}
namespace C
namespace C:: inline Colors
{
inline namespace Colors
{
using namespace testing_colors::C::Colors;
}
using namespace testing_colors::C::Colors;
}
template< OutputMode outputMode, typename T >
void printDebugging( const T &witness, const T &expected );
template< Aggregate Agg, TypeListType >
struct TupleSneak;
template< Aggregate Agg >
struct TupleSneak< Agg, Nil >
: Agg
{
TupleSneak() { std::cerr << "The inherited default ctor was called." << std::endl; }
protected:
void set( Agg agg ) { static_cast< Agg & >( *this )= agg; }
};
template< Aggregate Agg, typename ... Args >
struct TupleSneak< Agg, TypeList< Args... > >
: TupleSneak< Agg, cdr_t< TypeList< Args... > > >
{
using Parent= TupleSneak< Agg, cdr_t< TypeList< Args... > > >;
using Parent::Parent;
TupleSneak( Args ... args )
{
std::cerr << "I was the ctor called, with " << sizeof...( Args ) << " arguments." << std::endl;
tuple_for_each( std::tuple{ args... } ) <=
[]( const auto element )
{
std::cerr << "Element: " << element << std::endl;
};
this->set( { args... } );
}
};
enum class TestResult { Passed, Failed };
struct BlankBase {};
@ -100,7 +51,7 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
static consteval auto
compute_base_f() noexcept
{
if constexpr ( Aggregate< T > ) return std::type_identity< TupleSneak< T, list_from_tuple_t< Reflection::aggregate_tuple_t< T > > > >{};
if constexpr ( Aggregate< T > ) return std::type_identity< Utility::TupleAdapter< T > >{};
else if constexpr( std::is_class_v< T > ) return std::type_identity< T >{};
else return std::type_identity< BlankBase >{};
}
@ -109,24 +60,17 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
using compute_base_t= typename decltype( compute_base_f< std::decay_t< T > >() )::type;
template< typename return_type, OutputMode outputMode >
struct BasicUniversalHandler;
template< Primitive return_type, OutputMode outputMode >
struct BasicUniversalHandler< return_type, outputMode >
struct BasicUniversalHandler
: compute_base_t< return_type >
{
using ComputedBase= compute_base_t< return_type >;
using ComputedBase::ComputedBase;
using Invoker= std::function< return_type () >;
std::function< TestResult ( Invoker, const std::string & ) > impl;
TestResult
operator() ( Invoker invoker, const std::string &comment ) const
{
return impl( invoker, comment );
//if constexpr( std::is_base_of_v< std::decay_t< return_type >, ComputedBase > )
}
#if 1
BasicUniversalHandler( const return_type expected )
: impl
BasicUniversalHandler( const return_type expected ) : impl
{
[expected]( Invoker invoker, const std::string &comment )
{
@ -157,108 +101,12 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
}
}
{}
#endif
#if 0
template< typename ... Args >
requires ConstructibleFrom< return_type, std::decay_t< Args >... >
BasicUniversalHandler( Args &&... expected_init )
: BasicUniversalHandler( return_type{ std::forward< Args >( expected_init )... } )
{}
#endif
#if 1
template< typename T >
requires( not SameAs< T, void > )
BasicUniversalHandler( std::type_identity< T > ) : impl
{
[]( Invoker invoker, const std::string &comment )
{
try
{
std::ignore= invoker();
std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
return TestResult::Failed;
}
catch( const T & )
{
std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl;
return TestResult::Passed;
}
}
}
{}
template< typename T >
requires( SameAs< T, std::type_identity< void > > or SameAs< T, std::nothrow_t > )
BasicUniversalHandler( T ) : impl
{
[]( Invoker invoker, const std::string &comment )
{
try
{
std::ignore= invoker();
std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl;
return TestResult::Passed;
}
catch( ... )
{
std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
return TestResult::Failed;
}
}
}
{}
template< DerivedFrom< std::exception > T >
BasicUniversalHandler( const T exemplar ) : impl
{
[expected= std::string{ exemplar.what() }]( Invoker invoker, const std::string &comment )
{
try
{
invoker();
std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
std::cout << " " << C::testInfo << "NOTE" << resetStyle << ": expected exception `"
<< typeid( T ).name()
<< "` wasn't thrown." << std::endl;
return TestResult::Failed;
}
catch( const T &ex )
{
const std::string witness= ex.what();
const TestResult rv= witness == expected ? TestResult::Passed : TestResult::Failed;
if( rv == TestResult::Failed )
{
std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
std::cout << " " << C::testInfo << "NOTE" << resetStyle << ": expected message did not match." << std::endl;
printDebugging< outputMode >( witness, expected );
}
return rv;
}
}
}
{}
#endif
};
template< Aggregate return_type, OutputMode outputMode >
struct BasicUniversalHandler< return_type, outputMode >
: compute_base_t< return_type >
{
using ComputedBase= compute_base_t< return_type >;
using ComputedBase::ComputedBase;
using Invoker= std::function< return_type () >;
std::function< TestResult ( Invoker, const std::string & ) > impl;
TestResult
operator() ( Invoker invoker, const std::string &comment ) const
{
if( impl != nullptr ) return impl( invoker, comment );
//if constexpr( std::is_base_of_v< std::decay_t< return_type >, ComputedBase > )
if constexpr( true )
if constexpr( std::is_base_of_v< std::decay_t< return_type >, ComputedBase > )
{
const return_type *const expected_p= this;
const auto expected= *expected_p;
@ -291,39 +139,6 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
else throw std::logic_error( "Somehow we didn't setup impl, and it's not an adapted case!" );
}
#if 0
template< typename T_= return_type, typename= std::enable_if_t< not std::is_class_v< std::decay_t< T_ > > > >
BasicUniversalHandler( const T_ expected )
: impl
{
[expected]( Invoker invoker, const std::string &comment )
{
static_assert( not Aggregate< T_ > );
static_assert( not std::is_class_v< T_ > );
const return_type witness= invoker();
const auto result= witness == expected ? TestResult::Passed : TestResult::Failed;
if( result == TestResult::Failed )
{
std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
printDebugging< outputMode >( witness, expected );
}
else std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl;
return result;
}
}
{}
#endif
#if 0
template< typename ... Args >
requires ConstructibleFrom< return_type, std::decay_t< Args >... >
BasicUniversalHandler( Args &&... expected_init )
: BasicUniversalHandler( return_type{ std::forward< Args >( expected_init )... } )
{}
#endif
#if 1
template< typename T >
requires( not SameAs< T, void > )
BasicUniversalHandler( std::type_identity< T > ) : impl
@ -396,167 +211,8 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
}
}
{}
#endif
};
template< typename return_type, OutputMode outputMode >
struct BasicUniversalHandler
: return_type
{
using return_type::return_type;
BasicUniversalHandler( return_type rt ) : return_type( rt ) {}
using Invoker= std::function< return_type () >;
std::function< TestResult ( Invoker, const std::string & ) > impl;
TestResult
operator() ( Invoker invoker, const std::string &comment ) const
{
if( impl != nullptr ) return impl( invoker, comment );
//if constexpr( std::is_base_of_v< std::decay_t< return_type >, ComputedBase > )
if constexpr( true )
{
const return_type *const expected_p= this;
const auto expected= *expected_p;
const auto witness= Utility::evaluate <=[&]() -> std::optional< return_type >
{
try
{
return invoker();
}
catch( ... )
{
return std::nullopt;
}
};
const auto result= witness == expected ? TestResult::Passed : TestResult::Failed;
if( result == TestResult::Failed )
{
if( witness.has_value() )
{
std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
printDebugging< outputMode >( witness.value(), expected );
}
else std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": Unexpected exception in \"" << comment << '"' << std::endl;
}
else std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl;
return result;
}
else throw std::logic_error( "Somehow we didn't setup impl, and it's not an adapted case!" );
}
#if 0
template< typename T_= return_type, typename= std::enable_if_t< not std::is_class_v< std::decay_t< T_ > > > >
BasicUniversalHandler( const T_ expected )
: impl
{
[expected]( Invoker invoker, const std::string &comment )
{
static_assert( not Aggregate< T_ > );
static_assert( not std::is_class_v< T_ > );
const return_type witness= invoker();
const auto result= witness == expected ? TestResult::Passed : TestResult::Failed;
if( result == TestResult::Failed )
{
std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
printDebugging< outputMode >( witness, expected );
}
else std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl;
return result;
}
}
{}
#endif
#if 0
template< typename ... Args >
requires ConstructibleFrom< return_type, std::decay_t< Args >... >
BasicUniversalHandler( Args &&... expected_init )
: BasicUniversalHandler( return_type{ std::forward< Args >( expected_init )... } )
{}
#endif
#if 1
template< typename T >
requires( not SameAs< T, void > )
BasicUniversalHandler( std::type_identity< T > ) : impl
{
[]( Invoker invoker, const std::string &comment )
{
try
{
std::ignore= invoker();
std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
return TestResult::Failed;
}
catch( const T & )
{
std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl;
return TestResult::Passed;
}
}
}
{}
template< typename T >
requires( SameAs< T, std::type_identity< void > > or SameAs< T, std::nothrow_t > )
BasicUniversalHandler( T ) : impl
{
[]( Invoker invoker, const std::string &comment )
{
try
{
std::ignore= invoker();
std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl;
return TestResult::Passed;
}
catch( ... )
{
std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
return TestResult::Failed;
}
}
}
{}
template< DerivedFrom< std::exception > T >
BasicUniversalHandler( const T exemplar ) : impl
{
[expected= std::string{ exemplar.what() }]( Invoker invoker, const std::string &comment )
{
try
{
invoker();
std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
std::cout << " " << C::testInfo << "NOTE" << resetStyle << ": expected exception `"
<< typeid( T ).name()
<< "` wasn't thrown." << std::endl;
return TestResult::Failed;
}
catch( const T &ex )
{
const std::string witness= ex.what();
const TestResult rv= witness == expected ? TestResult::Passed : TestResult::Failed;
if( rv == TestResult::Failed )
{
std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
std::cout << " " << C::testInfo << "NOTE" << resetStyle << ": expected message did not match." << std::endl;
printDebugging< outputMode >( witness, expected );
}
return rv;
}
}
}
{}
#endif
};
template< typename F >
concept FunctionVariable=
requires( const F &f )
@ -564,7 +220,6 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
{ std::function{ f } };
};
namespace exports
{
template< FunctionVariable auto, OutputMode outputMode= OutputMode::All > struct TableTest;
@ -578,7 +233,6 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
using std::begin, std::end;
using namespace Utility::exports::evaluation_helpers;
using namespace std::literals::string_literals;
template< template< typename, typename... > class Sequence, typename ... TupleArgs >
auto
@ -598,151 +252,6 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
return rv;
}
template< OutputMode outputMode, typename T >
std::string
stringifyValue( const T &v )
{
std::ostringstream oss;
if constexpr( false ) ; // To keep the rest of the clauses regular
else if constexpr( std::is_same_v< std::uint8_t, std::decay_t< T > > )
{
oss << std::hex << std::setw( 2 ) << std::setfill( '0' ) << int( v );
}
else if constexpr( std::is_same_v< bool, std::decay_t< T > > )
{
oss << std::boolalpha << v;
}
else if constexpr( std::is_same_v< std::string, std::decay_t< T > > )
{
oss << "(String with " << v.size() << " chars)";
oss << '\n' << R"(""")" << '\n';
for( const char ch: v )
{
if( ch == '\n' ) oss << "<EOL>\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() ) : "<noopt>"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 << "<Unstreamable sequence of " << v.size() << " elements.>";
}
else
{
oss << Meta::sequence_kind_v< T > << "(" << v.size() << " elements):\n{" << std::endl;
int index= 0;
for( const auto &elem: v ) oss << "\t" << index++ << ": " << stringifyValue< outputMode >( elem ) << "," << std::endl;
oss << "}" << std::endl;
}
}
else if constexpr( Meta::is_pair_v< T > )
{
const auto &[ first, second ]= v;
return stringifyValue< outputMode >( std::tie( first, second ) );
}
else if constexpr( Meta::is_tuple_v< T > )
{
oss << '[';
tuple_for_each( v ) <=[&oss, first= true]( const auto &elem ) mutable
{
if( not first ) oss << ", ";
first= false;
oss << std::endl << stringifyValue< outputMode >( elem );
};
oss << std::endl << ']' << std::endl;
}
else if constexpr( std::is_same_v< T, TotalOrder > )
{
if( false ) ; // For alignment
else if( v == TotalOrder::less ) oss << "less";
else if ( v == TotalOrder::equal ) oss << "equal";
else if( v == TotalOrder::greater ) oss << "greater";
else throw std::logic_error( "Impossible `TotalOrder` condition." );
}
else
{
static_assert( dependent_value< false, T >, "One of the types used in the testing table does not support stringification." );
}
return std::move( oss ).str();
}
inline void
printDebuggingForStrings( const std::string &witness, const std::string &expected )
{
const std::size_t amount= std::min( witness.size(), expected.size() );
if( witness.size() != expected.size() )
{
std::cout << "Witness string size did not match the expected string size. Only mismatches found in the first "
<< amount << " characters will be printed." << std::endl;
}
for( int i= 0; i < amount; ++i )
{
if( witness.at( i ) == expected.at( i ) ) continue;
std::cout << "Mismatch at index: " << i << std::endl;
std::cout << "witness: " << witness.at( i ) << std::endl;
std::cout << "expected: " << expected.at( i ) << std::endl;
}
}
template< OutputMode outputMode, typename T >
void
printDebugging( const T &witness, const T &expected )
{
if constexpr( std::is_same_v< std::string, std::decay_t< T > > )
{
printDebuggingForStrings( witness, expected );
}
else if constexpr( Meta::is_sequence_v< T > )
{
if constexpr( std::is_same_v< std::string, typename T::value_type > )
{
if( witness.size() == expected.size() ) for( std::size_t i= 0; i < witness.size(); ++i )
{
if( witness.at( i ) != expected.at( i ) ) printDebuggingForStrings( witness.at( i ), expected.at( i ) );
}
}
else
{
if( witness.size() != expected.size() )
{
std::cout << "Witness sequence size of " << witness.size() << " did not match the expected sequence size of "
<< expected.size() << std::endl;
}
auto next= std::make_pair( begin( witness ), begin( expected ) );
bool first= true;
while( next.first != end( witness ) and next.second != end( expected ) )
{
if( not first )
{
std::cout << "Mismatch at witness index " << std::distance( begin( witness ), next.first ) << " and "
<< "expected index " << std::distance( begin( expected ), next.second ) << std::endl;
++next.first; ++next.second;
}
first= false;
next= std::mismatch( next.first, end( witness ), next.second, end( expected ) );
}
}
}
std::cout << std::endl
<< "computed: " << stringifyValue< outputMode >( witness ) << std::endl
<< "expected: " << stringifyValue< outputMode >( expected ) << std::endl << std::endl;
}
template< FunctionVariable auto function, OutputMode outputMode >
struct exports::TableTest
{
@ -751,175 +260,6 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
using args_type= Meta::product_type_decay_t< typename function_traits_type::args_type >;
using return_type= typename function_traits_type::return_type;
// The classic table-test engine would only support `Cases` which were run-and-test-value
// without the ability to test exceptions. The `ExceptionCases` construct was used to
// test throwing cases.
//
// A unified `Cases` type is forthcoming, and thus `ExecutionCases` exists for backwards
// compatibility.
struct ExecutionCases
{
using TestDescription= std::tuple< std::string, args_type, return_type >;
std::vector< TestDescription > tests;
explicit
ExecutionCases( std::initializer_list< TestDescription > initList )
: tests( initList ) {}
int
operator() () const
{
int failureCount= 0;
for( const auto &[ comment, params, expected ]: tests )
{
if( C::debugCaseTypes ) std::cerr << boost::core::demangle( typeid( params ).name() ) << std::endl;
breakpoint();
const auto witness= std::apply( function, params );
const auto result= witness == expected;
if( not result )
{
std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
++failureCount;
printDebugging< outputMode >( witness, expected );
}
else std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl;
}
return failureCount;
}
};
struct ExceptionCases_real
{
using Invoker= std::function< void () >;
struct ExceptionHandler
{
std::function< bool ( Invoker ) > impl;
bool operator() ( Invoker invoker ) const { return impl( invoker ); }
ExceptionHandler() : impl
{
[]( Invoker invoker )
{
try
{
invoker();
return true;
}
catch( ... ) { return false; }
}
}
{}
template< typename T >
requires( SameAs< T, std::type_identity< void > > or SameAs< T, std::nothrow_t > )
ExceptionHandler( T ) : impl
{
[]( Invoker invoker )
{
try
{
invoker();
return true;
}
catch( ... ) { return false; }
}
}
{}
template< typename T >
requires( not SameAs< T, void > )
ExceptionHandler( std::type_identity< T > ) : impl
{
[]( Invoker invoker )
{
try
{
invoker();
return false;
}
catch( const T & ) { return true; }
}
}
{}
template< typename T >
ExceptionHandler( const T exemplar ) : impl
{
[expected= std::string{ exemplar.what() }]( Invoker invoker )
{
try
{
invoker();
std::cerr << " " << C::testInfo << "NOTE" << resetStyle << ": expected exception `"<< typeid( T ).name()
<< "` wasn't thrown." << std::endl;
return false;
}
catch( const T &ex )
{
const std::string witness= ex.what();
const bool rv= witness == expected;
if( not rv )
{
std::cerr << " " << C::testInfo << "NOTE" << resetStyle << ": expected exception `"<< typeid( T ).name()
<< "` wasn't thrown." << std::endl;
printDebugging< outputMode >( witness, expected );
}
return rv;
}
}
}
{}
// This checker is invoked during `catch( ... )`
ExceptionHandler( const std::function< bool () > checker ) : impl
{
[=]( Invoker invoker )
{
try
{
invoker();
return false;
}
catch( ... ) { checker(); } // The `checker` can use `throw` to run any complex checks it needs.
}
}
{}
};
using TestDescription= std::tuple< std::string, args_type, ExceptionHandler >;
std::vector< TestDescription > tests;
explicit
ExceptionCases_real( std::initializer_list< TestDescription > initList )
: tests( initList ) {}
int
operator() () const
{
int failureCount= 0;
for( const auto &[ comment, params, checker ]: tests )
{
if( C::debugCaseTypes ) std::cerr << boost::core::demangle( typeid( params ).name() ) << std::endl;
breakpoint();
auto invoker= [&]{ std::apply( function, params ); };
const auto result= checker( invoker );
if( not result )
{
std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
++failureCount;
}
else std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl;
}
return failureCount;
}
};
using ComputedBase= compute_base_t< return_type >;
struct UniversalCases
@ -929,21 +269,17 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
using UniversalHandler= BasicUniversalHandler< return_type, outputMode >;
using TestDescription= std::tuple< std::string, args_type, UniversalHandler >;
struct TestDescription
{
std::string comment;
args_type args;
UniversalHandler handler;
};
std::vector< TestDescription > tests;
UniversalCases( std::initializer_list< TestDescription > initList )
: tests( initList )
{
for( const auto &desc: initList )
{
if constexpr( Aggregate< return_type > )
{
std::cerr << "Case: " << std::get< 0 >( desc );
const return_type &v= std::get< 2 >( desc );
std::cerr << " (" << v << ")" << std::endl;
tests.push_back( desc );
}
}
}
int
@ -958,7 +294,7 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
breakpoint();
return std::apply( function, params );
};
const TestResult result= checker( invoker, comment );
const auto result= checker( invoker, comment );
if( result == TestResult::Failed )
{
std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
@ -980,58 +316,6 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
//using ExceptionCases= ExceptionCases_real;
using ExceptionCases= UniversalCases;
};
#ifdef DISABLED
template< typename RetVal, typename ... Args, RetVal (*function)( Args... ) >
struct TableTest< function >::VectorCases
{
static_assert( sizeof...( Args ) == 1 );
static_assert( Meta::is_vector_v< RetVal > );
static_assert( Meta::is_vector_v< std::tuple_element_t< 0, std::tuple< Args... > > > );
using TestDescription= std::tuple< std::string,
std::vector< std::pair< typename std::tuple_element_t< 0, std::tuple< Args... > >::value_type, typename RetVal::value_type > > >;
std::vector< TestDescription > tests;
explicit
VectorCases( std::initializer_list< TestDescription > initList )
: tests( initList ) {}
int
operator() () const
{
int failureCount= 0;
for( const auto &[ comment, productions ]: tests )
{
const auto expected= evaluate <=[&]
{
std::vector< RetVal > rv;
std::transform( begin( productions ), end( productions ), back_inserter( rv ),
[]( const auto &prod ) { return prod.second; } );
return rv;
};
const auto params= evaluate <=[&]
{
std::vector< RetVal > rv;
std::transform( begin( productions ), end( productions ), back_inserter( rv ),
[]( const auto &prod ) { return prod.first; } );
return rv;
};
if( std::apply( function, std::tuple{ params } ) != expected )
{
std::cout << " FAILURE: " << comment << std::endl;
++failureCount;
}
else std::cout << " SUCCESS: " << comment << std::endl;
}
return failureCount;
}
};
#endif
}
namespace Alepha::Hydrogen::Testing::inline exports::inline table_test

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#include <Alepha/Testing/test.h>
#include <Alepha/Testing/TableTest.h>

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#include <Alepha/Testing/TableTest.h>

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

207
Testing/printDebugging.h Normal file
View File

@ -0,0 +1,207 @@
static_assert( __cplusplus > 2020'99 );
#pragma once
#include <Alepha/Alepha.h>
#include <cstdint>
#include <iostream>
#include <type_traits>
#include <iomanip>
#include <string>
#include <boost/core/demangle.hpp>
#include <boost/lexical_cast.hpp>
#include <Alepha/TotalOrder.h>
#include <Alepha/Console.h>
#include <Alepha/IOStreams/String.h>
#include <Alepha/Meta/product_type_decay.h>
#include <Alepha/Meta/sequence_kind.h>
#include <Alepha/Meta/is_vector.h>
#include <Alepha/Meta/is_optional.h>
#include <Alepha/Meta/is_streamable.h>
#include <Alepha/Meta/is_sequence.h>
#include <Alepha/Meta/is_product_type.h>
namespace Alepha::Hydrogen::Testing ::detail:: printDebugging_m
{
inline namespace exports
{
enum class OutputMode { All, Relaxed };
template< OutputMode outputMode, typename T >
void printDebugging( const T &witness, const T &expected );
}
using namespace std::literals::string_literals;
template< OutputMode outputMode, typename T >
void
streamValue( const T &v, std::ostream &oss )
{
if constexpr( false ) ; // To keep the rest of the clauses regular
else if constexpr( std::is_same_v< std::uint8_t, std::decay_t< T > > )
{
oss << std::hex << std::setw( 2 ) << std::setfill( '0' ) << int( v );
}
else if constexpr( std::is_same_v< bool, std::decay_t< T > > )
{
oss << std::boolalpha << v;
}
else if constexpr( std::is_same_v< std::string, std::decay_t< T > > )
{
oss << "(String with " << v.size() << " chars)";
oss << '\n' << R"(""")" << '\n';
for( const char ch: v )
{
if( ch == '\n' ) oss << "<EOL>\n";
else if( std::isalnum( ch ) or std::ispunct( ch ) or ( ch == ' ' ) ) oss << ch;
else oss << "<\\0x" << std::hex << std::setw( 2 ) << std::setfill( '0' ) << unsigned( ch ) << '>';
}
oss << '\n' << R"(""")";
}
else if constexpr( Meta::is_ostreamable_v< T > )
{
oss << v;
}
else if constexpr( Meta::is_optional_v< T > )
{
if( not v.has_value() ) oss << "<noopt>";
else streamValue< outputMode >( v.value(), oss );
}
else if constexpr( Meta::is_sequence_v< T > )
{
if constexpr( outputMode == OutputMode::Relaxed and not Meta::is_ostreamable_v< typename T::value_type > )
{
oss << "<Unstreamable sequence of " << v.size() << " elements.>";
}
else
{
oss << Meta::sequence_kind_v< T > << "(" << v.size() << " elements):\n{" << std::endl;
int index= 0;
for( const auto &elem: v )
{
oss << "\t" << index++ << ": ";
streamValue< outputMode >( elem, oss );
oss << "," << std::endl;
}
oss << "}" << std::endl;
}
}
else if constexpr( Meta::is_pair_v< T > )
{
const auto &[ first, second ]= v;
streamValue< outputMode >( std::tie( first, second ), oss );
}
else if constexpr( Meta::is_tuple_v< T > )
{
oss << '[';
tuple_for_each( v ) <=[&oss, first= true]( const auto &elem ) mutable
{
if( not first ) oss << ", ";
first= false;
oss << std::endl;
streamValue< outputMode >( elem, oss );
};
oss << std::endl << ']' << std::endl;
}
else if constexpr( std::is_same_v< T, TotalOrder > )
{
if( false ) ; // For alignment
else if( v == TotalOrder::less ) oss << "less";
else if( v == TotalOrder::equal ) oss << "equal";
else if( v == TotalOrder::greater ) oss << "greater";
else throw std::logic_error( "Impossible `TotalOrder` condition." );
}
else
{
static_assert( dependent_value< false, T >, "One of the types used in the testing table does not support stringification." );
}
}
template< OutputMode outputMode, typename T >
std::string
stringifyValue( const T &v )
{
std::ostringstream oss;
streamValue< outputMode >( v, oss );
return std::move( oss ).str();
}
inline void
printDebuggingForStrings( const std::string &witness, const std::string &expected )
{
const std::size_t amount= std::min( witness.size(), expected.size() );
if( witness.size() != expected.size() )
{
std::cout << "Witness string size did not match the expected string size. Only mismatches found in the first "
<< amount << " characters will be printed." << std::endl;
}
for( int i= 0; i < amount; ++i )
{
if( witness.at( i ) == expected.at( i ) ) continue;
std::cout << "Mismatch at index: " << i << std::endl;
std::cout << "witness: " << witness.at( i ) << std::endl;
std::cout << "expected: " << expected.at( i ) << std::endl;
}
}
template< OutputMode outputMode, typename T >
void
exports::printDebugging( const T &witness, const T &expected )
{
if constexpr( std::is_same_v< std::string, std::decay_t< T > > )
{
printDebuggingForStrings( witness, expected );
}
else if constexpr( Meta::is_sequence_v< T > )
{
if constexpr( std::is_same_v< std::string, typename T::value_type > )
{
if( witness.size() == expected.size() ) for( std::size_t i= 0; i < witness.size(); ++i )
{
if( witness.at( i ) != expected.at( i ) ) printDebuggingForStrings( witness.at( i ), expected.at( i ) );
}
}
else
{
if( witness.size() != expected.size() )
{
std::cout << "Witness sequence size of " << witness.size() << " did not match the expected sequence size of "
<< expected.size() << std::endl;
}
auto next= std::make_pair( begin( witness ), begin( expected ) );
bool first= true;
while( next.first != end( witness ) and next.second != end( expected ) )
{
if( not first )
{
std::cout << "Mismatch at witness index " << std::distance( begin( witness ), next.first ) << " and "
<< "expected index " << std::distance( begin( expected ), next.second ) << std::endl;
++next.first; ++next.second;
}
first= false;
next= std::mismatch( next.first, end( witness ), next.second, end( expected ) );
}
}
}
std::cout << std::endl
<< "computed: " << stringifyValue< outputMode >( witness ) << std::endl
<< "expected: " << stringifyValue< outputMode >( expected ) << std::endl << std::endl;
}
}
namespace Alepha::Hydrogen::Testing::inline exports::inline printDebugging_m
{
using namespace detail::printDebugging_m::exports;
}

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#include "test.h"

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#include <Alepha/Testing/test.h>
#include <Alepha/ProgramOptions.h>

View File

@ -1,12 +1,12 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once
#include <Alepha/Alepha.h>
#include <Alepha/boost_path/thread.hpp>
#include <Alepha/boost_path/thread/mutex.hpp>
#include <Alepha/boost_path/thread/condition_variable.hpp>
#include <boost/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition_variable.hpp>
#include <Alepha/Exception.h>
@ -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;
}
}

View File

@ -0,0 +1,3 @@
link_libraries( boost_thread )
unit_test( thread )

View File

@ -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

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#include <Alepha/Thread.h>
@ -6,7 +6,7 @@ static_assert( __cplusplus > 2020'00 );
#include <Alepha/Testing/test.h>
#include <Alepha/Testing/TableTest.h>
#include <Alepha/Utility/evaluation.h>
#include <Alepha/Utility/evaluation_helpers.h>
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 )

View File

@ -1,12 +1,14 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once
#include <Alepha/Alepha.h>
#include <compare>
#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;
}

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

View File

@ -1,4 +1,4 @@
static_assert( __cplusplus > 2020'00 );
static_assert( __cplusplus > 2020'99 );
#pragma once

Some files were not shown because too many files have changed in this diff Show More