forked from Alepha/Alepha
Merge branch 'master' of coruscant:/Sources/Repositories/Alepha
This commit is contained in:
2
Alepha.h
2
Alepha.h
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
193
Atomic/Mailbox.h
Normal file
193
Atomic/Mailbox.h
Normal 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
|
||||||
|
*/
|
||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#include <Alepha/AutoRAII.h>
|
#include <Alepha/AutoRAII.h>
|
||||||
|
|
||||||
|
|||||||
84
Blob.h
84
Blob.h
@ -1,18 +1,23 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <Alepha/Alepha.h>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "Buffer.h"
|
#include "Buffer.h"
|
||||||
#include "swappable.h"
|
#include "swappable.h"
|
||||||
#include "stringify.h"
|
#include "Exception.h"
|
||||||
#include "Exceptions.h"
|
//#include "threading.h"
|
||||||
#include "evaluation_helpers.h"
|
|
||||||
#include "threading.h"
|
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
|
|
||||||
namespace Alepha::inline Cavorite ::detail:: blob
|
#include <Alepha/IOStreams/String.h>
|
||||||
|
#include <Alepha/Utility/evaluation_helpers.h>
|
||||||
|
|
||||||
|
namespace Alepha::Hydrogen ::detail:: Blob_m
|
||||||
{
|
{
|
||||||
inline namespace exports
|
inline namespace exports
|
||||||
{
|
{
|
||||||
@ -34,28 +39,29 @@ namespace Alepha::inline Cavorite ::detail:: blob
|
|||||||
}
|
}
|
||||||
|
|
||||||
using std::begin, std::end;
|
using std::begin, std::end;
|
||||||
|
using IOStreams::stringify;
|
||||||
|
|
||||||
class exports::DataCarveTooLargeError
|
class exports::DataCarveTooLargeError
|
||||||
: public virtual OutOfRangeError
|
: public virtual Buffer_m::OutOfRangeError
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit
|
explicit
|
||||||
DataCarveTooLargeError( const void *const location, const std::size_t request, const std::size_t available )
|
DataCarveTooLargeError( const void *const location, const std::size_t request, const std::size_t available )
|
||||||
: std::out_of_range( "Tried to carve " + stringify( request ) + " bytes from `Blob` object at location "
|
: std::out_of_range( "Tried to carve " + stringify( request ) + " bytes from `Blob` object at location "
|
||||||
+ stringify( location ) + " which only has " + stringify( avail ) + " bytes allocated." ),
|
+ stringify( location ) + " which only has " + stringify( available ) + " bytes allocated." ),
|
||||||
OutOfRangeError( location, request, available )
|
OutOfRangeError( location, request, available )
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class exports::DataCarveOutOfRangeError
|
class exports::DataCarveOutOfRangeError
|
||||||
: public virtual OutOfRangeError
|
: public virtual Buffer_m::OutOfRangeError
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit
|
explicit
|
||||||
DataCarveOutOfRanceError( const void *const location, const std::size_t request, const std::size_t available )
|
DataCarveOutOfRangeError( const void *const location, const std::size_t request, const std::size_t available )
|
||||||
: std::out_of_range( "Tried to carve " + stringify( request ) + " bytes from `Blob` object at location "
|
: std::out_of_range( "Tried to carve " + stringify( request ) + " bytes from `Blob` object at location "
|
||||||
+ stringify( location ) + " which only has " + stringify( avail ) + " bytes allocated." ),
|
+ stringify( location ) + " which only has " + stringify( available ) + " bytes allocated." ),
|
||||||
OutOfRangeError( location, request, available )
|
OutOfRangeError( location, request, available )
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
@ -66,25 +72,27 @@ namespace Alepha::inline Cavorite ::detail:: blob
|
|||||||
private:
|
private:
|
||||||
using IndirectStorage= std::shared_ptr< std::shared_ptr< Blob > >;
|
using IndirectStorage= std::shared_ptr< std::shared_ptr< Blob > >;
|
||||||
IndirectStorage storage; // If this is empty, then this `Blob` object doesn't share ownership. This references the shared pool.
|
IndirectStorage storage; // If this is empty, then this `Blob` object doesn't share ownership. This references the shared pool.
|
||||||
Buffer buffer;
|
Buffer< Mutable > buffer;
|
||||||
std::size_t viewLimit= 0; // TODO: Consider allowing for unrooted sub-buffer views?
|
std::size_t viewLimit= 0; // TODO: Consider allowing for unrooted sub-buffer views?
|
||||||
|
|
||||||
// TODO: Take the `storage` parameter and make it not increment when this ctor is called -- only when the dice roll passes.
|
// TODO: Take the `storage` parameter and make it not increment when this ctor is called -- only when the dice roll passes.
|
||||||
explicit
|
explicit
|
||||||
Blob( IndirectStorage storage, Buffer buffer ) noexcept
|
Blob( IndirectStorage storage, Buffer< Mutable > buffer ) noexcept
|
||||||
: storage( evaluate <=[storage= std::move( storage )] () -> IndirectBacking
|
: storage( Utility::evaluate <=[storage= std::move( storage )] () -> IndirectStorage
|
||||||
{
|
{
|
||||||
if( fastRandomBits( C::storageSplitRandomBitDepth ) ) return std::move( storage );
|
//if( fastRandomBits( C::storageSplitRandomBitDepth ) )
|
||||||
if( C::debugSplitSharing ) error() << "Observed a use count of " << storage.use_count() << " when we failed the dice roll." << std::endl;
|
return std::move( storage );
|
||||||
auto split= std::make_shared< std::shared_ptr< Blob > >( *storage );
|
//if( C::debugSplitSharing ) error() << "Observed a use count of " << storage.use_count() << " when we failed the dice roll." << std::endl;
|
||||||
if( C::
|
//auto split= std::make_shared< std::shared_ptr< Blob > >( *storage );
|
||||||
|
//if( C::
|
||||||
|
//return split;
|
||||||
}),
|
}),
|
||||||
buffer( buffer )
|
buffer( buffer ),
|
||||||
viewLimit( buffer.size() )
|
viewLimit( buffer.size() )
|
||||||
{}
|
{}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~Buffer() { reset(); }
|
~Blob() { reset(); }
|
||||||
|
|
||||||
auto
|
auto
|
||||||
swap_lens() noexcept
|
swap_lens() noexcept
|
||||||
@ -106,7 +114,7 @@ namespace Alepha::inline Cavorite ::detail:: blob
|
|||||||
void
|
void
|
||||||
reset() noexcept
|
reset() noexcept
|
||||||
{
|
{
|
||||||
if( not storage ) delete buffer;
|
if( not storage ) delete [] buffer.byte_data();
|
||||||
else storage.reset();
|
else storage.reset();
|
||||||
|
|
||||||
buffer= {};
|
buffer= {};
|
||||||
@ -125,7 +133,7 @@ namespace Alepha::inline Cavorite ::detail:: blob
|
|||||||
* @note: No data are copied.
|
* @note: No data are copied.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
reset( const std::size_t )
|
reset( const std::size_t size )
|
||||||
{
|
{
|
||||||
Blob tmp{ size };
|
Blob tmp{ size };
|
||||||
swap( tmp, *this );
|
swap( tmp, *this );
|
||||||
@ -133,8 +141,8 @@ namespace Alepha::inline Cavorite ::detail:: blob
|
|||||||
|
|
||||||
// Copy deep copies the data.
|
// Copy deep copies the data.
|
||||||
Blob( const Blob © )
|
Blob( const Blob © )
|
||||||
: buffer( new std::byte[ copy.buffer.size() ] ),
|
: buffer( new std::byte[ copy.size() ], copy.size() ),
|
||||||
viewLimit( copy.viewLimit )
|
viewLimit( copy.size() )
|
||||||
{
|
{
|
||||||
if( C::debugCtors ) error() << "Blob copy invoked." << std::endl;
|
if( C::debugCtors ) error() << "Blob copy invoked." << std::endl;
|
||||||
copyData( *this, copy );
|
copyData( *this, copy );
|
||||||
@ -178,13 +186,13 @@ namespace Alepha::inline Cavorite ::detail:: blob
|
|||||||
|
|
||||||
explicit
|
explicit
|
||||||
Blob( const std::size_t amount )
|
Blob( const std::size_t amount )
|
||||||
: buffer( new std::byte[ amount ]{} ), // The data must be 0'ed upon allocation.
|
: buffer( new std::byte[ amount ]{}, amount ), // The data must be 0'ed upon allocation.
|
||||||
viewLimit( amount )
|
viewLimit( amount )
|
||||||
{}
|
{}
|
||||||
|
|
||||||
explicit
|
explicit
|
||||||
Blob( const Buffer< Const > b )
|
Blob( const Buffer< Const > b )
|
||||||
: Buffer( b.size() )
|
: Blob( b.size() )
|
||||||
{
|
{
|
||||||
copyData( buffer, b );
|
copyData( buffer, b );
|
||||||
}
|
}
|
||||||
@ -228,7 +236,7 @@ namespace Alepha::inline Cavorite ::detail:: blob
|
|||||||
|
|
||||||
// Now we assume that there's a two-layer scheme, so we operate based upon that.
|
// Now we assume that there's a two-layer scheme, so we operate based upon that.
|
||||||
|
|
||||||
Blob rv{ storage, Buffer< Mutable >{ buffer, amount } }
|
Blob rv{ storage, Buffer< Mutable >{ buffer, amount } };
|
||||||
buffer= buffer + amount;
|
buffer= buffer + amount;
|
||||||
viewLimit-= amount;
|
viewLimit-= amount;
|
||||||
|
|
||||||
@ -260,7 +268,7 @@ namespace Alepha::inline Cavorite ::detail:: blob
|
|||||||
template< typename T > void operator []( T ) const= delete;
|
template< typename T > void operator []( T ) const= delete;
|
||||||
template< typename T > void operator []( T )= delete;
|
template< typename T > void operator []( T )= delete;
|
||||||
|
|
||||||
constexpr std::size_t capacity() const noecept { return buffer.size(); }
|
constexpr std::size_t capacity() const noexcept { return buffer.size(); }
|
||||||
|
|
||||||
bool
|
bool
|
||||||
isContiguousWith( const Blob &other ) const & noexcept
|
isContiguousWith( const Blob &other ) const & noexcept
|
||||||
@ -269,7 +277,7 @@ namespace Alepha::inline Cavorite ::detail:: blob
|
|||||||
(
|
(
|
||||||
storage != nullptr
|
storage != nullptr
|
||||||
and
|
and
|
||||||
*storage == *other.backing
|
*storage == *other.storage
|
||||||
and
|
and
|
||||||
byte_data() + size() == other.byte_data()
|
byte_data() + size() == other.byte_data()
|
||||||
);
|
);
|
||||||
@ -331,7 +339,7 @@ namespace Alepha::inline Cavorite ::detail:: blob
|
|||||||
bool
|
bool
|
||||||
couldConcatenate( const Buffer< Const > buffer ) const noexcept
|
couldConcatenate( const Buffer< Const > buffer ) const noexcept
|
||||||
{
|
{
|
||||||
return data.size() <= ( capacity() - size() );
|
return buffer.size() <= ( capacity() - size() );
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -372,9 +380,9 @@ namespace Alepha::inline Cavorite ::detail:: blob
|
|||||||
[[nodiscard]] Buffer< constness >
|
[[nodiscard]] Buffer< constness >
|
||||||
concatenate( const Buffer< constness > data ) noexcept
|
concatenate( const Buffer< constness > data ) noexcept
|
||||||
{
|
{
|
||||||
const auto amount= std::min( capacity - size(), data.size() );
|
const auto amount= std::min( capacity() - size(), data.size() );
|
||||||
const DataWindow< const > fitted{ data, amount };
|
const Buffer< Const > fitted{ data, amount };
|
||||||
copyData( buffer, + size(), fitted );
|
copyData( buffer + size(), fitted );
|
||||||
setSize( size() + amount );
|
setSize( size() + amount );
|
||||||
return data + amount;
|
return data + amount;
|
||||||
}
|
}
|
||||||
@ -406,7 +414,7 @@ namespace Alepha::inline Cavorite ::detail:: blob
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const auto amount= concatenate( Buffer< Const >{ blob } ).size()
|
const auto amount= concatenate( Buffer< Const >{ blob } ).size();
|
||||||
const auto rv= blob.carveTail( amount );
|
const auto rv= blob.carveTail( amount );
|
||||||
blob.reset();
|
blob.reset();
|
||||||
return rv;
|
return rv;
|
||||||
@ -482,11 +490,11 @@ namespace Alepha::inline Cavorite ::detail:: blob
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert( HasCapability< Blob, swappable > );
|
//static_assert( Capability< Blob, swappable > );
|
||||||
static_assert( detail::swaps::SwapLensable< Blob > );
|
//static_assert( detail::swaps::SwapLensable< Blob > );
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Alepha::Cavorite::inline exports::inline blob
|
namespace Alepha::Hydrogen::inline exports::inline Blob_m
|
||||||
{
|
{
|
||||||
using namespace detail::blob::exports;
|
using namespace detail::Blob_m::exports;
|
||||||
}
|
}
|
||||||
|
|||||||
25
Blob.test/0.cc
Normal file
25
Blob.test/0.cc
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
|
#include "../Blob.h"
|
||||||
|
|
||||||
|
#include <Alepha/Testing/test.h>
|
||||||
|
|
||||||
|
#include <Alepha/Utility/evaluation_helpers.h>
|
||||||
|
|
||||||
|
static auto init= Alepha::Utility::enroll <=[]
|
||||||
|
{
|
||||||
|
using namespace Alepha::Testing::literals::test_literals;
|
||||||
|
"Simple carve head test"_test <=[]
|
||||||
|
{
|
||||||
|
Alepha::Blob b{ 1024 };
|
||||||
|
|
||||||
|
auto b2= b.carveHead( 256 );
|
||||||
|
|
||||||
|
assert( b.size() == 768 );
|
||||||
|
assert( b2.size() == 256 );
|
||||||
|
|
||||||
|
std::string h= "Hello world";
|
||||||
|
|
||||||
|
copyData( b2, Alepha::make_buffer( h ) );
|
||||||
|
};
|
||||||
|
};
|
||||||
84
Buffer.h
84
Buffer.h
@ -1,7 +1,12 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <Alepha.h>
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <array>
|
#include <array>
|
||||||
@ -10,19 +15,22 @@ static_assert( __cplusplus > 2020'00 );
|
|||||||
#include <exception>
|
#include <exception>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
#include <boost/lexical_cast.hpp>
|
#include <Alepha/Constness.h>
|
||||||
|
#include <Alepha/lifetime.h>
|
||||||
|
|
||||||
|
#include <Alepha/IOStreams/String.h>
|
||||||
|
|
||||||
#include "Concepts.h"
|
#include "Concepts.h"
|
||||||
#include "assertion.h"
|
#include "assertion.h"
|
||||||
#include "Capabilities.h"
|
#include "Capabilities.h"
|
||||||
#include "lifetime.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace Alepha::inline Cavorite ::detail:: buffer
|
namespace Alepha::Hydrogen ::detail:: Buffer_m
|
||||||
{
|
{
|
||||||
inline namespace exports {}
|
inline namespace exports {}
|
||||||
|
|
||||||
using namespace std::literals::string_literals;
|
using namespace std::literals::string_literals;
|
||||||
|
using IOStreams::stringify;
|
||||||
|
|
||||||
namespace exports
|
namespace exports
|
||||||
{
|
{
|
||||||
@ -42,7 +50,7 @@ namespace Alepha::inline Cavorite ::detail:: buffer
|
|||||||
: baseAddress( address ), requestedSize( requestedSize ), availableSize( availableSize )
|
: baseAddress( address ), requestedSize( requestedSize ), availableSize( availableSize )
|
||||||
{}
|
{}
|
||||||
|
|
||||||
public
|
public:
|
||||||
const void *getAddress() const noexcept { return baseAddress; }
|
const void *getAddress() const noexcept { return baseAddress; }
|
||||||
const std::size_t getRequestedSize() const noexcept { return requestedSize; }
|
const std::size_t getRequestedSize() const noexcept { return requestedSize; }
|
||||||
const std::size_t getAvailableSize() const noexcept { return availableSize; }
|
const std::size_t getAvailableSize() const noexcept { return availableSize; }
|
||||||
@ -62,20 +70,20 @@ namespace Alepha::inline Cavorite ::detail:: buffer
|
|||||||
: std::out_of_range( "Tried to access an object of type "s + type.name() + " which is " + stringify( requestedSize ) + " bytes in size. "
|
: std::out_of_range( "Tried to access an object of type "s + type.name() + " which is " + stringify( requestedSize ) + " bytes in size. "
|
||||||
+ "The request was at location " + stringify( location ) + " which only has " + stringify( availableSize )
|
+ "The request was at location " + stringify( location ) + " which only has " + stringify( availableSize )
|
||||||
+ " bytes allocated" ),
|
+ " bytes allocated" ),
|
||||||
OutOfRangeError( location, requestedSIze, availableSize ),
|
OutOfRangeError( location, requestedSize, availableSize ),
|
||||||
typeID( type )
|
typeID( type )
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
class OutOfRangeSizeError
|
class OutOfRangeSizeError
|
||||||
: virtual public OutOfRangeException
|
: virtual public OutOfRangeError
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit
|
explicit
|
||||||
OutOfRangeSizeException( const void *const location, const std::ptrdiff_t requestedOffset, const std::size_t availableSpace )
|
OutOfRangeSizeError( const void *const location, const std::ptrdiff_t requestedOffset, const std::size_t availableSpace )
|
||||||
: std::out_of_range( "Tried to view a byte offset of " + stringify( requestedOffset ) + " into location " + stringify( location )
|
: std::out_of_range( "Tried to view a byte offset of " + stringify( requestedOffset ) + " into location " + stringify( location )
|
||||||
+ " which is " + stringify( availableSpace ) + " bytes in size." ),
|
+ " which is " + stringify( availableSpace ) + " bytes in size." ),
|
||||||
OutOfRangeException( location, requestedOffset, availableSpace )
|
OutOfRangeError( location, requestedOffset, availableSpace )
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -106,26 +114,31 @@ namespace Alepha::inline Cavorite ::detail:: buffer
|
|||||||
|
|
||||||
constexpr
|
constexpr
|
||||||
Buffer( const pointer_type ptr, const std::size_t bytes ) noexcept
|
Buffer( const pointer_type ptr, const std::size_t bytes ) noexcept
|
||||||
: ptr( static_cast< byte_pointer_type >( ptr ), bytes( bytes )
|
: ptr( static_cast< byte_pointer_type >( ptr ) ), bytes( bytes )
|
||||||
{}
|
{}
|
||||||
|
|
||||||
constexpr
|
constexpr
|
||||||
Buffer( const Buffer< Mutable > © ) noexcept
|
Buffer( const Buffer< Mutable > © ) noexcept
|
||||||
: ptr( copy.byte_data() ), bytes( copy.size() ) {}
|
: ptr( copy.byte_data() ), bytes( copy.size() ) {}
|
||||||
|
|
||||||
|
template< Constness constness_= constness >
|
||||||
|
requires( constness_ == Mutable )
|
||||||
constexpr
|
constexpr
|
||||||
Buffer( const Buffer< Const > © ) noexcept requires( Constness == Mutable )= delete;
|
Buffer( const Buffer< Const > © ) noexcept = delete;
|
||||||
|
|
||||||
|
|
||||||
constexpr byte_pointer_type byte_data() const noexcept { return ptr; }
|
constexpr byte_pointer_type byte_data() const noexcept { return ptr; }
|
||||||
constexpr pointer_type data() const noexcept { return ptr; }
|
constexpr pointer_type data() const noexcept { return ptr; }
|
||||||
|
|
||||||
constexpr const_byte_pointer_type byte_data() const noexcept { return ptr; }
|
constexpr const_byte_pointer_type const_byte_data() const noexcept { return ptr; }
|
||||||
constexpr const_pointer_type data() const noexcept { return ptr; }
|
constexpr const_pointer_type const_data() const noexcept { return ptr; }
|
||||||
|
|
||||||
constexpr std::size_t size() const noexcept { return bytes; }
|
constexpr std::size_t size() const noexcept { return bytes; }
|
||||||
constexpr bool empty() const noexcept { return size() == 0; }
|
constexpr bool empty() const noexcept { return size() == 0; }
|
||||||
|
|
||||||
|
constexpr byte_pointer_type begin() const noexcept { return byte_data(); }
|
||||||
|
constexpr byte_pointer_type end() const noexcept { return begin() + size(); }
|
||||||
|
|
||||||
constexpr const_byte_pointer_type cbegin() const noexcept { return begin(); }
|
constexpr const_byte_pointer_type cbegin() const noexcept { return begin(); }
|
||||||
constexpr const_byte_pointer_type cend() const noexcept { return end(); }
|
constexpr const_byte_pointer_type cend() const noexcept { return end(); }
|
||||||
|
|
||||||
@ -137,7 +150,7 @@ namespace Alepha::inline Cavorite ::detail:: buffer
|
|||||||
as( std::nothrow_t ) const noexcept
|
as( std::nothrow_t ) const noexcept
|
||||||
{
|
{
|
||||||
assertion( sizeof( T ) <= bytes );
|
assertion( sizeof( T ) <= bytes );
|
||||||
return *start_lifetime_as< std::add_lvalue_reference_t< maybe_const_t< T, constness > > >( ptr );
|
return *Alepha::start_lifetime_as< std::add_lvalue_reference_t< maybe_const_t< T, constness > > >( ptr );
|
||||||
}
|
}
|
||||||
|
|
||||||
template< typename T >
|
template< typename T >
|
||||||
@ -153,7 +166,7 @@ namespace Alepha::inline Cavorite ::detail:: buffer
|
|||||||
const_as( std::nothrow_t ) const noexcept
|
const_as( std::nothrow_t ) const noexcept
|
||||||
{
|
{
|
||||||
assertion( sizeof( T ) <= bytes );
|
assertion( sizeof( T ) <= bytes );
|
||||||
return *start_lifetime_as< std::add_const_t< T > >( ptr );
|
return *Alepha::start_lifetime_as< std::add_const_t< T > >( ptr );
|
||||||
}
|
}
|
||||||
|
|
||||||
template< typename T >
|
template< typename T >
|
||||||
@ -222,7 +235,7 @@ namespace Alepha::inline Cavorite ::detail:: buffer
|
|||||||
struct BufferModel_capability {};
|
struct BufferModel_capability {};
|
||||||
|
|
||||||
template< typename T >
|
template< typename T >
|
||||||
concept UndecayedBufferModelable= HasCapability< T, BufferModel_capability >;
|
concept UndecayedBufferModelable= Capability< T, BufferModel_capability >;
|
||||||
|
|
||||||
template< typename T >
|
template< typename T >
|
||||||
concept BufferModelable= UndecayedBufferModelable< std::decay_t< T > >;
|
concept BufferModelable= UndecayedBufferModelable< std::decay_t< T > >;
|
||||||
@ -234,8 +247,8 @@ namespace Alepha::inline Cavorite ::detail:: buffer
|
|||||||
constexpr auto &crtp() noexcept { return static_cast< Derived & >( *this ); }
|
constexpr auto &crtp() noexcept { return static_cast< Derived & >( *this ); }
|
||||||
constexpr const auto &crtp() const noexcept { return static_cast< const Derived & >( *this ); }
|
constexpr const auto &crtp() const noexcept { return static_cast< const Derived & >( *this ); }
|
||||||
|
|
||||||
constexpr auto &buffer() { return static_cast< Buffer< Mutable > >( crtp() ); }
|
constexpr auto buffer() { return static_cast< Buffer< Mutable > >( crtp() ); }
|
||||||
constexpr const auto &buffer() const { return static_cast< Buffer< Const > >( crtp() ); }
|
constexpr auto buffer() const { return static_cast< Buffer< Const > >( crtp() ); }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
constexpr auto byte_data() { return buffer().byte_data(); }
|
constexpr auto byte_data() { return buffer().byte_data(); }
|
||||||
@ -273,8 +286,8 @@ namespace Alepha::inline Cavorite ::detail:: buffer
|
|||||||
template< typename T >
|
template< typename T >
|
||||||
extern Constness constness_of_v;
|
extern Constness constness_of_v;
|
||||||
|
|
||||||
template< UndecayedBufferModelble T >
|
template< UndecayedBufferModelable T >
|
||||||
constexpr Constness constness_of_v= std::is_const_v< T >;
|
constexpr Constness constness_of_v< T >{ std::is_const_v< T > };
|
||||||
|
|
||||||
template< Constness constness >
|
template< Constness constness >
|
||||||
constexpr Constness constness_of_v< Buffer< constness > >{ constness };
|
constexpr Constness constness_of_v< Buffer< constness > >{ constness };
|
||||||
@ -283,7 +296,7 @@ namespace Alepha::inline Cavorite ::detail:: buffer
|
|||||||
constexpr auto
|
constexpr auto
|
||||||
operator + ( const Buffer< constness > buffer, const std::size_t offset )
|
operator + ( const Buffer< constness > buffer, const std::size_t offset )
|
||||||
{
|
{
|
||||||
if( offset > buffer.size() ) throw OutOfRangeError{ buffer.data(), offset, buffer.size() };
|
if( offset > buffer.size() ) throw OutOfRangeSizeError{ buffer.data(), std::ptrdiff_t( offset ), buffer.size() };
|
||||||
return Buffer< constness >{ buffer.byte_data() + offset, buffer.size() - offset };
|
return Buffer< constness >{ buffer.byte_data() + offset, buffer.size() - offset };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -372,23 +385,6 @@ namespace Alepha::inline Cavorite ::detail:: buffer
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template< Concepts::StandardLayout T, std::size_t size >
|
|
||||||
constexpr Buffer< Mutable >
|
|
||||||
make_buffer( T array[ size ] ) noexcept
|
|
||||||
{
|
|
||||||
// TODO: Do we need to consider overflow here?
|
|
||||||
return { array, sizeof( array ) };
|
|
||||||
}
|
|
||||||
|
|
||||||
template< Concepts::StandardLayout T, std::size_t size >
|
|
||||||
constexpr Buffer< Const >
|
|
||||||
make_buffer( const T array[ size ] ) noexcept
|
|
||||||
{
|
|
||||||
// TODO: Do we need to consider overflow here?
|
|
||||||
return { array, sizeof( array ) };
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inline Buffer< Mutable >
|
inline Buffer< Mutable >
|
||||||
make_buffer( std::string &string ) noexcept
|
make_buffer( std::string &string ) noexcept
|
||||||
{
|
{
|
||||||
@ -407,7 +403,7 @@ namespace Alepha::inline Cavorite ::detail:: buffer
|
|||||||
{
|
{
|
||||||
if( source.size() > destination.size() ) throw InsufficientSizeError{ destination.data(), source.size(), destination.size(), typeid( std::byte ) };
|
if( source.size() > destination.size() ) throw InsufficientSizeError{ destination.data(), source.size(), destination.size(), typeid( std::byte ) };
|
||||||
|
|
||||||
::memcpy( destination, source, source.size() );
|
std::memcpy( destination, source, source.size() );
|
||||||
return { destination, source.size() };
|
return { destination, source.size() };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -419,13 +415,13 @@ namespace Alepha::inline Cavorite ::detail:: buffer
|
|||||||
|
|
||||||
namespace exports
|
namespace exports
|
||||||
{
|
{
|
||||||
using detail::buffer::make_buffer;
|
using detail::Buffer_m::make_buffer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Alepha::Cavorite::inline exports::inline buffer
|
namespace Alepha::Hydrogen::inline exports::inline Buffer_m
|
||||||
{
|
{
|
||||||
using namespace detail::buffer::exports;
|
using namespace detail::Buffer_m::exports;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -453,8 +449,8 @@ namespace Alepha::Cavorite::inline exports::inline buffer
|
|||||||
|
|
||||||
template<>
|
template<>
|
||||||
constexpr auto
|
constexpr auto
|
||||||
std::cbegin( const ::Alepha::Cavorite::Buffer< Alepha::Cavorite::Mutable > &range ) -> decltype( range.begin() )= delete;
|
std::cbegin( const ::Alepha::Hydrogen::Buffer< Alepha::Hydrogen::Mutable > &range ) -> decltype( range.begin() )= delete;
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
constexpr auto
|
constexpr auto
|
||||||
std::cend( const ::Alepha::Cavorite::Buffer< Alepha::Cavorite::Mutable > &range ) -> decltype( range.end() )= delete;
|
std::cend( const ::Alepha::Hydrogen::Buffer< Alepha::Hydrogen::Mutable > &range ) -> decltype( range.end() )= delete;
|
||||||
|
|||||||
@ -3,6 +3,7 @@ project( Alepha )
|
|||||||
|
|
||||||
include( cmake/rules.cmake )
|
include( cmake/rules.cmake )
|
||||||
|
|
||||||
|
link_libraries( pthread )
|
||||||
|
|
||||||
|
|
||||||
# The core alepha library:
|
# The core alepha library:
|
||||||
@ -31,6 +32,10 @@ add_subdirectory( Exception.test )
|
|||||||
add_subdirectory( word_wrap.test )
|
add_subdirectory( word_wrap.test )
|
||||||
add_subdirectory( string_algorithms.test )
|
add_subdirectory( string_algorithms.test )
|
||||||
add_subdirectory( tuplize_args.test )
|
add_subdirectory( tuplize_args.test )
|
||||||
|
add_subdirectory( Thread.test )
|
||||||
|
add_subdirectory( assertion.test )
|
||||||
|
add_subdirectory( Constness.test )
|
||||||
|
add_subdirectory( Blob.test )
|
||||||
|
|
||||||
# Sample applications
|
# Sample applications
|
||||||
add_executable( example example.cc )
|
add_executable( example example.cc )
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@ -78,6 +78,7 @@ namespace Alepha::Hydrogen
|
|||||||
has_cap_in_capability_base( const Left &, Meta::type_value< Cap > cap )
|
has_cap_in_capability_base( const Left &, Meta::type_value< Cap > cap )
|
||||||
{
|
{
|
||||||
throw "Unevaluated";
|
throw "Unevaluated";
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template< typename Cap, typename ... TParams >
|
template< typename Cap, typename ... TParams >
|
||||||
@ -100,6 +101,13 @@ namespace Alepha::Hydrogen
|
|||||||
return has_cap( Meta::Container::vector< TParams... >{}, cap );
|
return has_cap( Meta::Container::vector< TParams... >{}, cap );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template< typename Type, typename Cap >
|
||||||
|
consteval bool
|
||||||
|
has_cap( const Meta::type_value< Type > &, Meta::type_value< Cap > )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
namespace exports
|
namespace exports
|
||||||
{
|
{
|
||||||
template< typename T, typename cap >
|
template< typename T, typename cap >
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@ -11,7 +11,7 @@ static_assert( __cplusplus > 2020'00 );
|
|||||||
#include "meta.h"
|
#include "meta.h"
|
||||||
#include "function_traits.h"
|
#include "function_traits.h"
|
||||||
|
|
||||||
namespace Alepha::Hydrogen ::detail:: core_concepts
|
namespace Alepha::Hydrogen ::detail:: Concepts_m
|
||||||
{
|
{
|
||||||
inline namespace exports
|
inline namespace exports
|
||||||
{
|
{
|
||||||
@ -282,7 +282,7 @@ namespace Alepha::Hydrogen ::detail:: core_concepts
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Alepha::Hydrogen::inline exports::inline core_concepts
|
namespace Alepha::Hydrogen::inline exports::inline Concepts_m::inline Concepts
|
||||||
{
|
{
|
||||||
using namespace detail::core_concepts::exports;
|
using namespace detail::Concepts_m::exports;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#include "Console.h"
|
#include "Console.h"
|
||||||
|
|
||||||
@ -13,9 +13,9 @@ static_assert( __cplusplus > 2020'00 );
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include <ext/stdio_filebuf.h>
|
|
||||||
|
|
||||||
#include <Alepha/Utility/evaluation_helpers.h>
|
#include <Alepha/Utility/evaluation_helpers.h>
|
||||||
|
#include <Alepha/IOStreams/OutUnixFileBuf.h>
|
||||||
|
|
||||||
#include "Enum.h"
|
#include "Enum.h"
|
||||||
#include "ProgramOptions.h"
|
#include "ProgramOptions.h"
|
||||||
@ -348,8 +348,7 @@ namespace Alepha::Hydrogen ::detail:: console_m
|
|||||||
struct Console::Impl
|
struct Console::Impl
|
||||||
{
|
{
|
||||||
int fd;
|
int fd;
|
||||||
// TODO: Do we want to make this not gnu libstdc++ specific?
|
IOStreams::OutUnixFileBuf filebuf;
|
||||||
__gnu_cxx::stdio_filebuf< char > filebuf;
|
|
||||||
std::ostream stream;
|
std::ostream stream;
|
||||||
std::stack< std::pair< struct termios, ConsoleMode > > modeStack;
|
std::stack< std::pair< struct termios, ConsoleMode > > modeStack;
|
||||||
ConsoleMode mode= cooked;
|
ConsoleMode mode= cooked;
|
||||||
@ -357,7 +356,7 @@ namespace Alepha::Hydrogen ::detail:: console_m
|
|||||||
|
|
||||||
explicit
|
explicit
|
||||||
Impl( const int fd )
|
Impl( const int fd )
|
||||||
: fd( fd ), filebuf( fd, std::ios::out ), stream( &filebuf )
|
: fd( fd ), filebuf( fd ), stream( &filebuf )
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <Alepha/Alepha.h>
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
@ -9,9 +11,7 @@ static_assert( __cplusplus > 2020'00 );
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include <Alepha/Alepha.h>
|
namespace Alepha::Hydrogen ::detail:: ConstexprString_m
|
||||||
|
|
||||||
namespace Alepha::Cavorite ::detail:: constexpr_string
|
|
||||||
{
|
{
|
||||||
namespace C
|
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
152
Constness.h
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
static_assert( __cplusplus > 2020'00 );
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Alepha/Alepha.h>
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "meta.h"
|
||||||
|
|
||||||
|
namespace Alepha::Hydrogen ::detail:: Constness_m
|
||||||
|
{
|
||||||
|
inline namespace exports
|
||||||
|
{
|
||||||
|
/*!
|
||||||
|
* Conditional constness primitive.
|
||||||
|
*
|
||||||
|
* C++17 and beyond have many conditional attributes on functions. Conditional `noexcept`,
|
||||||
|
* conditional `explicit`, and even conditional instantiation. The `noexcept` and `explicit`
|
||||||
|
* are conditional on a boolean condition. This enum permits making templates which
|
||||||
|
* are conditionally const, depending upon instantiation.
|
||||||
|
*
|
||||||
|
* Simple example:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* template< Constness constness >
|
||||||
|
* struct IntWrapper
|
||||||
|
* {
|
||||||
|
* private:
|
||||||
|
* int variable;
|
||||||
|
*
|
||||||
|
* public:
|
||||||
|
* maybe_const_t< int &, constness > // This is sort of like if one could write `const( constness ) int &`
|
||||||
|
* view() { return variable; }
|
||||||
|
*
|
||||||
|
* const int &
|
||||||
|
* view() const { return variable; }
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* using MutableIntWrapper= IntWrapper< Mutable >;
|
||||||
|
* using ConstIntWrapper= IntWrapper< Const >;
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Now `Constness< Mutable >` has a member `view` which returns `int &` and `Constness< Const >` has a member
|
||||||
|
* `view` which returns `const int &`. This facility can be useful in implementing pairs of `const`/non-`const`
|
||||||
|
* iterators, and other gadgets.
|
||||||
|
*/
|
||||||
|
enum Constness : bool
|
||||||
|
{
|
||||||
|
Const= true,
|
||||||
|
Mutable= false,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Apply the `Constness` requested to the specified type.
|
||||||
|
*
|
||||||
|
* If the type isn't `const`, but `Constness` is set, then
|
||||||
|
* it evaluates to `const T`.
|
||||||
|
*/
|
||||||
|
template< typename Type, Constness > struct maybe_const;
|
||||||
|
|
||||||
|
template< typename Type >
|
||||||
|
struct maybe_const< Type, Const >
|
||||||
|
{
|
||||||
|
using type= std::add_const_t< Type >;
|
||||||
|
};
|
||||||
|
|
||||||
|
template< typename Type >
|
||||||
|
struct maybe_const< Type, Mutable >
|
||||||
|
{
|
||||||
|
using type= Type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template< typename Type >
|
||||||
|
struct maybe_const< Type &, Const >
|
||||||
|
{
|
||||||
|
using type= std::add_const_t< Type > &;
|
||||||
|
};
|
||||||
|
|
||||||
|
template< typename Type >
|
||||||
|
struct maybe_const< Type &, Mutable >
|
||||||
|
{
|
||||||
|
using type= Type &;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Conditionally make `Type` `const`.
|
||||||
|
*/
|
||||||
|
template< typename Type, Constness constness >
|
||||||
|
using maybe_const_t= typename maybe_const< Type, constness >::type;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Conditionally make `Type` into `const Type *`.
|
||||||
|
*/
|
||||||
|
template< typename Type, Constness constness >
|
||||||
|
using maybe_const_ptr_t= std::add_pointer_t< maybe_const_t< Type, constness > >;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Conditionally call `std::as_const`.
|
||||||
|
*
|
||||||
|
* Sometimes `std::as_const` is appropriate to call, and sometimes it isn't.
|
||||||
|
* In some cases, one might want to have type deduction driven by an expression
|
||||||
|
* where, in a `const` "branch" `std::as_const` is called, but in a non-`const`
|
||||||
|
* branch, it isn't. This facilitates that:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* template< Constness constness >
|
||||||
|
* struct IntWrapper
|
||||||
|
* {
|
||||||
|
* private:
|
||||||
|
* int variable;
|
||||||
|
*
|
||||||
|
* public:
|
||||||
|
* // `std::as_const` will be called when `constness` is true,
|
||||||
|
* // and will be skipped otherwise. This permits the right
|
||||||
|
* // client overload to be called
|
||||||
|
* template< typename Function >
|
||||||
|
* decltype( auto )
|
||||||
|
* apply( Function func )
|
||||||
|
* {
|
||||||
|
* return func( maybe_as_const< constness >( variable ) );
|
||||||
|
* }
|
||||||
|
* };
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
template< Constness constness, typename T >
|
||||||
|
constexpr auto &
|
||||||
|
maybe_as_const( T &t ) noexcept
|
||||||
|
{
|
||||||
|
if constexpr( constness ) return std::as_const( t );
|
||||||
|
else return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
template< Constness constness, typename T >
|
||||||
|
constexpr decltype( auto )
|
||||||
|
maybe_as_const( const T &t ) noexcept
|
||||||
|
{
|
||||||
|
if constexpr( constness ) return std::as_const( t );
|
||||||
|
else static_assert( dependent_value< false, T > );
|
||||||
|
}
|
||||||
|
|
||||||
|
template< Constness constness, typename T >
|
||||||
|
void maybe_as_const( T && )= delete;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Alepha::Hydrogen::inline exports::inline Constness_m
|
||||||
|
{
|
||||||
|
using namespace detail::Constness_m::exports;
|
||||||
|
}
|
||||||
3
Constness.test/0.cc
Normal file
3
Constness.test/0.cc
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
|
#include "../Constness.h"
|
||||||
1
Constness.test/CMakeLists.txt
Normal file
1
Constness.test/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
unit_test( 0 )
|
||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
14
Enum.h
14
Enum.h
@ -1,7 +1,9 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <Alepha/Alepha.h>
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
@ -17,7 +19,7 @@ static_assert( __cplusplus > 2020'00 );
|
|||||||
#include "ConstexprString.h"
|
#include "ConstexprString.h"
|
||||||
#include "meta.h"
|
#include "meta.h"
|
||||||
|
|
||||||
namespace Alepha::inline Cavorite ::detail:: enhanced_enum
|
namespace Alepha::Hydrogen ::detail:: Enum_m
|
||||||
{
|
{
|
||||||
inline namespace exports {}
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@ -277,6 +277,8 @@ namespace Alepha::Hydrogen ::detail:: Exception_m
|
|||||||
|
|
||||||
~Violation() override { if( not active ) abort(); }
|
~Violation() override { if( not active ) abort(); }
|
||||||
|
|
||||||
|
Violation()= default;
|
||||||
|
|
||||||
Violation( const Violation © )= delete;
|
Violation( const Violation © )= delete;
|
||||||
Violation( Violation © ) : active( copy.active ) { copy.active= false; }
|
Violation( Violation © ) : active( copy.active ) { copy.active= false; }
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#include <Alepha/Exception.h>
|
#include <Alepha/Exception.h>
|
||||||
|
|
||||||
|
|||||||
388
Format
Normal file
388
Format
Normal 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.
|
||||||
@ -1,3 +1,10 @@
|
|||||||
add_subdirectory( IStreamable.test )
|
add_subdirectory( IStreamable.test )
|
||||||
add_subdirectory( OStreamable.test )
|
add_subdirectory( OStreamable.test )
|
||||||
add_subdirectory( streamable.test )
|
add_subdirectory( streamable.test )
|
||||||
|
add_subdirectory( OutUnixFileBuf.test )
|
||||||
|
add_subdirectory( StackableStreambuf.test )
|
||||||
|
|
||||||
|
target_sources( alepha PRIVATE
|
||||||
|
StackableStreambuf.cc
|
||||||
|
)
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#include "../IStreamable.h"
|
#include "../IStreamable.h"
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#include "../OStreamable.h"
|
#include "../OStreamable.h"
|
||||||
|
|
||||||
|
|||||||
47
IOStreams/OutUnixFileBuf.h
Normal file
47
IOStreams/OutUnixFileBuf.h
Normal 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;
|
||||||
|
}
|
||||||
28
IOStreams/OutUnixFileBuf.test/0.cc
Normal file
28
IOStreams/OutUnixFileBuf.test/0.cc
Normal 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 };
|
||||||
|
};
|
||||||
|
};
|
||||||
1
IOStreams/OutUnixFileBuf.test/CMakeLists.txt
Normal file
1
IOStreams/OutUnixFileBuf.test/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
unit_test( 0 )
|
||||||
@ -1,17 +1,25 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#include "StackableStreambuf.h"
|
#include "StackableStreambuf.h"
|
||||||
|
|
||||||
namespace Alepha::Hydrogen::Utility::detail::stackable_streambuf
|
#include <cassert>
|
||||||
|
|
||||||
|
namespace Alepha::Hydrogen::IOStreams::detail::stackable_streambuf
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
const auto index= std::ios::xalloc();
|
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 )
|
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 ) >{};
|
if( not ownership ) ownership= new std::decay_t< decltype( *ownership ) >{};
|
||||||
|
|
||||||
return *ownership;
|
return *ownership;
|
||||||
@ -24,6 +32,7 @@ namespace Alepha::Hydrogen::Utility::detail::stackable_streambuf
|
|||||||
{
|
{
|
||||||
auto &ownership= getStack( ios );
|
auto &ownership= getStack( ios );
|
||||||
|
|
||||||
|
assert( not ownership.empty() );
|
||||||
// Since it's owned, delete happens at scope exit.
|
// Since it's owned, delete happens at scope exit.
|
||||||
const std::unique_ptr top= std::move( ownership.top() );
|
const std::unique_ptr top= std::move( ownership.top() );
|
||||||
ownership.pop();
|
ownership.pop();
|
||||||
@ -48,9 +57,8 @@ namespace Alepha::Hydrogen::Utility::detail::stackable_streambuf
|
|||||||
inline void
|
inline void
|
||||||
releaseStack( std::ios_base &ios )
|
releaseStack( std::ios_base &ios )
|
||||||
{
|
{
|
||||||
auto &os= dynamic_cast< std::ostream & >( ios );
|
delete getStackPtr( ios );
|
||||||
|
getStackPtr( ios )= nullptr;
|
||||||
while( releaseTop( os ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@ -10,7 +10,7 @@ static_assert( __cplusplus > 2020'00 );
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
|
|
||||||
namespace Alepha::Hydrogen::Utility ::detail:: stackable_streambuf
|
namespace Alepha::Hydrogen::IOStreams ::detail:: stackable_streambuf
|
||||||
{
|
{
|
||||||
inline namespace exports
|
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;
|
using namespace detail::stackable_streambuf::exports;
|
||||||
}
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#include "../StackableStreambuf.h"
|
#include "../StackableStreambuf.h"
|
||||||
|
|
||||||
1
IOStreams/StackableStreambuf.test/CMakeLists.txt
Normal file
1
IOStreams/StackableStreambuf.test/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
unit_test( 0 )
|
||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#include "../streamable.h"
|
#include "../streamable.h"
|
||||||
|
|
||||||
|
|||||||
26
LICENSE
26
LICENSE
@ -1,23 +1,3 @@
|
|||||||
Boost Software License - Version 1.0 - August 17th, 2003
|
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
|
||||||
Permission is hereby granted, free of charge, to any person or organization
|
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
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.
|
|
||||||
|
|||||||
373
MPL-LICENSE.txt
Normal file
373
MPL-LICENSE.txt
Normal 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.
|
||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@ -69,6 +69,7 @@ namespace Alepha::Hydrogen::Meta::Container
|
|||||||
};
|
};
|
||||||
|
|
||||||
template< typename MetaFunction, typename Arg1, typename First, typename ... Members >
|
template< typename MetaFunction, typename Arg1, typename First, typename ... Members >
|
||||||
|
requires( sizeof...( Members ) != 0 )
|
||||||
constexpr decltype( auto )
|
constexpr decltype( auto )
|
||||||
invoke_call( MetaFunction func, type_value< Arg1 > arg1, dereferenced_iterator< vector< First, Members... > > deref )
|
invoke_call( MetaFunction func, type_value< Arg1 > arg1, dereferenced_iterator< vector< First, Members... > > deref )
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#include <Alepha/Meta/is_sequence.h>
|
#include <Alepha/Meta/is_sequence.h>
|
||||||
#include <Alepha/Meta/is_streamable.h>
|
#include <Alepha/Meta/is_streamable.h>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@ -18,8 +18,8 @@ namespace Alepha::Hydrogen::Meta
|
|||||||
template< typename T >
|
template< typename T >
|
||||||
struct is_optional : std::false_type {};
|
struct is_optional : std::false_type {};
|
||||||
|
|
||||||
template< typename T, typename ... Args >
|
template< typename T >
|
||||||
struct is_optional< std::optional< T, Args... > > : std::true_type {};
|
struct is_optional< std::optional< T > > : std::true_type {};
|
||||||
|
|
||||||
template< typename T >
|
template< typename T >
|
||||||
constexpr bool is_optional_v= is_optional< T >::value;
|
constexpr bool is_optional_v= is_optional< T >::value;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#include "ProgramOptions.h"
|
#include "ProgramOptions.h"
|
||||||
|
|
||||||
@ -10,7 +10,9 @@ static_assert( __cplusplus > 2020'00 );
|
|||||||
#include <Alepha/StaticValue.h>
|
#include <Alepha/StaticValue.h>
|
||||||
#include <Alepha/error.h>
|
#include <Alepha/error.h>
|
||||||
|
|
||||||
namespace Alepha::Cavorite ::detail:: program_options
|
#include <Alepha/System/programName.h>
|
||||||
|
|
||||||
|
namespace Alepha::Hydrogen ::detail:: ProgramOptions_m
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
@ -205,9 +207,7 @@ namespace Alepha::Cavorite ::detail:: program_options
|
|||||||
|
|
||||||
VariableMap substitutions=
|
VariableMap substitutions=
|
||||||
{
|
{
|
||||||
// This uses a GNU extension, but it's fine. We can always make this
|
{ "program-name"s, lambaste<=System::programName() },
|
||||||
// portable, later.
|
|
||||||
{ "program-name"s, lambaste<=::program_invocation_short_name },
|
|
||||||
{ "option-name"s, lambaste<=name },
|
{ "option-name"s, lambaste<=name },
|
||||||
{ "default"s, [&defaultBuilder= defaultBuilder, &name= name]
|
{ "default"s, [&defaultBuilder= defaultBuilder, &name= name]
|
||||||
{
|
{
|
||||||
@ -283,8 +283,7 @@ namespace Alepha::Cavorite ::detail:: program_options
|
|||||||
{
|
{
|
||||||
VariableMap substitutions
|
VariableMap substitutions
|
||||||
{
|
{
|
||||||
// Another use of the GNUism.
|
{ "program-name"s, lambaste<=System::programName() },
|
||||||
{ "program-name"s, lambaste<=::program_invocation_short_name },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if( canonicalName.has_value() ) substitutions[ "canonical-name"s ]= lambaste<=canonicalName.value();
|
if( canonicalName.has_value() ) substitutions[ "canonical-name"s ]= lambaste<=canonicalName.value();
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@ -96,7 +96,7 @@ static_assert( __cplusplus > 2020'00 );
|
|||||||
|
|
||||||
#include <Alepha/Utility/evaluation_helpers.h>
|
#include <Alepha/Utility/evaluation_helpers.h>
|
||||||
|
|
||||||
namespace Alepha::inline Cavorite ::detail:: program_options
|
namespace Alepha::Hydrogen ::detail:: ProgramOptions_m
|
||||||
{
|
{
|
||||||
inline namespace exports {}
|
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.
|
// 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;
|
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 );
|
impl::checkArgument( argument, name );
|
||||||
|
|
||||||
@ -311,7 +311,7 @@ namespace Alepha::inline Cavorite ::detail:: program_options
|
|||||||
};
|
};
|
||||||
handler( parsed );
|
handler( parsed );
|
||||||
};
|
};
|
||||||
return registerHandler( handler );
|
return registerHandler( wrapped );
|
||||||
}
|
}
|
||||||
else
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
/*!
|
/*!
|
||||||
* @file
|
* @file
|
||||||
* @brief The `Attestation` framework permits code which can provide limited compiletime guarantees of conditions.
|
* @brief The `Attestation` framework permits code which can provide limited compiletime guarantees of conditions.
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@ -72,14 +72,6 @@ namespace Alepha::Hydrogen::Reflection
|
|||||||
// any initialization method.
|
// any initialization method.
|
||||||
struct argument { template< typename T > constexpr operator T (); };
|
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.
|
// Any empty-base-class argument.
|
||||||
template< typename Aggregate >
|
template< typename Aggregate >
|
||||||
struct empty_base
|
struct empty_base
|
||||||
@ -88,7 +80,6 @@ namespace Alepha::Hydrogen::Reflection
|
|||||||
requires
|
requires
|
||||||
(
|
(
|
||||||
true
|
true
|
||||||
//typename= typename checker< std::decay_t< T > >::type,
|
|
||||||
and EmptyType< std::decay_t< T > >
|
and EmptyType< std::decay_t< T > >
|
||||||
and not SameAs< std::decay_t< T >, Aggregate >
|
and not SameAs< std::decay_t< T >, Aggregate >
|
||||||
and DerivedFrom< Aggregate, std::decay_t< T > >
|
and DerivedFrom< Aggregate, std::decay_t< T > >
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#include <Alepha/Reflection/tuplizeAggregate.h>
|
#include <Alepha/Reflection/tuplizeAggregate.h>
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
223
Style
Normal file
223
Style
Normal 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
46
System/programName.h
Normal 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;
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@ -17,81 +17,32 @@ static_assert( __cplusplus > 2020'00 );
|
|||||||
#include <boost/core/demangle.hpp>
|
#include <boost/core/demangle.hpp>
|
||||||
#include <boost/lexical_cast.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/function_traits.h>
|
||||||
#include <Alepha/template_for_each.h>
|
#include <Alepha/template_for_each.h>
|
||||||
|
|
||||||
#include <Alepha/IOStreams/String.h>
|
#include <Alepha/IOStreams/String.h>
|
||||||
|
|
||||||
#include <Alepha/Utility/evaluation_helpers.h>
|
#include <Alepha/Utility/evaluation_helpers.h>
|
||||||
|
#include <Alepha/Utility/TupleAdapter.h>
|
||||||
|
|
||||||
#include <Alepha/Reflection/tuplizeAggregate.h>
|
#include <Alepha/Reflection/tuplizeAggregate.h>
|
||||||
|
|
||||||
#include <Alepha/TotalOrder.h>
|
|
||||||
#include <Alepha/Console.h>
|
#include <Alepha/Console.h>
|
||||||
|
|
||||||
#include "colors.h"
|
#include "colors.h"
|
||||||
|
#include "printDebugging.h"
|
||||||
|
|
||||||
namespace Alepha::Hydrogen::Testing ::detail:: table_test
|
namespace Alepha::Hydrogen::Testing ::detail:: table_test
|
||||||
{
|
{
|
||||||
inline namespace exports
|
inline namespace exports {}
|
||||||
{
|
|
||||||
enum class OutputMode { All, Relaxed };
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void breakpoint() {}
|
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 };
|
enum class TestResult { Passed, Failed };
|
||||||
|
|
||||||
struct BlankBase {};
|
struct BlankBase {};
|
||||||
@ -100,7 +51,7 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
|
|||||||
static consteval auto
|
static consteval auto
|
||||||
compute_base_f() noexcept
|
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 if constexpr( std::is_class_v< T > ) return std::type_identity< T >{};
|
||||||
else return std::type_identity< BlankBase >{};
|
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;
|
using compute_base_t= typename decltype( compute_base_f< std::decay_t< T > >() )::type;
|
||||||
|
|
||||||
template< typename return_type, OutputMode outputMode >
|
template< typename return_type, OutputMode outputMode >
|
||||||
struct BasicUniversalHandler;
|
struct BasicUniversalHandler
|
||||||
|
: compute_base_t< return_type >
|
||||||
template< Primitive return_type, OutputMode outputMode >
|
|
||||||
struct BasicUniversalHandler< return_type, outputMode >
|
|
||||||
{
|
{
|
||||||
|
using ComputedBase= compute_base_t< return_type >;
|
||||||
|
using ComputedBase::ComputedBase;
|
||||||
|
|
||||||
using Invoker= std::function< return_type () >;
|
using Invoker= std::function< return_type () >;
|
||||||
|
|
||||||
std::function< TestResult ( Invoker, const std::string & ) > impl;
|
std::function< TestResult ( Invoker, const std::string & ) > impl;
|
||||||
|
|
||||||
TestResult
|
BasicUniversalHandler( const return_type expected ) : impl
|
||||||
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
|
|
||||||
{
|
{
|
||||||
[expected]( Invoker invoker, const std::string &comment )
|
[expected]( Invoker invoker, const std::string &comment )
|
||||||
{
|
{
|
||||||
@ -142,7 +86,7 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
const auto result= witness == expected ? TestResult::Passed : TestResult::Failed;
|
const auto result= witness == expected ? TestResult::Passed : TestResult::Failed;
|
||||||
|
|
||||||
if( result == TestResult::Failed )
|
if( result == TestResult::Failed )
|
||||||
{
|
{
|
||||||
if( witness.has_value() )
|
if( witness.has_value() )
|
||||||
@ -157,108 +101,12 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
{}
|
{}
|
||||||
#endif
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
template< typename ... Args >
|
|
||||||
requires ConstructibleFrom< return_type, std::decay_t< Args >... >
|
|
||||||
BasicUniversalHandler( Args &&... expected_init )
|
|
||||||
: BasicUniversalHandler( return_type{ std::forward< Args >( expected_init )... } )
|
|
||||||
{}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if 1
|
|
||||||
template< typename T >
|
|
||||||
requires( not SameAs< T, void > )
|
|
||||||
BasicUniversalHandler( std::type_identity< T > ) : impl
|
|
||||||
{
|
|
||||||
[]( Invoker invoker, const std::string &comment )
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
std::ignore= invoker();
|
|
||||||
std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
|
|
||||||
return TestResult::Failed;
|
|
||||||
}
|
|
||||||
catch( const T & )
|
|
||||||
{
|
|
||||||
std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl;
|
|
||||||
return TestResult::Passed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{}
|
|
||||||
|
|
||||||
template< typename T >
|
|
||||||
requires( SameAs< T, std::type_identity< void > > or SameAs< T, std::nothrow_t > )
|
|
||||||
BasicUniversalHandler( T ) : impl
|
|
||||||
{
|
|
||||||
[]( Invoker invoker, const std::string &comment )
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
std::ignore= invoker();
|
|
||||||
std::cout << " " << C::testPass << "PASSED CASE" << resetStyle << ": " << comment << std::endl;
|
|
||||||
return TestResult::Passed;
|
|
||||||
}
|
|
||||||
catch( ... )
|
|
||||||
{
|
|
||||||
std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
|
|
||||||
return TestResult::Failed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{}
|
|
||||||
|
|
||||||
template< DerivedFrom< std::exception > T >
|
|
||||||
BasicUniversalHandler( const T exemplar ) : impl
|
|
||||||
{
|
|
||||||
[expected= std::string{ exemplar.what() }]( Invoker invoker, const std::string &comment )
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
invoker();
|
|
||||||
std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
|
|
||||||
std::cout << " " << C::testInfo << "NOTE" << resetStyle << ": expected exception `"
|
|
||||||
<< typeid( T ).name()
|
|
||||||
<< "` wasn't thrown." << std::endl;
|
|
||||||
return TestResult::Failed;
|
|
||||||
}
|
|
||||||
catch( const T &ex )
|
|
||||||
{
|
|
||||||
const std::string witness= ex.what();
|
|
||||||
const TestResult rv= witness == expected ? TestResult::Passed : TestResult::Failed;
|
|
||||||
if( rv == TestResult::Failed )
|
|
||||||
{
|
|
||||||
std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
|
|
||||||
std::cout << " " << C::testInfo << "NOTE" << resetStyle << ": expected message did not match." << std::endl;
|
|
||||||
printDebugging< outputMode >( witness, expected );
|
|
||||||
}
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{}
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
template< Aggregate return_type, OutputMode outputMode >
|
|
||||||
struct BasicUniversalHandler< return_type, outputMode >
|
|
||||||
: compute_base_t< return_type >
|
|
||||||
{
|
|
||||||
using ComputedBase= compute_base_t< return_type >;
|
|
||||||
using ComputedBase::ComputedBase;
|
|
||||||
|
|
||||||
using Invoker= std::function< return_type () >;
|
|
||||||
|
|
||||||
std::function< TestResult ( Invoker, const std::string & ) > impl;
|
|
||||||
|
|
||||||
TestResult
|
TestResult
|
||||||
operator() ( Invoker invoker, const std::string &comment ) const
|
operator() ( Invoker invoker, const std::string &comment ) const
|
||||||
{
|
{
|
||||||
if( impl != nullptr ) return impl( invoker, comment );
|
if( impl != nullptr ) return impl( invoker, comment );
|
||||||
//if constexpr( std::is_base_of_v< std::decay_t< return_type >, ComputedBase > )
|
if constexpr( std::is_base_of_v< std::decay_t< return_type >, ComputedBase > )
|
||||||
if constexpr( true )
|
|
||||||
{
|
{
|
||||||
const return_type *const expected_p= this;
|
const return_type *const expected_p= this;
|
||||||
const auto expected= *expected_p;
|
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!" );
|
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 >
|
template< typename T >
|
||||||
requires( not SameAs< T, void > )
|
requires( not SameAs< T, void > )
|
||||||
BasicUniversalHandler( std::type_identity< T > ) : impl
|
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 >
|
template< typename F >
|
||||||
concept FunctionVariable=
|
concept FunctionVariable=
|
||||||
requires( const F &f )
|
requires( const F &f )
|
||||||
@ -564,7 +220,6 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
|
|||||||
{ std::function{ f } };
|
{ std::function{ f } };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
namespace exports
|
namespace exports
|
||||||
{
|
{
|
||||||
template< FunctionVariable auto, OutputMode outputMode= OutputMode::All > struct TableTest;
|
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 std::begin, std::end;
|
||||||
using namespace Utility::exports::evaluation_helpers;
|
using namespace Utility::exports::evaluation_helpers;
|
||||||
using namespace std::literals::string_literals;
|
|
||||||
|
|
||||||
template< template< typename, typename... > class Sequence, typename ... TupleArgs >
|
template< template< typename, typename... > class Sequence, typename ... TupleArgs >
|
||||||
auto
|
auto
|
||||||
@ -598,151 +252,6 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
|
|||||||
return rv;
|
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 >
|
template< FunctionVariable auto function, OutputMode outputMode >
|
||||||
struct exports::TableTest
|
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 args_type= Meta::product_type_decay_t< typename function_traits_type::args_type >;
|
||||||
using return_type= typename function_traits_type::return_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 >;
|
using ComputedBase= compute_base_t< return_type >;
|
||||||
|
|
||||||
struct UniversalCases
|
struct UniversalCases
|
||||||
@ -929,21 +269,17 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
|
|||||||
|
|
||||||
using UniversalHandler= BasicUniversalHandler< return_type, outputMode >;
|
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;
|
std::vector< TestDescription > tests;
|
||||||
|
|
||||||
UniversalCases( std::initializer_list< TestDescription > initList )
|
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
|
int
|
||||||
@ -958,7 +294,7 @@ namespace Alepha::Hydrogen::Testing ::detail:: table_test
|
|||||||
breakpoint();
|
breakpoint();
|
||||||
return std::apply( function, params );
|
return std::apply( function, params );
|
||||||
};
|
};
|
||||||
const TestResult result= checker( invoker, comment );
|
const auto result= checker( invoker, comment );
|
||||||
if( result == TestResult::Failed )
|
if( result == TestResult::Failed )
|
||||||
{
|
{
|
||||||
std::cout << " " << C::testFail << "FAILED CASE" << resetStyle << ": " << comment << std::endl;
|
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= ExceptionCases_real;
|
||||||
using ExceptionCases= UniversalCases;
|
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
|
namespace Alepha::Hydrogen::Testing::inline exports::inline table_test
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#include <Alepha/Testing/test.h>
|
#include <Alepha/Testing/test.h>
|
||||||
#include <Alepha/Testing/TableTest.h>
|
#include <Alepha/Testing/TableTest.h>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#include <Alepha/Testing/TableTest.h>
|
#include <Alepha/Testing/TableTest.h>
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
207
Testing/printDebugging.h
Normal file
207
Testing/printDebugging.h
Normal 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;
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#include "test.h"
|
#include "test.h"
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#include <Alepha/Testing/test.h>
|
#include <Alepha/Testing/test.h>
|
||||||
#include <Alepha/ProgramOptions.h>
|
#include <Alepha/ProgramOptions.h>
|
||||||
|
|||||||
30
Thread.h
30
Thread.h
@ -1,12 +1,12 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Alepha/Alepha.h>
|
#include <Alepha/Alepha.h>
|
||||||
|
|
||||||
#include <Alepha/boost_path/thread.hpp>
|
#include <boost/thread.hpp>
|
||||||
#include <Alepha/boost_path/thread/mutex.hpp>
|
#include <boost/thread/mutex.hpp>
|
||||||
#include <Alepha/boost_path/thread/condition_variable.hpp>
|
#include <boost/thread/condition_variable.hpp>
|
||||||
|
|
||||||
#include <Alepha/Exception.h>
|
#include <Alepha/Exception.h>
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ namespace Alepha::Hydrogen
|
|||||||
{
|
{
|
||||||
callable();
|
callable();
|
||||||
}
|
}
|
||||||
catch( const boost_ns::thread_interrupted & )
|
catch( const boost::thread_interrupted & )
|
||||||
{
|
{
|
||||||
std::lock_guard lock( access );
|
std::lock_guard lock( access );
|
||||||
if( not notification ) throw;
|
if( not notification ) throw;
|
||||||
@ -63,7 +63,7 @@ namespace Alepha::Hydrogen
|
|||||||
namespace exports
|
namespace exports
|
||||||
{
|
{
|
||||||
class ConditionVariable
|
class ConditionVariable
|
||||||
: private boost_ns::condition_variable
|
: private boost::condition_variable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using condition_variable::notify_all;
|
using condition_variable::notify_all;
|
||||||
@ -89,17 +89,17 @@ namespace Alepha::Hydrogen
|
|||||||
{
|
{
|
||||||
template< typename Clock, typename Duration >
|
template< typename Clock, typename Duration >
|
||||||
void
|
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
|
#if 0
|
||||||
template< typename Rep, typename Period >
|
template< typename Rep, typename Period >
|
||||||
void
|
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
|
#endif
|
||||||
}
|
}
|
||||||
@ -113,7 +113,7 @@ namespace Alepha::Hydrogen
|
|||||||
namespace exports
|
namespace exports
|
||||||
{
|
{
|
||||||
class Thread
|
class Thread
|
||||||
: ThreadNotification, boost_ns::thread
|
: ThreadNotification, boost::thread
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
template< typename Callable >
|
template< typename Callable >
|
||||||
@ -153,10 +153,10 @@ namespace Alepha::Hydrogen
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
using Mutex= boost_ns::mutex;
|
using Mutex= boost::mutex;
|
||||||
using boost_ns::mutex;
|
using boost::mutex;
|
||||||
using boost_ns::unique_lock;
|
using boost::unique_lock;
|
||||||
using boost_ns::lock_guard;
|
using boost::lock_guard;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
3
Thread.test/CMakeLists.txt
Normal file
3
Thread.test/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
link_libraries( boost_thread )
|
||||||
|
|
||||||
|
unit_test( thread )
|
||||||
@ -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
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#include <Alepha/Thread.h>
|
#include <Alepha/Thread.h>
|
||||||
|
|
||||||
@ -6,7 +6,7 @@ static_assert( __cplusplus > 2020'00 );
|
|||||||
|
|
||||||
#include <Alepha/Testing/test.h>
|
#include <Alepha/Testing/test.h>
|
||||||
#include <Alepha/Testing/TableTest.h>
|
#include <Alepha/Testing/TableTest.h>
|
||||||
#include <Alepha/Utility/evaluation.h>
|
#include <Alepha/Utility/evaluation_helpers.h>
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
@ -36,7 +36,7 @@ namespace
|
|||||||
}
|
}
|
||||||
catch( const boost::thread_interrupted & )
|
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;
|
throw;
|
||||||
}
|
}
|
||||||
catch( const MyNotification &n )
|
catch( const MyNotification &n )
|
||||||
|
|||||||
10
TotalOrder.h
10
TotalOrder.h
@ -1,12 +1,14 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <Alepha/Alepha.h>
|
||||||
|
|
||||||
#include <compare>
|
#include <compare>
|
||||||
|
|
||||||
#include "Concepts.h"
|
#include "Concepts.h"
|
||||||
|
|
||||||
namespace Alepha::inline Cavorite ::detail:: total_order
|
namespace Alepha::Hydrogen ::detail:: TotalOrder_m
|
||||||
{
|
{
|
||||||
inline namespace exports
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
static_assert( __cplusplus > 2020'00 );
|
static_assert( __cplusplus > 2020'99 );
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user