Skip to content
This repository has been archived by the owner on Oct 12, 2022. It is now read-only.

Added std::string #2310

Merged
merged 7 commits into from
Jul 29, 2019
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@ build_script:
- call "c:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat"

- cd c:\projects\dmd\src
- msbuild vcbuild\dmd.sln /p:Configuration=Release;Platform=x64
- msbuild vcbuild\dmd.sln /m /p:Configuration=Release;Platform=x64
- cd c:\projects\druntime
- setmscver.bat
- make -f win64.mak DMD=../dmd/generated/Windows/Release/x64/dmd CC=cl auto-tester-build auto-tester-test

test_script: true
3 changes: 3 additions & 0 deletions changelog/std_string.dd
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Added `core.stdcpp.string`.

Added `core.stdcpp.string`, which links against C++ `std::string`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should have stated that it's only for Windows :/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I'll update that, or I'll just make the other platforms CI work. (Well, Linux still has the interior point issue...)

1 change: 1 addition & 0 deletions mak/COPY
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ COPY=\
$(IMPDIR)\core\stdcpp\array.d \
$(IMPDIR)\core\stdcpp\exception.d \
$(IMPDIR)\core\stdcpp\new_.d \
$(IMPDIR)\core\stdcpp\string.d \
thewilsonator marked this conversation as resolved.
Show resolved Hide resolved
$(IMPDIR)\core\stdcpp\string_view.d \
$(IMPDIR)\core\stdcpp\typeinfo.d \
$(IMPDIR)\core\stdcpp\type_traits.d \
Expand Down
1 change: 1 addition & 0 deletions mak/DOCS
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ DOCS=\
$(DOCDIR)\core_stdcpp_array.html \
$(DOCDIR)\core_stdcpp_exception.html \
$(DOCDIR)\core_stdcpp_new_.html \
$(DOCDIR)\core_stdcpp_string.html \
$(DOCDIR)\core_stdcpp_string_view.html \
$(DOCDIR)\core_stdcpp_typeinfo.html \
$(DOCDIR)\core_stdcpp_type_traits.html \
Expand Down
1 change: 1 addition & 0 deletions mak/SRCS
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ SRCS=\
src\core\stdcpp\array.d \
src\core\stdcpp\exception.d \
src\core\stdcpp\new_.d \
src\core\stdcpp\string.d \
src\core\stdcpp\string_view.d \
src\core\stdcpp\type_traits.d \
src\core\stdcpp\xutility.d \
Expand Down
3 changes: 3 additions & 0 deletions mak/WINDOWS
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,9 @@ $(IMPDIR)\core\stdcpp\exception.d : src\core\stdcpp\exception.d
$(IMPDIR)\core\stdcpp\new_.d : src\core\stdcpp\new_.d
copy $** $@

$(IMPDIR)\core\stdcpp\string.d : src\core\stdcpp\string.d
copy $** $@

$(IMPDIR)\core\stdcpp\string_view.d : src\core\stdcpp\string_view.d
copy $** $@

Expand Down
7 changes: 7 additions & 0 deletions setmscver.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@echo off
echo _MSC_VER > ver.c
cl /nologo /EP ver.c > ver.txt
findstr /v /r /c:"^$" "ver.txt" > "ver_trim.txt"
set /P _MSC_VER=< ver_trim.txt
echo set _MSC_VER=%_MSC_VER%
del ver.c ver.txt ver_trim.txt
202 changes: 180 additions & 22 deletions src/core/stdcpp/allocator.d
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,6 @@ module core.stdcpp.allocator;
import core.stdcpp.new_;
import core.stdcpp.xutility : __cpp_sized_deallocation, __cpp_aligned_new;

// some versions of VS require a `* const` pointer mangling hack
// we need a way to supply the target VS version to the compile
version (CppRuntime_Microsoft)
version = NeedsMangleHack;

