// 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
// TODO: technically this test should have included first, but GEFN
#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
#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
#ifdef __cpp_lib_memory_resource
#include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if __has_include()
#include
#endif
#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
// libstdc++ sometime after GCC 10 got a dependency on linking TBB if is
// included, so let's not pull in that header in our "import std;" simulation mode
#if not defined(__GLIBCXX__) || __GLIBCXX__ < 20210101
#include
#endif
#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
#ifdef __cpp_lib_jthread
#include
#endif
#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
#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]] inline 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
std::string {
return "(customize me - no cpp2::to_string overload exists for this type)";
}
template
inline auto to_string(T const& t) -> std::string
requires requires { std::to_string(t); }
{
return std::to_string(t);
}
inline auto to_string(std::string const& s) -> std::string const&
{
return s;
}
template
inline auto to_string(T const& sv) -> std::string
requires (std::is_convertible_v
&& !std::is_convertible_v)
{
return std::string{sv};
}
template
inline auto to_string(std::variant const& v) -> std::string;
template < typename T, typename U>
inline auto to_string(std::pair const& p) -> std::string;
template < typename... Ts>
inline auto to_string(std::tuple const& t) -> std::string;
template
inline auto to_string(std::optional const& o) -> std::string {
if (o.has_value()) {
return cpp2::to_string(o.value());
}
return "(empty)";
}
template
inline auto to_string(std::variant const& v) -> std::string
{
if (v.valueless_by_exception()) return "(empty)";
// Need to guard this with is_any otherwise the get_if is illegal
if constexpr (is_any) if (std::get_if(&v) != nullptr) return "(empty)";
return std::visit([](auto&& arg) -> std::string {
return cpp2::to_string(arg);
}, v);
}
template < typename T, typename U>
inline auto to_string(std::pair const& p) -> std::string
{
return "(" + cpp2::to_string(p.first) + ", " + cpp2::to_string(p.second) + ")";
}
template < typename... Ts>
inline auto to_string(std::tuple const& t) -> std::string
{
if constexpr (sizeof...(Ts) == 0) {
return "()";
} else {
std::string out = "(" + cpp2::to_string(std::get(t));
std::apply([&out](auto&&, auto&&... args) {
((out += ", " + cpp2::to_string(args)), ...);
}, t);
out += ")";
return out;
}
}
//-----------------------------------------------------------------------
//
// Speculative: RAII wrapping for the C standard library
//
// As part of embracing compatibility while also reducing what we have to
// teach and learn about C++ (which includes the C standard library), I
// was curious to see if we can improve use of the C standard library
// from Cpp2 code... UFCS is a part of that, and then RAII destructors is
// another that goes hand in hand with that, hence this section...
// but see caveat note at the end.
//
//-----------------------------------------------------------------------
//
template
class c_raii {
T t;
void (*dtor)(void*);
public:
template
c_raii( T t_, D d )
: t{ t_ }
, dtor{ [](void* x) { (void)(D)(x); } }
{ }
~c_raii() { if (dtor) dtor(t); }
operator T&() { return t; }
c_raii(c_raii const&) = delete;
auto operator=(c_raii const&) = delete;
c_raii(c_raii&& that) : t {std::move(that.t)}, dtor {that.dtor} { that.dtor = nullptr; }
auto operator=(c_raii&& that) { t = std::move(that.t); dtor = that.dtor; that.dtor = nullptr; }
};
inline auto fopen( const char* filename, const char* mode ) {
auto x = std::fopen(filename, mode);
if (!x) {
throw std::make_error_condition(std::errc::no_such_file_or_directory);
}
return c_raii( x, &fclose );
}
// Caveat: There's little else in the C stdlib that allocates a resource...
//
// malloc is already wrapped like this via std::unique_ptr, which
// typically uses malloc or gets memory from the same pool
// thrd_create std::jthread is better
//
// ... is that it? I don't think it's useful to provide a c_raii just for fopen,
// but perhaps c_raii may be useful for bringing forward third-party C code too,
// with cpp2::fopen as a starting example.
}
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