// Copyright (c) Herb Sutter
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
// 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//===========================================================================
// Cpp2 utilities:
// Language support implementations
// #include'd by generated Cpp1 code
//===========================================================================
#ifndef __CPP2_UTIL
#define __CPP2_UTIL
// If this implementation doesn't support source_location yet, disable it
#if !defined(_MSC_VER) && !defined(__cpp_lib_source_location)
#undef CPP2_USE_SOURCE_LOCATION
#endif
// If the cppfront user requested -pure-cpp2, this will be set
// and we should be using modules only
#ifdef CPP2_USE_MODULES
// If we have real modules, use those the best we can
// as implementations are still underway
#ifdef __cpp_modules
#ifndef _MSC_VER
// This is the ideal -- note that we just voted "import std;"
// into draft C++23 in late July 2022, so implementers haven't
// had time to catch up yet. As of this writing (September 2022)
// no compiler will take this path yet, but they're on the way...
import std;
#else // MSVC
// Note: When C++23 "import std;" is available, we will switch to that here
// In the meantime, this is what works on MSVC which is the only compiler
// I've been able to get access to that implements modules enough to demo
// (but we'll have more full-C++20 compilers soon!)
import std.core;
import std.regex;
import std.filesystem;
import std.memory;
import std.threading;
// Suppress spurious MSVC modules warning
#pragma warning(disable:5050)
#endif
// Otherwise, "fake it till you make it"... include (nearly) all the
// standard headers, with a feature test #ifdef for each header that
// isn't yet supported by all of { VS 2022, g++-10, clang++-12 }
// ... this should approximate "import std;" on those compilers
#else
#include
#ifdef __cpp_lib_coroutine
#include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef __cpp_lib_source_location
#include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef __cpp_lib_format
#include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef __cpp_lib_spanstream
#include
#endif
#include
#include
#ifdef __cpp_lib_syncstream
#include
#endif
#include
#include
#include
#ifdef __cpp_lib_barrier
#include
#endif
#include
#include
#ifdef __cpp_lib_latch
#include
#endif
#include
#ifdef __cpp_lib_semaphore
#include
#endif
#include
#include
#include
#include
#endif
// Otherwise, we're not in -pure-cpp2 and so just #include
// what we need in this header to make this self-contained
#else
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if defined(CPP2_USE_SOURCE_LOCATION)
#include
#endif
#endif
#define CPP2_TYPEOF(x) std::remove_cvref_t
#define CPP2_FORWARD(x) std::forward(x)
namespace cpp2 {
//-----------------------------------------------------------------------
//
// contract_group
//
//-----------------------------------------------------------------------
//
#ifdef CPP2_USE_SOURCE_LOCATION
#define CPP2_SOURCE_LOCATION_PARAM , std::source_location where
#define CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT , std::source_location where = std::source_location::current()
#define CPP2_SOURCE_LOCATION_PARAM_SOLO std::source_location where
#define CPP2_SOURCE_LOCATION_ARG , where
#else
#define CPP2_SOURCE_LOCATION_PARAM
#define CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT
#define CPP2_SOURCE_LOCATION_PARAM_SOLO
#define CPP2_SOURCE_LOCATION_ARG
#endif
// For C++23: make this std::string_view and drop the macro
// Before C++23 std::string_view was not guaranteed to be trivially copyable,
// and so in will pass it by const& and really it should be by value
#define CPP2_MESSAGE_PARAM char const*
class contract_group {
public:
using handler = void (*)(CPP2_MESSAGE_PARAM msg CPP2_SOURCE_LOCATION_PARAM);
constexpr contract_group (handler h = nullptr) : reporter(h) { }
constexpr auto set_handler(handler h) -> handler;
constexpr auto get_handler() const -> handler { return reporter; }
constexpr auto expects (bool b, CPP2_MESSAGE_PARAM msg = "" CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT)
-> void { if (!b) reporter(msg CPP2_SOURCE_LOCATION_ARG); }
private:
handler reporter;
};
[[noreturn]] auto report_and_terminate(std::string_view group, CPP2_MESSAGE_PARAM msg = "" CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) noexcept -> void {
std::cerr
#ifdef CPP2_USE_SOURCE_LOCATION
void { Default.expects(!init); new (&t) T{std::forward(args)...}; init = true; }
};
template
class out {
// Not going to bother with std::variant here
union {
T* t;
deferred_init* dt;
};
int uncaught_count = std::uncaught_exceptions();
bool has_t;
bool called_construct = false;
public:
out(T* t) noexcept : t{t}, has_t{true} { }
out(deferred_init* dt) noexcept : dt{dt}, has_t{false} { }
// In the case of an exception, if the parameter was uninitialized
// then leave it in the same state on exit (strong guarantee)
~out() {
if (called_construct && uncaught_count != std::uncaught_exceptions()) {
Default.expects(!has_t);
dt->value().~T();
}
}
auto construct (auto ...args) -> void {
if (has_t) {
*t = T(args...);
}
else if (dt->init) {
dt->value() = T(args...);
}
else {
dt->construct(args...);
called_construct = true;
}
}
auto construct_list(auto ...args) -> void {
if (has_t) {
*t = T{args...};
}
else if (dt->init) {
dt->value() = T{args...};
}
else {
dt->construct_list(args...);
called_construct = true;
}
}
};
//-----------------------------------------------------------------------
//
// CPP2_UFCS: Variadic macro generating a variadic lamba, oh my...
//
//-----------------------------------------------------------------------
//
#define CPP2_UFCS(FUNCNAME,PARAM1,...) \
[](auto&& obj, auto&& ...params) { \
if constexpr (requires{ std::forward(obj).FUNCNAME(std::forward(params)...); }) { \
return std::forward(obj).FUNCNAME(std::forward(params)...); \
} else { \
return FUNCNAME(std::forward(obj), std::forward(params)...); \
} \
}(PARAM1, __VA_ARGS__)
#define CPP2_UFCS_0(FUNCNAME,PARAM1) \
[](auto&& obj) { \
if constexpr (requires{ std::forward(obj).FUNCNAME(); }) { \
return std::forward(obj).FUNCNAME(); \
} else { \
return FUNCNAME(std::forward(obj)); \
} \
}(PARAM1)
//-----------------------------------------------------------------------
//
// is and as
//
//-----------------------------------------------------------------------
//
//-------------------------------------------------------------------------------------------------------------
// Built-in is (partial)
//
// For use when returning "no such thing", such as
// when customizing is/as for std::variant
static std::nullptr_t nonesuch = nullptr;
template< typename C, typename X >
auto is( X const& ) -> bool {
return false;
}
template< typename C, typename X >
requires std::is_same_v
auto is( X const& ) -> bool {
return true;
}
template< typename C, typename X >
requires (std::is_base_of_v && !std::is_same_v)
auto is( X const& ) -> bool {
return true;
}
template< typename C, typename X >
requires (std::is_base_of_v && !std::is_same_v)
auto is( X const& x ) -> bool {
return dynamic_cast(&x) != nullptr;
}
template< typename C, typename X >
requires (std::is_base_of_v && !std::is_same_v)
auto is( X const* x ) -> bool {
return dynamic_cast(x) != nullptr;
}
//-------------------------------------------------------------------------------------------------------------
// Built-in as (partial)
//
template< typename C >
auto as(...) -> auto {
return nonesuch;
}
template< typename C, typename X >
requires std::is_same_v
auto as( X const& x ) -> auto&& {
return x;
}
template< typename C, typename X >
auto as( X const& x ) -> auto
requires (!std::is_same_v && requires { C{x}; })
{
return C{x};
}
template< typename C, typename X >
requires std::is_base_of_v
auto as( X&& x ) -> C&& {
return std::forward(x);
}
template< typename C, typename X >
requires (std::is_base_of_v && !std::is_same_v)
auto as( X& x ) -> C& {
return dynamic_cast(x);
}
template< typename C, typename X >
requires (std::is_base_of_v && !std::is_same_v)
auto as( X const& x ) -> C const& {
return dynamic_cast(x);
}
template< typename C, typename X >
requires (std::is_base_of_v && !std::is_same_v)
auto as( X* x ) -> C* {
return dynamic_cast(x);
}
template< typename C, typename X >
requires (std::is_base_of_v && !std::is_same_v)
auto as( X const* x ) -> C const* {
return dynamic_cast(x);
}
//-------------------------------------------------------------------------------------------------------------
// std::variant is and as
//
template
constexpr auto operator_is( std::variant const& x ) {
return x.index();
}
template
constexpr auto operator_as( std::variant const& x ) -> auto&& {
if constexpr (I < std::variant_size_v) {
return std::get( x );
}
else {
return nonesuch;
}
}
template
auto is( std::variant const& x ) {
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as(x)), T >) if (x.index() == 0) return true;
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as(x)), T >) if (x.index() == 1) return true;
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as(x)), T >) if (x.index() == 2) return true;
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as(x)), T >) if (x.index() == 3) return true;
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as(x)), T >) if (x.index() == 4) return true;
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as(x)), T >) if (x.index() == 5) return true;
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as(x)), T >) if (x.index() == 6) return true;
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as(x)), T >) if (x.index() == 7) return true;
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as(x)), T >) if (x.index() == 8) return true;
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as(x)), T >) if (x.index() == 9) return true;
return false;
}
template
auto as( std::variant const& x ) {
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as(x)), T >) if (x.index() == 0) return operator_as(x);
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as(x)), T >) if (x.index() == 1) return operator_as(x);
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as(x)), T >) if (x.index() == 2) return operator_as(x);
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as(x)), T >) if (x.index() == 3) return operator_as(x);
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as(x)), T >) if (x.index() == 4) return operator_as(x);
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as(x)), T >) if (x.index() == 5) return operator_as(x);
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as(x)), T >) if (x.index() == 6) return operator_as(x);
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as(x)), T >) if (x.index() == 7) return operator_as(x);
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as(x)), T >) if (x.index() == 8) return operator_as(x);
if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as(x)), T >) if (x.index() == 9) return operator_as(x);
throw std::bad_variant_access();
}
//-------------------------------------------------------------------------------------------------------------
// std::any is and as
//
template
requires std::is_same_v
constexpr auto is( X const& x ) -> bool
{ return x.type() == typeid(T); }
template
requires (!std::is_reference_v && std::is_same_v)
constexpr auto as( X const& x ) -> T
{ return std::any_cast( x ); }
//-------------------------------------------------------------------------------------------------------------
// std::optional is and as
//
template
requires std::is_same_v
constexpr auto is( X const& x ) -> bool
{ return x.has_value(); }
template
requires std::is_same_v
constexpr auto as( X const& x ) -> auto&&
{ return x.value(); }
//-----------------------------------------------------------------------
//
// A variation of GSL's final_action_success and finally to run only on success
// (based on a PR I contributed to Microsoft GSL)
//
// final_action_success_success ensures something is run at the end of a scope
// if no exception is thrown
//
// finally_success is a convenience function to make a final_action_success_success
//
//-----------------------------------------------------------------------
//
template
class final_action_success
{
public:
explicit final_action_success(const F& ff) noexcept : f{ff} { }
explicit final_action_success(F&& ff) noexcept : f{std::move(ff)} { }
~final_action_success() noexcept
{
if (invoke && ecount == std::uncaught_exceptions()) {
f();
}
}
final_action_success(final_action_success&& other) noexcept
: f(std::move(other.f)), invoke(std::exchange(other.invoke, false))
{ }
final_action_success(const final_action_success&) = delete;
void operator=(const final_action_success&) = delete;
void operator=(final_action_success&&) = delete;
private:
F f;
int ecount = std::uncaught_exceptions();
bool invoke = true;
};
template
[[nodiscard]] auto finally_success(F&& f) noexcept
{
return final_action_success{std::forward(f)};
}
//-----------------------------------------------------------------------
//
// to_string for string interpolation
//
//-----------------------------------------------------------------------
//
template
auto to_string(T const& t) -> std::string
requires requires { std::to_string(t); }
{
return std::to_string(t);
}
auto to_string(std::string const& s) -> std::string const&
{
return s;
}
template
auto to_string(std::optional const& o) -> std::string {
if (o.has_value()) {
return std::to_string(o.value());
}
return "(empty)";
}
auto to_string(...) -> std::string {
return "(customize me - no cpp2::to_string overload exists for this type)";
}
}
using cpp2::cpp2_new;
//-----------------------------------------------------------------------
//
// A partial implementation of GSL features Cpp2 relies on,
// to keep this a standalone header without non-std dependencies
//
//-----------------------------------------------------------------------
//
namespace gsl {
//-----------------------------------------------------------------------
//
// An implementation of GSL's narrow_cast
//
//-----------------------------------------------------------------------
//
template
constexpr auto narrow_cast(From&& from) noexcept -> To
{
return static_cast(std::forward(from));
}
}
#endif