/**
* Allocators are classes that define memory models to be used by some parts of
* the C++ Standard Library, and most specifically, by STL containers.
Expand All @@ -30,35 +25,114 @@ struct allocator(T)
static assert(!is(T == const), "The C++ Standard forbids containers of const elements because allocator!(const T) is ill-formed.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should also check for immutable no ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. What I really care about though, is working out how to run those tests in this pr to prove it works on all those build machines...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure it does not work on POSIX, because template mangling is broken.
But having those tests running on Windows would be awesome.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to run it on every target, then I'd know that template mangling is broken and fix it...
I'm not aware of problems with template mangling. This needs to work everywhere. Can't know that unless ci executes the tests.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

immutable should already be caught by the mangling code, because there is no equivalent.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's right, however the additional check is a small price to pay here. A casual reader might not realize that immutable is disallowed by the compiler. Also, we have no guarantee this won't change in the future. Lastly, it will provide a better error message.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are all great ideas, but it's all worthless unless we find a way to test it ;)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See TurkeyMan#1. I suspect it will conflict with master due to other added tests, though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rainers Thanks! I can't see how the .cpp file gets built in there though, that's the trouble point, since it needs to compile for all targets, and that includes DMC and MSVC on windows...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't see how the .cpp file gets built in there though, that's the trouble point, since it needs to compile for all targets, and that includes DMC and MSVC on windows...

The amended changes to the makefile now contain the cpp-compilation, but as string.d doesn't compile on anything but win64, the auto-tester stops before it schedules anything to the Windows machines. I'd suggest adding version(CRuntime_Microsoft): at the top for now.

When this works to some extend, adding makefiles for other targets to the test should be straight-forward.

static assert(!is(T == immutable), "immutable is not representable in C++");
static assert(!is(T == class), "Instantiation with `class` is not supported; D can't mangle the base (non-pointer) type of a class. Use `extern (C++, class) struct T { ... }` instead.");
extern(D):

///
this(U)(ref allocator!U) {}

///
alias value_type = T;

///
alias rebind(U) = allocator!U;

version (CppRuntime_Microsoft)
{
///
T* allocate(size_t count) @nogc;
///
void deallocate(T* ptr, size_t count) @nogc;
import core.stdcpp.xutility : _MSC_VER;

///
enum size_t max_size = size_t.max / T.sizeof;
T* allocate(size_t count) @nogc
{
static if (_MSC_VER <= 1800)
{
import core.stdcpp.xutility : _Xbad_alloc;
if (count == 0)
return null;
T* mem;
if ((size_t.max / T.sizeof < count) || (mem = __cpp_new(count * T.sizeof)) == 0)
Copy link
Contributor

@thewilsonator thewilsonator Jul 29, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

src\core\stdcpp\allocator.d(52): Error: cannot implicitly convert expression `__cpp_new(count * 4LU)` of type `void*` to `int*`
test\stdcpp\src\allocator_test.d(17): Error: template instance `core.stdcpp.allocator.allocator!int` error instantiating
src\core\stdcpp\allocator.d(52): Error: cannot implicitly convert expression `__cpp_new(count * 8LU)` of type `void*` to `double*`
test\stdcpp\src\allocator_test.d(18): Error: template instance `core.stdcpp.allocator.allocator!double` error instantiating
src\core\stdcpp\allocator.d(52): Error: cannot implicitly convert expression `__cpp_new(count * 24LU)` of type `void*` to `MyStruct*`
test\stdcpp\src\allocator_test.d(19): Error: template instance `core.stdcpp.allocator.allocator!(MyStruct)` error instantiating
Suggested change
if ((size_t.max / T.sizeof < count) || (mem = __cpp_new(count * T.sizeof)) == 0)
if ((size_t.max / T.sizeof < count) || (mem = cast(T*)__cpp_new(count * T.sizeof)) is null)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see that ;) ... i just had to wait 2 hours to get the error message!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see that ;) ... i just had to wait 2 hours to get the error message!

If you cannot test locally, maybe it would be more productive to setup your own CI on appveyor, you can select an image with VS2013 installed, too (see top of appveyor.xml). You can probably set it up before next iteration here is done...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pushed before going to work. if it fails again, i'll do that tonight.
I had no idea appveyor had testers for 2013 still. we should just use it for all windows builds and get rid of the win32 auto-tester machines.

Look at the auto-tester logs; most machines take 5-10 minutes, except the windows machines which take 30-50 miunutes...

_Xbad_alloc();
return mem;
}
else
{
enum _Align = _New_alignof!T;

version (NeedsMangleHack)
static size_t _Get_size_of_n(T)(const size_t _Count)
{
static if (T.sizeof == 1)
return _Count;
else
{
enum size_t _Max_possible = size_t.max / T.sizeof;
return _Max_possible < _Count ? size_t.max : _Count * T.sizeof;
}
}
const size_t _Bytes = _Get_size_of_n!T(count);

static if (!__cpp_aligned_new || _Align <= __STDCPP_DEFAULT_NEW_ALIGNMENT__)
{
version (INTEL_ARCH)
{
if (_Bytes >= _Big_allocation_threshold)
return cast(T*)_Allocate_manually_vector_aligned(_Bytes);
}
return _Bytes ? cast(T*)__cpp_new(_Bytes) : null;
}
else
{
if (_Bytes == 0)
return null;
size_t _Passed_align = _Align;
version (INTEL_ARCH)
{
if (_Bytes >= _Big_allocation_threshold)
_Passed_align = _Align < _Big_allocation_alignment ? _Big_allocation_alignment : _Align;
}
return cast(T*)__cpp_new_aligned(_Bytes, cast(align_val_t)_Passed_align);
}
}
}
///
void deallocate(T* ptr, size_t count) @nogc
{
// HACK: workaround to make `deallocate` link as a `T * const`
private extern (D) enum string constHack(string name) = (){
version (Win64)
enum sub = "AAXPE";
static if (_MSC_VER <= 1800)
{
__cpp_delete(ptr);
}
else
{
// this is observed from VS2017
void* _Ptr = ptr;
size_t _Bytes = T.sizeof * count;

enum _Align = _New_alignof!T;
static if (!__cpp_aligned_new || _Align <= __STDCPP_DEFAULT_NEW_ALIGNMENT__)
{
version (INTEL_ARCH)
{
if (_Bytes >= _Big_allocation_threshold)
_Adjust_manually_vector_aligned(_Ptr, _Bytes);
}
static if (_MSC_VER <= 1900)
__cpp_delete(ptr);
else
__cpp_delete_size(_Ptr, _Bytes);
}
else
enum sub = "AEXPA";
foreach (i; 0 .. name.length - sub.length)
if (name[i .. i + sub.length] == sub[])
return name[0 .. i + 3] ~ 'Q' ~ name[i + 4 .. $];
assert(false, "substitution string not found!");
}();
pragma(linkerDirective, "/alternatename:" ~ deallocate.mangleof ~ "=" ~ constHack!(deallocate.mangleof));
{
size_t _Passed_align = _Align;
version (INTEL_ARCH)
{
if (_Bytes >= _Big_allocation_threshold)
_Passed_align = _Align < _Big_allocation_alignment ? _Big_allocation_alignment : _Align;
}
__cpp_delete_size_aligned(_Ptr, _Bytes, cast(align_val_t)_Passed_align);
}
}
}

///
enum size_t max_size = size_t.max / T.sizeof;
}
else version (CppRuntime_Gcc)
{
Expand Down Expand Up @@ -124,3 +198,87 @@ struct allocator(T)
static assert(false, "C++ runtime not supported");
}
}


private:

// MSVC has some bonus complexity!
version (CppRuntime_Microsoft)
{
// some versions of VS require a `* const` pointer mangling hack
// we need a way to supply the target VS version to the compile
version = NeedsMangleHack;

version (X86)
version = INTEL_ARCH;
version (X86_64)
version = INTEL_ARCH;

// HACK: should we guess _DEBUG for `debug` builds?
version (NDEBUG) {}
else debug version = _DEBUG;

enum _New_alignof(T) = T.alignof > __STDCPP_DEFAULT_NEW_ALIGNMENT__ ? T.alignof : __STDCPP_DEFAULT_NEW_ALIGNMENT__;

version (INTEL_ARCH)
{
enum size_t _Big_allocation_threshold = 4096;
enum size_t _Big_allocation_alignment = 32;

static assert(2 * (void*).sizeof <= _Big_allocation_alignment, "Big allocation alignment should at least match vector register alignment");
static assert((v => v != 0 && (v & (v - 1)) == 0)(_Big_allocation_alignment), "Big allocation alignment must be a power of two");
static assert(size_t.sizeof == (void*).sizeof, "uintptr_t is not the same size as size_t");

// NOTE: this must track `_DEBUG` macro used in C++...
version (_DEBUG)
enum size_t _Non_user_size = 2 * (void*).sizeof + _Big_allocation_alignment - 1;
else
enum size_t _Non_user_size = (void*).sizeof + _Big_allocation_alignment - 1;

version (Win64)
enum size_t _Big_allocation_sentinel = 0xFAFAFAFAFAFAFAFA;
else
enum size_t _Big_allocation_sentinel = 0xFAFAFAFA;

void* _Allocate_manually_vector_aligned(const size_t _Bytes) @nogc
{
size_t _Block_size = _Non_user_size + _Bytes;
if (_Block_size <= _Bytes)
_Block_size = size_t.max;

const size_t _Ptr_container = cast(size_t)__cpp_new(_Block_size);
if (!(_Ptr_container != 0))
assert(false, "invalid argument");
void* _Ptr = cast(void*)((_Ptr_container + _Non_user_size) & ~(_Big_allocation_alignment - 1));
(cast(size_t*)_Ptr)[-1] = _Ptr_container;

version (_DEBUG)
(cast(size_t*)_Ptr)[-2] = _Big_allocation_sentinel;
return (_Ptr);
}

void _Adjust_manually_vector_aligned(ref void* _Ptr, ref size_t _Bytes) pure nothrow @nogc
{
_Bytes += _Non_user_size;

const size_t* _Ptr_user = cast(size_t*)_Ptr;
const size_t _Ptr_container = _Ptr_user[-1];

// If the following asserts, it likely means that we are performing
// an aligned delete on memory coming from an unaligned allocation.
assert(_Ptr_user[-2] == _Big_allocation_sentinel, "invalid argument");

// Extra paranoia on aligned allocation/deallocation; ensure _Ptr_container is
// in range [_Min_back_shift, _Non_user_size]
version (_DEBUG)
enum size_t _Min_back_shift = 2 * (void*).sizeof;
else
enum size_t _Min_back_shift = (void*).sizeof;

const size_t _Back_shift = cast(size_t)_Ptr - _Ptr_container;
if (!(_Back_shift >= _Min_back_shift && _Back_shift <= _Non_user_size))
assert(false, "invalid argument");
_Ptr = cast(void*)_Ptr_container;
}
}
}
4 changes: 2 additions & 2 deletions src/core/stdcpp/exception.d
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,10 @@ else version (CppRuntime_Microsoft)
///
this(const(char)* message = "unknown", int = 1) nothrow { msg = message; }
///
~this() nothrow {}
extern(D) ~this() nothrow {}

///
const(char)* what() const nothrow { return msg != null ? msg : "unknown exception"; }
extern(D) const(char)* what() const nothrow { return msg != null ? msg : "unknown exception"; }

// TODO: do we want this? exceptions are classes... ref types.
// final ref exception opAssign(ref const(exception) e) nothrow { msg = e.msg; return this; }
Expand Down
Loading