| /** | |
| * D header file for interaction with Microsoft C++ <xutility> | |
| * | |
| * Copyright: Copyright (c) 2018 D Language Foundation | |
| * License: Distributed under the | |
| * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). | |
| * (See accompanying file LICENSE) | |
| * Authors: Manu Evans | |
| * Source: $(DRUNTIMESRC core/stdcpp/xutility.d) | |
| */ | |
| module core.stdcpp.xutility; | |
| @nogc: | |
| version (CppRuntime_LLVM) | |
| { | |
| import core.internal.traits : AliasSeq; | |
| enum StdNamespace = AliasSeq!("std", "__1"); | |
| } | |
| else | |
| { | |
| enum StdNamespace = "std"; | |
| } | |
| /** | |
| * Possible values of the `__cplusplus` macro provided by C++ compilers | |
| * | |
| * For foolproofness, use ordering comparison, e.g. `__cplusplus >= CppStdRevision.cpp17`. | |
| */ | |
| enum CppStdRevision : uint | |
| { | |
| cpp98 = 199711, | |
| cpp11 = 201103, | |
| cpp14 = 201402, | |
| cpp17 = 201703, | |
| cpp20 = 202002, | |
| cpp23 = 202302, | |
| } | |
| /** | |
| * Returns the target C++ version, encoded as C++ compilers do | |
| * | |
| * C++ compilers provide a `__cplusplus` macro which returns an integer | |
| * representing the targetted standard. This manifest provides the same | |
| * interface, retrieved from the compiler via a `__traits`. | |
| */ | |
| enum __cplusplus = __traits(getTargetInfo, "cppStd"); | |
| // wrangle C++ features | |
| enum __cpp_sized_deallocation = __cplusplus >= CppStdRevision.cpp14 || is(typeof(_MSC_VER)) ? 201309 : 0; | |
| enum __cpp_aligned_new = __cplusplus >= CppStdRevision.cpp17 ? 201606 : 0; | |
| version (CppRuntime_Microsoft) | |
| { | |
| import core.stdcpp.type_traits : is_empty; | |
| version (_MSC_VER_1200) | |
| enum _MSC_VER = 1200; | |
| else version (_MSC_VER_1300) | |
| enum _MSC_VER = 1300; | |
| else version (_MSC_VER_1310) | |
| enum _MSC_VER = 1310; | |
| else version (_MSC_VER_1400) | |
| enum _MSC_VER = 1400; | |
| else version (_MSC_VER_1500) | |
| enum _MSC_VER = 1500; | |
| else version (_MSC_VER_1600) | |
| enum _MSC_VER = 1600; | |
| else version (_MSC_VER_1700) | |
| enum _MSC_VER = 1700; | |
| else version (_MSC_VER_1800) | |
| enum _MSC_VER = 1800; | |
| else version (_MSC_VER_1900) | |
| enum _MSC_VER = 1900; | |
| else version (_MSC_VER_1910) | |
| enum _MSC_VER = 1910; | |
| else version (_MSC_VER_1911) | |
| enum _MSC_VER = 1911; | |
| else version (_MSC_VER_1912) | |
| enum _MSC_VER = 1912; | |
| else version (_MSC_VER_1913) | |
| enum _MSC_VER = 1913; | |
| else version (_MSC_VER_1914) | |
| enum _MSC_VER = 1914; | |
| else version (_MSC_VER_1915) | |
| enum _MSC_VER = 1915; | |
| else version (_MSC_VER_1916) | |
| enum _MSC_VER = 1916; | |
| else version (_MSC_VER_1920) | |
| enum _MSC_VER = 1920; | |
| else version (_MSC_VER_1921) | |
| enum _MSC_VER = 1921; | |
| else version (_MSC_VER_1922) | |
| enum _MSC_VER = 1922; | |
| else version (_MSC_VER_1923) | |
| enum _MSC_VER = 1923; | |
| else | |
| enum _MSC_VER = 1923; // assume most recent compiler version | |
| // Client code can mixin the set of MSVC linker directives | |
| mixin template MSVCLinkDirectives(bool failMismatch = false) | |
| { | |
| import core.stdcpp.xutility : __CXXLIB__, _ITERATOR_DEBUG_LEVEL; | |
| static if (__CXXLIB__ == "libcmtd") | |
| { | |
| pragma(lib, "libcpmtd"); | |
| static if (failMismatch) | |
| pragma(linkerDirective, "/FAILIFMISMATCH:RuntimeLibrary=MTd_StaticDebug"); | |
| } | |
| else static if (__CXXLIB__ == "msvcrtd") | |
| { | |
| pragma(lib, "msvcprtd"); | |
| static if (failMismatch) | |
| pragma(linkerDirective, "/FAILIFMISMATCH:RuntimeLibrary=MDd_DynamicDebug"); | |
| } | |
| else static if (__CXXLIB__ == "libcmt") | |
| { | |
| pragma(lib, "libcpmt"); | |
| static if (failMismatch) | |
| pragma(linkerDirective, "/FAILIFMISMATCH:RuntimeLibrary=MT_StaticRelease"); | |
| } | |
| else static if (__CXXLIB__ == "msvcrt") | |
| { | |
| pragma(lib, "msvcprt"); | |
| static if (failMismatch) | |
| pragma(linkerDirective, "/FAILIFMISMATCH:RuntimeLibrary=MD_DynamicRelease"); | |
| } | |
| static if (failMismatch) | |
| pragma(linkerDirective, "/FAILIFMISMATCH:_ITERATOR_DEBUG_LEVEL=" ~ ('0' + _ITERATOR_DEBUG_LEVEL)); | |
| } | |
| // HACK: should we guess _DEBUG for `debug` builds? | |
| version (NDEBUG) {} | |
| else debug version = _DEBUG; | |
| // By specific user request | |
| version (_ITERATOR_DEBUG_LEVEL_0) | |
| enum _ITERATOR_DEBUG_LEVEL = 0; | |
| else version (_ITERATOR_DEBUG_LEVEL_1) | |
| enum _ITERATOR_DEBUG_LEVEL = 1; | |
| else version (_ITERATOR_DEBUG_LEVEL_2) | |
| enum _ITERATOR_DEBUG_LEVEL = 2; | |
| else | |
| { | |
| // Match the C Runtime | |
| static if (__CXXLIB__ == "libcmtd" || __CXXLIB__ == "msvcrtd") | |
| enum _ITERATOR_DEBUG_LEVEL = 2; | |
| else static if (__CXXLIB__ == "libcmt" || __CXXLIB__ == "msvcrt" || | |
| __CXXLIB__ == "msvcrt100" || __CXXLIB__ == "msvcrt110" || __CXXLIB__ == "msvcrt120") | |
| enum _ITERATOR_DEBUG_LEVEL = 0; | |
| else | |
| { | |
| static if (__CXXLIB__.length > 0) | |
| pragma(msg, "Unrecognised C++ runtime library '" ~ __CXXLIB__ ~ "'"); | |
| // No runtime specified; as a best-guess, -release will produce code that matches the MSVC release CRT | |
| version (_DEBUG) | |
| enum _ITERATOR_DEBUG_LEVEL = 2; | |
| else | |
| enum _ITERATOR_DEBUG_LEVEL = 0; | |
| } | |
| } | |
| // convenient alias for the C++ std library name | |
| enum __CXXLIB__ = __traits(getTargetInfo, "cppRuntimeLibrary"); | |
| extern(C++, "std"): | |
| package: | |
| enum _LOCK_DEBUG = 3; | |
| extern(C++, class) struct _Lockit | |
| { | |
| this(int) nothrow @nogc @safe; | |
| ~this() nothrow @nogc @safe; | |
| private: | |
| int _Locktype; | |
| } | |
| void dummyDtor() { assert(false); } | |
| pragma(linkerDirective, "/ALTERNATENAME:" ~ _Lockit.__dtor.mangleof ~ "=" ~ dummyDtor.mangleof); | |
| struct _Container_base0 | |
| { | |
| extern(D): | |
| void _Orphan_all()() nothrow @nogc @safe {} | |
| void _Swap_all()(ref _Container_base0) nothrow @nogc @safe {} | |
| void _Swap_proxy_and_iterators()(ref _Container_base0) nothrow {} | |
| } | |
| struct _Iterator_base0 | |
| { | |
| extern(D): | |
| void _Adopt()(const(void)*) nothrow @nogc @safe {} | |
| const(_Container_base0)* _Getcont()() const nothrow @nogc @safe { return null; } | |
| enum bool _Unwrap_when_unverified = true; | |
| } | |
| struct _Container_proxy | |
| { | |
| const(_Container_base12)* _Mycont; | |
| _Iterator_base12* _Myfirstiter; | |
| } | |
| struct _Container_base12 | |
| { | |
| extern(D): | |
| inout(_Iterator_base12*)*_Getpfirst()() inout nothrow @nogc @safe | |
| { | |
| return _Myproxy == null ? null : &_Myproxy._Myfirstiter; | |
| } | |
| void _Orphan_all()() nothrow @nogc @safe | |
| { | |
| static if (_ITERATOR_DEBUG_LEVEL == 2) | |
| { | |
| if (_Myproxy != null) | |
| { | |
| auto _Lock = _Lockit(_LOCK_DEBUG); | |
| for (_Iterator_base12 **_Pnext = &_Myproxy._Myfirstiter; *_Pnext != null; *_Pnext = (*_Pnext)._Mynextiter) | |
| (*_Pnext)._Myproxy = null; | |
| _Myproxy._Myfirstiter = null; | |
| } | |
| } | |
| } | |
| // void _Swap_all()(ref _Container_base12) nothrow @nogc; | |
| void _Swap_proxy_and_iterators()(ref _Container_base12 _Right) nothrow | |
| { | |
| static if (_ITERATOR_DEBUG_LEVEL == 2) | |
| auto _Lock = _Lockit(_LOCK_DEBUG); | |
| _Container_proxy* _Temp = _Myproxy; | |
| _Myproxy = _Right._Myproxy; | |
| _Right._Myproxy = _Temp; | |
| if (_Myproxy) | |
| _Myproxy._Mycont = &this; | |
| if (_Right._Myproxy) | |
| _Right._Myproxy._Mycont = &_Right; | |
| } | |
| _Container_proxy* _Myproxy; | |
| } | |
| struct _Iterator_base12 | |
| { | |
| extern(D): | |
| void _Adopt()(_Container_base12 *_Parent) nothrow @nogc @safe | |
| { | |
| if (_Parent == null) | |
| { | |
| static if (_ITERATOR_DEBUG_LEVEL == 2) | |
| { | |
| auto _Lock = _Lockit(_LOCK_DEBUG); | |
| _Orphan_me(); | |
| } | |
| } | |
| else | |
| { | |
| _Container_proxy *_Parent_proxy = _Parent._Myproxy; | |
| static if (_ITERATOR_DEBUG_LEVEL == 2) | |
| { | |
| if (_Myproxy != _Parent_proxy) | |
| { | |
| auto _Lock = _Lockit(_LOCK_DEBUG); | |
| _Orphan_me(); | |
| _Mynextiter = _Parent_proxy._Myfirstiter; | |
| _Parent_proxy._Myfirstiter = &this; | |
| _Myproxy = _Parent_proxy; | |
| } | |
| } | |
| else | |
| _Myproxy = _Parent_proxy; | |
| } | |
| } | |
| void _Clrcont()() nothrow @nogc @safe | |
| { | |
| _Myproxy = null; | |
| } | |
| const(_Container_base12)* _Getcont()() const nothrow @nogc @safe | |
| { | |
| return _Myproxy == null ? null : _Myproxy._Mycont; | |
| } | |
| inout(_Iterator_base12*)*_Getpnext()() inout nothrow @nogc @safe | |
| { | |
| return &_Mynextiter; | |
| } | |
| void _Orphan_me()() nothrow @nogc @safe | |
| { | |
| static if (_ITERATOR_DEBUG_LEVEL == 2) | |
| { | |
| if (_Myproxy != null) | |
| { | |
| _Iterator_base12 **_Pnext = &_Myproxy._Myfirstiter; | |
| while (*_Pnext != null && *_Pnext != &this) | |
| _Pnext = &(*_Pnext)._Mynextiter; | |
| assert(*_Pnext, "ITERATOR LIST CORRUPTED!"); | |
| *_Pnext = _Mynextiter; | |
| _Myproxy = null; | |
| } | |
| } | |
| } | |
| enum bool _Unwrap_when_unverified = _ITERATOR_DEBUG_LEVEL == 0; | |
| _Container_proxy *_Myproxy; | |
| _Iterator_base12 *_Mynextiter; | |
| } | |
| static if (_ITERATOR_DEBUG_LEVEL == 0) | |
| { | |
| alias _Container_base = _Container_base0; | |
| alias _Iterator_base = _Iterator_base0; | |
| } | |
| else | |
| { | |
| alias _Container_base = _Container_base12; | |
| alias _Iterator_base = _Iterator_base12; | |
| } | |
| extern (C++, class) struct _Compressed_pair(_Ty1, _Ty2, bool Ty1Empty = is_empty!_Ty1.value) | |
| { | |
| pragma (inline, true): | |
| extern(D): | |
| pure nothrow @nogc: | |
| enum _HasFirst = !Ty1Empty; | |
| ref inout(_Ty1) first() inout @safe { return _Myval1; } | |
| ref inout(_Ty2) second() inout @safe { return _Myval2; } | |
| static if (!Ty1Empty) | |
| _Ty1 _Myval1; | |
| else | |
| { | |
| @property ref inout(_Ty1) _Myval1() inout @trusted { return *_GetBase(); } | |
| private inout(_Ty1)* _GetBase() inout @trusted { return cast(inout(_Ty1)*)&this; } | |
| } | |
| _Ty2 _Myval2; | |
| } | |
| // these are all [[noreturn]] | |
| void _Xbad_alloc() nothrow; | |
| void _Xinvalid_argument(const(char)* message) nothrow; | |
| void _Xlength_error(const(char)* message) nothrow; | |
| void _Xout_of_range(const(char)* message) nothrow; | |
| void _Xoverflow_error(const(char)* message) nothrow; | |
| void _Xruntime_error(const(char)* message) nothrow; | |
| } | |
| else version (CppRuntime_LLVM) | |
| { | |
| import core.stdcpp.type_traits : is_empty; | |
| extern(C++, "std"): | |
| extern (C++, class) struct __compressed_pair(_T1, _T2) | |
| { | |
| pragma (inline, true): | |
| extern(D): | |
| enum Ty1Empty = is_empty!_T1.value; | |
| enum Ty2Empty = is_empty!_T2.value; | |
| ref inout(_T1) first() inout nothrow @safe @nogc { return __value1_; } | |
| ref inout(_T2) second() inout nothrow @safe @nogc { return __value2_; } | |
| private: | |
| private inout(_T1)* __get_base1() inout { return cast(inout(_T1)*)&this; } | |
| private inout(_T2)* __get_base2() inout { return cast(inout(_T2)*)&__get_base1()[Ty1Empty ? 0 : 1]; } | |
| static if (!Ty1Empty) | |
| _T1 __value1_; | |
| else | |
| @property ref inout(_T1) __value1_() inout nothrow @trusted @nogc { return *__get_base1(); } | |
| static if (!Ty2Empty) | |
| _T2 __value2_; | |
| else | |
| @property ref inout(_T2) __value2_() inout nothrow @trusted @nogc { return *__get_base2(); } | |
| } | |
| } | |
| version (CppRuntime_GNU) | |
| { | |
| import core.atomic; | |
| alias _Atomic_word = int; | |
| void __atomic_add_dispatch()(_Atomic_word* __mem, int __val) nothrow @nogc @safe | |
| { | |
| version (__GTHREADS) | |
| { | |
| // TODO: check __gthread_active_p() | |
| // if (__gthread_active_p()) | |
| __atomic_add(__mem, __val); | |
| // } | |
| // else | |
| // __atomic_add_single(__mem, __val); | |
| } | |
| else | |
| __atomic_add_single(__mem, __val); | |
| } | |
| void __atomic_add()(_Atomic_word* __mem, int __val) nothrow @nogc @safe | |
| { | |
| atomicFetchAdd!(MemoryOrder.acq_rel)(*__mem, __val); | |
| } | |
| void __atomic_add_single()(_Atomic_word* __mem, int __val) nothrow @nogc @safe | |
| { | |
| *__mem += __val; | |
| } | |
| _Atomic_word __exchange_and_add_dispatch()(_Atomic_word* __mem, int __val) nothrow @nogc @safe | |
| { | |
| version (__GTHREADS) | |
| { | |
| // TODO: check __gthread_active_p() | |
| return __exchange_and_add(__mem, __val); | |
| // if (__gthread_active_p()) | |
| // return __exchange_and_add(__mem, __val); | |
| // else | |
| // return __exchange_and_add_single(__mem, __val); | |
| } | |
| else | |
| return __exchange_and_add_single(__mem, __val); | |
| } | |
| _Atomic_word __exchange_and_add()(_Atomic_word* __mem, int __val) nothrow @nogc @safe | |
| { | |
| return atomicFetchAdd!(MemoryOrder.acq_rel)(*__mem, __val); | |
| } | |
| _Atomic_word __exchange_and_add_single()(_Atomic_word* __mem, int __val) nothrow @nogc @safe | |
| { | |
| _Atomic_word __result = *__mem; | |
| *__mem += __val; | |
| return __result; | |
| } | |
| } |