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