1
0
forked from Alepha/Alepha

Constness.

This commit is contained in:
2023-11-10 23:25:12 -05:00
parent e61e754d56
commit e2c5d28c5b
4 changed files with 157 additions and 0 deletions

152
Constness.h Normal file
View File

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