1 /**
2  * D header file for interaction with Microsoft C++ <xutility>
3  *
4  * Copyright: Copyright (c) 2018 D Language Foundation
5  * License: Distributed under the
6  *      $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
7  *    (See accompanying file LICENSE)
8  * Authors:   Manu Evans
9  * Source:    $(DRUNTIMESRC core/stdcpp/xutility.d)
10  */
11 
12 module core.stdcpp.xutility;
13 
14 @nogc:
15 
version(CppRuntime_Clang)16 version (CppRuntime_Clang)
17 {
18     import core.internal.traits : AliasSeq;
19     enum StdNamespace = AliasSeq!("std", "__1");
20 }
21 else
22 {
23     enum StdNamespace = "std";
24 }
25 
26 enum CppStdRevision : uint
27 {
28     cpp98 = 199711,
29     cpp11 = 201103,
30     cpp14 = 201402,
31     cpp17 = 201703
32 }
33 
34 enum __cplusplus = __traits(getTargetInfo, "cppStd");
35 
36 // wrangle C++ features
37 enum __cpp_sized_deallocation = __cplusplus >= CppStdRevision.cpp14 || is(typeof(_MSC_VER)) ? 201309 : 0;
38 enum __cpp_aligned_new = __cplusplus >= CppStdRevision.cpp17 ? 201606 : 0;
39 
40 
version(CppRuntime_Microsoft)41 version (CppRuntime_Microsoft)
42 {
43     import core.stdcpp.type_traits : is_empty;
44 
45     version (_MSC_VER_1200)
46         enum _MSC_VER = 1200;
47     else version (_MSC_VER_1300)
48         enum _MSC_VER = 1300;
49     else version (_MSC_VER_1310)
50         enum _MSC_VER = 1310;
51     else version (_MSC_VER_1400)
52         enum _MSC_VER = 1400;
53     else version (_MSC_VER_1500)
54         enum _MSC_VER = 1500;
55     else version (_MSC_VER_1600)
56         enum _MSC_VER = 1600;
57     else version (_MSC_VER_1700)
58         enum _MSC_VER = 1700;
59     else version (_MSC_VER_1800)
60         enum _MSC_VER = 1800;
61     else version (_MSC_VER_1900)
62         enum _MSC_VER = 1900;
63     else version (_MSC_VER_1910)
64         enum _MSC_VER = 1910;
65     else version (_MSC_VER_1911)
66         enum _MSC_VER = 1911;
67     else version (_MSC_VER_1912)
68         enum _MSC_VER = 1912;
69     else version (_MSC_VER_1913)
70         enum _MSC_VER = 1913;
71     else version (_MSC_VER_1914)
72         enum _MSC_VER = 1914;
73     else version (_MSC_VER_1915)
74         enum _MSC_VER = 1915;
75     else version (_MSC_VER_1916)
76         enum _MSC_VER = 1916;
77     else version (_MSC_VER_1920)
78         enum _MSC_VER = 1920;
79     else version (_MSC_VER_1921)
80         enum _MSC_VER = 1921;
81     else version (_MSC_VER_1922)
82         enum _MSC_VER = 1922;
83     else version (_MSC_VER_1923)
84         enum _MSC_VER = 1923;
85     else
86         enum _MSC_VER = 1923; // assume most recent compiler version
87 
88     // Client code can mixin the set of MSVC linker directives
89     mixin template MSVCLinkDirectives(bool failMismatch = false)
90     {
91         import core.stdcpp.xutility : __CXXLIB__, _ITERATOR_DEBUG_LEVEL;
92 
93         static if (__CXXLIB__ == "libcmtd")
94         {
95             pragma(lib, "libcpmtd");
96             static if (failMismatch)
97                 pragma(linkerDirective, "/FAILIFMISMATCH:RuntimeLibrary=MTd_StaticDebug");
98         }
99         else static if (__CXXLIB__ == "msvcrtd")
100         {
101             pragma(lib, "msvcprtd");
102             static if (failMismatch)
103                 pragma(linkerDirective, "/FAILIFMISMATCH:RuntimeLibrary=MDd_DynamicDebug");
104         }
105         else static if (__CXXLIB__ == "libcmt")
106         {
107             pragma(lib, "libcpmt");
108             static if (failMismatch)
109                 pragma(linkerDirective, "/FAILIFMISMATCH:RuntimeLibrary=MT_StaticRelease");
110         }
111         else static if (__CXXLIB__ == "msvcrt")
112         {
113             pragma(lib, "msvcprt");
114             static if (failMismatch)
115                 pragma(linkerDirective, "/FAILIFMISMATCH:RuntimeLibrary=MD_DynamicRelease");
116         }
117         static if (failMismatch)
118             pragma(linkerDirective, "/FAILIFMISMATCH:_ITERATOR_DEBUG_LEVEL=" ~ ('0' + _ITERATOR_DEBUG_LEVEL));
119     }
120 
121     // HACK: should we guess _DEBUG for `debug` builds?
122     version (NDEBUG) {}
123     else debug version = _DEBUG;
124 
125     // By specific user request
126     version (_ITERATOR_DEBUG_LEVEL_0)
127         enum _ITERATOR_DEBUG_LEVEL = 0;
128     else version (_ITERATOR_DEBUG_LEVEL_1)
129         enum _ITERATOR_DEBUG_LEVEL = 1;
130     else version (_ITERATOR_DEBUG_LEVEL_2)
131         enum _ITERATOR_DEBUG_LEVEL = 2;
132     else
133     {
134         // Match the C Runtime
135         static if (__CXXLIB__ == "libcmtd" || __CXXLIB__ == "msvcrtd")
136             enum _ITERATOR_DEBUG_LEVEL = 2;
137         else static if (__CXXLIB__ == "libcmt" || __CXXLIB__ == "msvcrt" ||
138                         __CXXLIB__ == "msvcrt100" || __CXXLIB__ == "msvcrt110" || __CXXLIB__ == "msvcrt120")
139             enum _ITERATOR_DEBUG_LEVEL = 0;
140         else
141         {
142             static if (__CXXLIB__.length > 0)
143                 pragma(msg, "Unrecognised C++ runtime library '" ~ __CXXLIB__ ~ "'");
144 
145             // No runtime specified; as a best-guess, -release will produce code that matches the MSVC release CRT
146             version (_DEBUG)
147                 enum _ITERATOR_DEBUG_LEVEL = 2;
148             else
149                 enum _ITERATOR_DEBUG_LEVEL = 0;
150         }
151     }
152 
153     // convenient alias for the C++ std library name
154     enum __CXXLIB__ = __traits(getTargetInfo, "cppRuntimeLibrary");
155 
156 extern(C++, "std"):
157 package:
158     enum _LOCK_DEBUG = 3;
159 
160     extern(C++, class) struct _Lockit
161     {
162         this(int) nothrow @nogc @safe;
163         ~this() nothrow @nogc @safe;
164 
165     private:
166         int _Locktype;
167     }
168     void dummyDtor() { assert(false); }
169     pragma(linkerDirective, "/ALTERNATENAME:" ~ _Lockit.__dtor.mangleof ~ "=" ~ dummyDtor.mangleof);
170 
171     struct _Container_base0
172     {
173     extern(D):
174         void _Orphan_all()() nothrow @nogc @safe {}
175         void _Swap_all()(ref _Container_base0) nothrow @nogc @safe {}
176         void _Swap_proxy_and_iterators()(ref _Container_base0) nothrow {}
177     }
178     struct _Iterator_base0
179     {
180     extern(D):
181         void _Adopt()(const(void)*) nothrow @nogc @safe {}
182         const(_Container_base0)* _Getcont()() const nothrow @nogc @safe { return null; }
183 
184         enum bool _Unwrap_when_unverified = true;
185     }
186 
187     struct _Container_proxy
188     {
189         const(_Container_base12)* _Mycont;
190         _Iterator_base12* _Myfirstiter;
191     }
192 
193     struct _Container_base12
194     {
195     extern(D):
196         inout(_Iterator_base12*)*_Getpfirst()() inout nothrow @nogc @safe
197         {
198             return _Myproxy == null ? null : &_Myproxy._Myfirstiter;
199         }
200         void _Orphan_all()() nothrow @nogc @safe
201         {
202             static if (_ITERATOR_DEBUG_LEVEL == 2)
203             {
204                 if (_Myproxy != null)
205                 {
206                     auto _Lock = _Lockit(_LOCK_DEBUG);
207                     for (_Iterator_base12 **_Pnext = &_Myproxy._Myfirstiter; *_Pnext != null; *_Pnext = (*_Pnext)._Mynextiter)
208                         (*_Pnext)._Myproxy = null;
209                     _Myproxy._Myfirstiter = null;
210                 }
211             }
212         }
213 //        void _Swap_all()(ref _Container_base12) nothrow @nogc;
214 
215         void _Swap_proxy_and_iterators()(ref _Container_base12 _Right) nothrow
216         {
217             static if (_ITERATOR_DEBUG_LEVEL == 2)
218                 auto _Lock = _Lockit(_LOCK_DEBUG);
219 
220             _Container_proxy* _Temp = _Myproxy;
221             _Myproxy = _Right._Myproxy;
222             _Right._Myproxy = _Temp;
223 
224             if (_Myproxy)
225                 _Myproxy._Mycont = &this;
226 
227             if (_Right._Myproxy)
228                 _Right._Myproxy._Mycont = &_Right;
229         }
230 
231         _Container_proxy* _Myproxy;
232     }
233 
234     struct _Iterator_base12
235     {
236     extern(D):
237         void _Adopt()(_Container_base12 *_Parent) nothrow @nogc @safe
238         {
239             if (_Parent == null)
240             {
241                 static if (_ITERATOR_DEBUG_LEVEL == 2)
242                 {
243                     auto _Lock = _Lockit(_LOCK_DEBUG);
244                     _Orphan_me();
245                 }
246             }
247             else
248             {
249                 _Container_proxy *_Parent_proxy = _Parent._Myproxy;
250 
251                 static if (_ITERATOR_DEBUG_LEVEL == 2)
252                 {
253                     if (_Myproxy != _Parent_proxy)
254                     {
255                         auto _Lock = _Lockit(_LOCK_DEBUG);
256                         _Orphan_me();
257                         _Mynextiter = _Parent_proxy._Myfirstiter;
258                         _Parent_proxy._Myfirstiter = &this;
259                         _Myproxy = _Parent_proxy;
260                     }
261                 }
262                 else
263                     _Myproxy = _Parent_proxy;
264             }
265         }
266         void _Clrcont()() nothrow @nogc @safe
267         {
268             _Myproxy = null;
269         }
270         const(_Container_base12)* _Getcont()() const nothrow @nogc @safe
271         {
272             return _Myproxy == null ? null : _Myproxy._Mycont;
273         }
274         inout(_Iterator_base12*)*_Getpnext()() inout nothrow @nogc @safe
275         {
276             return &_Mynextiter;
277         }
278         void _Orphan_me()() nothrow @nogc @safe
279         {
280             static if (_ITERATOR_DEBUG_LEVEL == 2)
281             {
282                 if (_Myproxy != null)
283                 {
284                     _Iterator_base12 **_Pnext = &_Myproxy._Myfirstiter;
285                     while (*_Pnext != null && *_Pnext != &this)
286                         _Pnext = &(*_Pnext)._Mynextiter;
287                     assert(*_Pnext, "ITERATOR LIST CORRUPTED!");
288                     *_Pnext = _Mynextiter;
289                     _Myproxy = null;
290                 }
291             }
292         }
293 
294         enum bool _Unwrap_when_unverified = _ITERATOR_DEBUG_LEVEL == 0;
295 
296         _Container_proxy *_Myproxy;
297         _Iterator_base12 *_Mynextiter;
298     }
299 
300     static if (_ITERATOR_DEBUG_LEVEL == 0)
301     {
302         alias _Container_base = _Container_base0;
303         alias _Iterator_base = _Iterator_base0;
304     }
305     else
306     {
307         alias _Container_base = _Container_base12;
308         alias _Iterator_base = _Iterator_base12;
309     }
310 
311     extern (C++, class) struct _Compressed_pair(_Ty1, _Ty2, bool Ty1Empty = is_empty!_Ty1.value)
312     {
313     pragma (inline, true):
314     extern(D):
315     pure nothrow @nogc:
316         enum _HasFirst = !Ty1Empty;
317 
318         ref inout(_Ty1) first() inout @safe { return _Myval1; }
319         ref inout(_Ty2) second() inout @safe { return _Myval2; }
320 
321         static if (!Ty1Empty)
322             _Ty1 _Myval1;
323         else
324         {
325             @property ref inout(_Ty1) _Myval1() inout @trusted { return *_GetBase(); }
326             private inout(_Ty1)* _GetBase() inout @trusted { return cast(inout(_Ty1)*)&this; }
327         }
328         _Ty2 _Myval2;
329     }
330 
331     // these are all [[noreturn]]
332     void _Xbad_alloc() nothrow;
333     void _Xinvalid_argument(const(char)* message) nothrow;
334     void _Xlength_error(const(char)* message) nothrow;
335     void _Xout_of_range(const(char)* message) nothrow;
336     void _Xoverflow_error(const(char)* message) nothrow;
337     void _Xruntime_error(const(char)* message) nothrow;
338 }
version(CppRuntime_Clang)339 else version (CppRuntime_Clang)
340 {
341     import core.stdcpp.type_traits : is_empty;
342 
343 extern(C++, "std"):
344 
345     extern (C++, class) struct __compressed_pair(_T1, _T2)
346     {
347     pragma (inline, true):
348     extern(D):
349         enum Ty1Empty = is_empty!_T1.value;
350         enum Ty2Empty = is_empty!_T2.value;
351 
352         ref inout(_T1) first() inout nothrow @safe @nogc { return __value1_; }
353         ref inout(_T2) second() inout nothrow @safe @nogc { return __value2_; }
354 
355     private:
356         private inout(_T1)* __get_base1() inout { return cast(inout(_T1)*)&this; }
357         private inout(_T2)* __get_base2() inout { return cast(inout(_T2)*)&__get_base1()[Ty1Empty ? 0 : 1]; }
358 
359         static if (!Ty1Empty)
360             _T1 __value1_;
361         else
362             @property ref inout(_T1) __value1_() inout nothrow @trusted @nogc { return *__get_base1(); }
363         static if (!Ty2Empty)
364             _T2 __value2_;
365         else
366             @property ref inout(_T2) __value2_() inout nothrow @trusted @nogc { return *__get_base2(); }
367     }
368 }
version(CppRuntime_Gcc)369 version (CppRuntime_Gcc)
370 {
371     import core.atomic;
372 
373     alias _Atomic_word = int;
374 
375     void __atomic_add_dispatch()(_Atomic_word* __mem, int __val) nothrow @nogc @safe
376     {
377         version (__GTHREADS)
378         {
379             // TODO: check __gthread_active_p()
380 //            if (__gthread_active_p())
381                 __atomic_add(__mem, __val);
382 //            }
383 //            else
384 //            __atomic_add_single(__mem, __val);
385         }
386         else
387             __atomic_add_single(__mem, __val);
388     }
389 
390     void __atomic_add()(_Atomic_word* __mem, int __val) nothrow @nogc @safe
391     {
392         atomicFetchAdd!(MemoryOrder.acq_rel)(*__mem, __val);
393     }
394 
395     void __atomic_add_single()(_Atomic_word* __mem, int __val) nothrow @nogc @safe
396     {
397         *__mem += __val;
398     }
399 
400     _Atomic_word __exchange_and_add_dispatch()(_Atomic_word* __mem, int __val) nothrow @nogc @safe
401     {
402         version (__GTHREADS)
403         {
404             // TODO: check __gthread_active_p()
405             return __exchange_and_add(__mem, __val);
406 
407 //            if (__gthread_active_p())
408 //                return __exchange_and_add(__mem, __val);
409 //            else
410 //                return __exchange_and_add_single(__mem, __val);
411         }
412         else
413             return __exchange_and_add_single(__mem, __val);
414     }
415 
416     _Atomic_word __exchange_and_add()(_Atomic_word* __mem, int __val) nothrow @nogc @safe
417     {
418         return atomicFetchAdd!(MemoryOrder.acq_rel)(*__mem, __val);
419     }
420 
421     _Atomic_word __exchange_and_add_single()(_Atomic_word* __mem, int __val) nothrow @nogc @safe
422     {
423         _Atomic_word __result = *__mem;
424         *__mem += __val;
425         return __result;
426     }
427 }
428