diff --git a/.editorconfig b/.editorconfig index f1e0252cbc..f7a0475382 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,6 +1,6 @@ root = true -[*.{c,h,d,di,dd}] +[*.{c,cpp,h,d,di,dd}] insert_final_newline = true indent_style = space indent_size = 4 diff --git a/changelog/array.dd b/changelog/array.dd new file mode 100644 index 0000000000..5d367d4596 --- /dev/null +++ b/changelog/array.dd @@ -0,0 +1,3 @@ +Added `core.stdcpp.array`. + +Added `core.stdcpp.array`, which links against C++ `std::array` diff --git a/mak/COPY b/mak/COPY index 2c3c20f3b3..b2c5d9dffd 100644 --- a/mak/COPY +++ b/mak/COPY @@ -48,8 +48,10 @@ COPY=\ $(IMPDIR)\core\stdc\wchar_.d \ $(IMPDIR)\core\stdc\wctype.d \ \ - $(IMPDIR)\core\stdcpp\typeinfo.d \ + $(IMPDIR)\core\stdcpp\array.d \ $(IMPDIR)\core\stdcpp\exception.d \ + $(IMPDIR)\core\stdcpp\typeinfo.d \ + $(IMPDIR)\core\stdcpp\xutility.d \ \ $(IMPDIR)\core\sys\darwin\execinfo.d \ $(IMPDIR)\core\sys\darwin\pthread.d \ diff --git a/mak/DOCS b/mak/DOCS index c825362b20..5904029c10 100644 --- a/mak/DOCS +++ b/mak/DOCS @@ -38,6 +38,7 @@ DOCS=\ $(DOCDIR)\core_stdc_wchar_.html \ $(DOCDIR)\core_stdc_wctype.html \ \ + $(DOCDIR)\core_stdcpp_array.html \ $(DOCDIR)\core_stdcpp_exception.html \ $(DOCDIR)\core_stdcpp_typeinfo.html \ \ diff --git a/mak/SRCS b/mak/SRCS index 7d5db0c2d7..62a2226059 100644 --- a/mak/SRCS +++ b/mak/SRCS @@ -46,6 +46,9 @@ SRCS=\ src\core\stdc\time.d \ src\core\stdc\wchar_.d \ \ + src\core\stdcpp\array.d \ + src\core\stdcpp\xutility.d \ + \ src\core\sync\barrier.d \ src\core\sync\condition.d \ src\core\sync\config.d \ diff --git a/mak/WINDOWS b/mak/WINDOWS index ef24e80916..7766625190 100644 --- a/mak/WINDOWS +++ b/mak/WINDOWS @@ -187,9 +187,15 @@ $(IMPDIR)\core\stdc\wctype.d : src\core\stdc\wctype.d $(IMPDIR)\core\stdcpp\exception.d : src\core\stdcpp\exception.d copy $** $@ +$(IMPDIR)\core\stdcpp\array.d : src\core\stdcpp\array.d + copy $** $@ + $(IMPDIR)\core\stdcpp\typeinfo.d : src\core\stdcpp\typeinfo.d copy $** $@ +$(IMPDIR)\core\stdcpp\xutility.d : src\core\stdcpp\xutility.d + copy $** $@ + $(IMPDIR)\core\sys\darwin\execinfo.d : src\core\sys\darwin\execinfo.d copy $** $@ diff --git a/posix.mak b/posix.mak index 4b5f3eb7cb..c2d100d63b 100644 --- a/posix.mak +++ b/posix.mak @@ -244,7 +244,7 @@ HAS_ADDITIONAL_TESTS:=$(shell test -d test && echo 1) ifeq ($(HAS_ADDITIONAL_TESTS),1) ADDITIONAL_TESTS:=test/init_fini test/exceptions test/coverage test/profile test/cycles test/allocations test/typeinfo \ test/aa test/hash \ - test/thread test/unittest test/imports test/betterc + test/thread test/unittest test/imports test/betterc test/stdcpp ADDITIONAL_TESTS+=$(if $(SHARED),test/shared,) endif diff --git a/src/core/stdcpp/array.d b/src/core/stdcpp/array.d new file mode 100644 index 0000000000..a8955c81f6 --- /dev/null +++ b/src/core/stdcpp/array.d @@ -0,0 +1,145 @@ +/** + * D header file for interaction with C++ std::array. + * + * 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/array.d) + */ + +module core.stdcpp.array; + +// hacks to support DMD on Win32 +version (CppRuntime_Microsoft) +{ + version = CppRuntime_Windows; // use the MS runtime ABI for win32 +} +else version (CppRuntime_DigitalMars) +{ + version = CppRuntime_Windows; // use the MS runtime ABI for win32 + pragma(msg, "std::array not supported by DMC"); +} +version (CppRuntime_Clang) +{ + private alias AliasSeq(Args...) = Args; + private enum StdNamespace = AliasSeq!("std", "__1"); +} +else +{ + private enum StdNamespace = "std"; +} + +extern(C++, (StdNamespace)): + +/** + * D language counterpart to C++ std::array. + * + * C++ reference: $(LINK2 https://en.cppreference.com/w/cpp/container/array) + */ +extern(C++, class) struct array(T, size_t N) +{ +extern(D): +pragma(inline, true): + + /// + alias size_type = size_t; + /// + alias difference_type = ptrdiff_t; + /// + alias value_type = T; + /// + alias pointer = T*; + /// + alias const_pointer = const(T)*; + + /// + alias as_array this; + + /// Variadic constructor + this(T[N] args ...) { this[] = args[]; } + + /// + size_type size() const nothrow @safe @nogc { return N; } + /// + alias length = size; + /// + alias opDollar = length; + /// + size_type max_size() const nothrow @safe @nogc { return N; } + /// + bool empty() const nothrow @safe @nogc { return N == 0; } + + /// + ref inout(T) front() inout nothrow @safe @nogc { static if (N > 0) { return this[0]; } else { return as_array()[][0]; /* HACK: force OOB */ } } + /// + ref inout(T) back() inout nothrow @safe @nogc { static if (N > 0) { return this[N-1]; } else { return as_array()[][0]; /* HACK: force OOB */ } } + + /// + void fill()(auto ref const(T) value) { this[] = value; } + + version (CppRuntime_Windows) + { + /// + inout(T)* data() inout nothrow @safe @nogc { return &_Elems[0]; } + /// + ref inout(T)[N] as_array() const inout @safe @nogc { return _Elems[0 .. N]; } + /// + ref inout(T) at(size_type i) inout nothrow @safe @nogc { return _Elems[0 .. N][i]; } + + version (CppRuntime_Microsoft) + { + import core.stdcpp.xutility : MSVCLinkDirectives; + mixin MSVCLinkDirectives!false; + } + + private: + T[N ? N : 1] _Elems; + } + else version (CppRuntime_Gcc) + { + /// + inout(T)* data() inout nothrow @safe @nogc { static if (N > 0) { return &_M_elems[0]; } else { return null; } } + /// + ref inout(T)[N] as_array() inout nothrow @trusted @nogc { return data()[0 .. N]; } + /// + ref inout(T) at(size_type i) inout nothrow @trusted @nogc { return data()[0 .. N][i]; } + + private: + static if (N > 0) + { + T[N] _M_elems; + } + else + { + struct _Placeholder {} + _Placeholder _M_placeholder; + } + } + else version (CppRuntime_Clang) + { + /// + inout(T)* data() inout nothrow @trusted @nogc { static if (N > 0) { return &__elems_[0]; } else { return cast(inout(T)*)__elems_.ptr; } } + /// + ref inout(T)[N] as_array() inout nothrow @trusted @nogc { return data()[0 .. N]; } + /// + ref inout(T) at(size_type i) inout nothrow @trusted @nogc { return data()[0 .. N][i]; } + + private: + static if (N > 0) + { + T[N] __elems_; + } + else + { + struct _ArrayInStructT { T[1] __data_; } + align(_ArrayInStructT.alignof) + byte[_ArrayInStructT.sizeof] __elems_ = void; + } + } + else + { + static assert(false, "C++ runtime not supported"); + } +} diff --git a/src/core/stdcpp/xutility.d b/src/core/stdcpp/xutility.d new file mode 100644 index 0000000000..a864e78e08 --- /dev/null +++ b/src/core/stdcpp/xutility.d @@ -0,0 +1,60 @@ +/** + * D header file for interaction with Microsoft C++ + * + * 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; + +extern(C++, "std"): + +version (CppRuntime_Microsoft) +{ + // Client code can mixin the set of MSVC linker directives + mixin template MSVCLinkDirectives(bool failMismatch = false) + { + import core.stdcpp.xutility : __CXXLIB__; + + 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"); + } + } + + // convenient alias for the C++ std library name + enum __CXXLIB__ = __traits(getTargetInfo, "cppRuntimeLibrary"); + +package: + // these are all [[noreturn]] + void _Xbad() nothrow @trusted @nogc; + void _Xinvalid_argument(const(char)* message) nothrow @trusted @nogc; + void _Xlength_error(const(char)* message) nothrow @trusted @nogc; + void _Xout_of_range(const(char)* message) nothrow @trusted @nogc; + void _Xoverflow_error(const(char)* message) nothrow @trusted @nogc; + void _Xruntime_error(const(char)* message) nothrow @trusted @nogc; +} diff --git a/test/common.mak b/test/common.mak index dc9680c5b8..fbd72aea5f 100644 --- a/test/common.mak +++ b/test/common.mak @@ -28,3 +28,7 @@ else DFLAGS += -O -release CFLAGS += -O3 endif +CXXFLAGS:=$(CFLAGS) -std=c++11 +ifeq (osx,$(OS)) + CXXFLAGS+=-stdlib=libc++ +endif diff --git a/test/stdcpp/Makefile b/test/stdcpp/Makefile new file mode 100644 index 0000000000..e6d665e55a --- /dev/null +++ b/test/stdcpp/Makefile @@ -0,0 +1,29 @@ +include ../common.mak + +TESTS:=array + +.PHONY: all clean + +# osx32 does not link properly, nobody uses it anyway... +ifeq ($(OS)$(MODEL), osx32) + +all: + +clean: + +else +all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS))) + +$(ROOT)/%.done : $(ROOT)/% + @echo Testing $* + $(QUIET)$(TIMELIMIT)$(ROOT)/$* $(RUN_ARGS) + @touch $@ + +$(ROOT)/%: $(SRC)/%_test.d $(SRC)/%.cpp + mkdir -p $(dir $@) + $(QUIET)$(CXX) $(CXXFLAGS) -c -o $(ROOT)/$*_cpp.o $(SRC)/$*.cpp + $(QUIET)$(DMD) $(DFLAGS) -main -unittest -of$@ $< $(ROOT)/$*_cpp.o + +clean: + rm -rf $(GENERATED) +endif diff --git a/test/stdcpp/src/array.cpp b/test/stdcpp/src/array.cpp new file mode 100644 index 0000000000..846e89836e --- /dev/null +++ b/test/stdcpp/src/array.cpp @@ -0,0 +1,20 @@ +#include + +std::array fromC_val(std::array); +std::array& fromC_ref(std::array&); + +std::array& sumOfElements_ref(std::array& arr) +{ + int r = 0; + for (size_t i = 0; i < arr.size(); ++i) + r += arr[i]; + arr.fill(r); + return arr; +} + +std::array sumOfElements_val(std::array arr) +{ + int r = sumOfElements_ref(arr)[0] + fromC_ref(arr)[0] + fromC_val(arr)[0]; + arr.fill(r); + return arr; +} diff --git a/test/stdcpp/src/array_test.d b/test/stdcpp/src/array_test.d new file mode 100644 index 0000000000..d38f02e4c9 --- /dev/null +++ b/test/stdcpp/src/array_test.d @@ -0,0 +1,62 @@ +import core.stdcpp.array; + +extern (C++) int test_array() +{ + array!(int, 5) arr; + arr[] = [0, 2, 3, 4, 5]; + ++arr.front; + + assert(arr.size == 5); + assert(arr.length == 5); + assert(arr.max_size == 5); + assert(arr.empty == false); + assert(arr.front == 1); + + assert(sumOfElements_val(arr)[0] == 160); + assert(sumOfElements_ref(arr)[0] == 15); + + array!(int, 0) arr2; + assert(arr2.size == 0); + assert(arr2.length == 0); + assert(arr2.max_size == 0); + assert(arr2.empty == true); + assert(arr2[] == []); + + return 0; +} + + +extern(C++): + +// test the ABI for calls to C++ +array!(int, 5) sumOfElements_val(array!(int, 5) arr); +ref array!(int, 5) sumOfElements_ref(return ref array!(int, 5) arr); + +// test the ABI for calls from C++ +array!(int, 5) fromC_val(array!(int, 5) arr) +{ + assert(arr[] == [1, 2, 3, 4, 5]); + assert(arr.front == 1); + assert(arr.back == 5); + assert(arr.at(2) == 3); + + arr.fill(2); + + int r; + foreach (e; arr) + r += e; + + assert(r == 10); + + arr[] = r; + return arr; +} + +ref array!(int, 5) fromC_ref(return ref array!(int, 5) arr) +{ + int r; + foreach (e; arr) + r += e; + arr[] = r; + return arr; +} diff --git a/test/stdcpp/win64.mak b/test/stdcpp/win64.mak new file mode 100644 index 0000000000..d460ac2403 --- /dev/null +++ b/test/stdcpp/win64.mak @@ -0,0 +1,12 @@ +# built from the druntime top-level folder +# to be overwritten by caller +DMD=dmd +MODEL=64 +DRUNTIMELIB=druntime64.lib +CC=cl + +test: + "$(CC)" -c /Foarray_cpp.obj test\stdcpp\src\array.cpp /EHsc + "$(DMD)" -of=test.exe -m$(MODEL) -conf= -Isrc -defaultlib=$(DRUNTIMELIB) -main -unittest test\stdcpp\src\array_test.d array_cpp.obj + test.exe + del test.exe test.obj array_cpp.obj diff --git a/win64.mak b/win64.mak index 85c5ae95e0..3764b9d955 100644 --- a/win64.mak +++ b/win64.mak @@ -102,6 +102,11 @@ test_aa: test_hash: $(DMD) -m$(MODEL) -conf= -Isrc -defaultlib=$(DRUNTIME) -run test\hash\src\test_hash.d +test_stdcpp: + $(MAKE) -f test\stdcpp\win64.mak "DMD=$(DMD)" MODEL=$(MODEL) "VCDIR=$(VCDIR)" DRUNTIMELIB=$(DRUNTIME) "CC=$(CC)" test + +test_all: test_uuid test_aa test_hash test_stdcpp + ################### zip/install/clean ########################## zip: druntime.zip @@ -121,4 +126,4 @@ clean: auto-tester-build: target -auto-tester-test: unittest test_uuid test_aa test_hash +auto-tester-test: unittest test_all