From fbeeb0bde6250aef90ae2c7cf46ef2054e6fda95 Mon Sep 17 00:00:00 2001 From: Emmankoko Date: Sun, 15 Oct 2023 07:45:47 +0000 Subject: [PATCH] Add support for vector and list on POSIX This adds support and test for C++'s `` and `` headers on POSIX (Linux / Mac OS) platforms. --- .github/workflows/e2e.yml | 3 +- dub.json | 15 ++ extras/list.cpp | 3 + extras/vector.cpp | 3 + source/stdcpp/list.d | 293 ++++++++++++++++++++++++++++++++++++ source/stdcpp/string.d | 20 ++- source/stdcpp/test/list.d | 37 +++++ source/stdcpp/test/string.d | 18 +++ source/stdcpp/test/vector.d | 61 +++++++- source/stdcpp/vector.d | 130 +++++++++++++--- 10 files changed, 551 insertions(+), 32 deletions(-) create mode 100644 extras/list.cpp create mode 100644 extras/vector.cpp create mode 100644 source/stdcpp/list.d create mode 100644 source/stdcpp/test/list.d create mode 100644 source/stdcpp/test/string.d diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index c97495f..9d9aa0e 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -39,7 +39,8 @@ jobs: dc: - ldc-latest target: - - { name: g++-9, compiler: g++, cxx-version: 9.4.0 } +# - { name: g++-9, compiler: g++, cxx-version: 9.4.0 } + - { name: clang-13, compiler: clang, cxx-version: 13.0.0 } # Using a specific version for reproductibility. # Feel free to update when a new release has matured. diff --git a/dub.json b/dub.json index e9e38c0..24b99c5 100644 --- a/dub.json +++ b/dub.json @@ -13,6 +13,21 @@ }, { "name": "unittest", + "lflags": [ "-lstdc++" ], + "preGenerateCommands": [ + "clang++ -c extras/vector.cpp -o extras/vector.o", + "clang++ -c extras/list.cpp -o extras/list.o" + ], + "sourceFiles": [ "extras/*.o" ] }, + { + "name": "unittest-gcc", + "lflags": [ "-lc++" ], + "preGenerateCommands": [ + "g++ -c extras/vector.cpp -o extras/vector.o", + "g++ -c extras/list.cpp -o extras/list.o" + ], + "sourceFiles": [ "extras/*.o" ] + } ] } diff --git a/extras/list.cpp b/extras/list.cpp new file mode 100644 index 0000000..b06e7ed --- /dev/null +++ b/extras/list.cpp @@ -0,0 +1,3 @@ +#include + +template class std::list; diff --git a/extras/vector.cpp b/extras/vector.cpp new file mode 100644 index 0000000..d561bce --- /dev/null +++ b/extras/vector.cpp @@ -0,0 +1,3 @@ +#include + +template class std::vector; diff --git a/source/stdcpp/list.d b/source/stdcpp/list.d new file mode 100644 index 0000000..a9cbfa2 --- /dev/null +++ b/source/stdcpp/list.d @@ -0,0 +1,293 @@ +/** + * D bindings for std::list + * + * Copyright: Copyright (c) 2023 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: Emmanuel Nyarko, Mathias Lang + */ +module stdcpp.list; + +import stdcpp.allocator; + +version (CppRuntime_Gcc) +{ + version (GLIBCXX_USE_CXX98_ABI) + { + private enum listNamespace = "std"; + } + else + { + import core.internal.traits : AliasSeq; + private enum listNamespace = AliasSeq!("std", "__cxx11"); + } +} +else +{ + import stdcpp.xutility : StdNamespace; + alias listNamespace = StdNamespace; +} + +enum def {value}; //for later use +extern(C++, (listNamespace)): + +alias list(Type) = list!(Type, allocator!Type); + +extern(C++, class) struct list(Type, Allocator) +{ + static assert(!is(Type == bool), " list not required to have specialization for bool"); + + /// + alias value_type = Type; + + /// + alias allocator_type = Allocator; + + /// + alias size_type = size_t; + + /// + alias pointer = Type*; + + /// + alias const_pointer = const(Type)*; + + /// + alias difference_type = ptrdiff_t; + + /// + ref list opAssign(); + + /// + @disable this() @safe pure nothrow @nogc scope; + + version (CppRuntime_Gcc) + { + version (GLIBCXX_USE_CXX98_ABI) + { + //allocator ctor + this(ref const allocator!Type); + + //list(n,value) ctor until c++11 + this(size_type __n, ref const value_type value, ref const allocator!Type); + + /// Copy constructor + this(ref const list!Type __x); + + extern(D) this(size_type n) + { + value_type type_instance = value_type.init; + allocator!Type alloc_instance = allocator!(Type).init; + this(n, type_instance, alloc_instance); + } + + extern(D) void assign(size_type n, const value_type item) + { + this.assign(n, item); + } + + extern(D) void push_back(const Type item) + { + this.push_back(item); + } + + extern(D) void push_front(const Type item) + { + this.push_front(item); + } + + extern(D) void remove(const value_type item) + { + this.remove(item); + } + + void assign(size_type count, ref const value_type value); + + ref list opAssign(ref const list!Type other); + + //just const until c++11 + allocator_type get_allocator() const; + + ref value_type front(); + +// const(value_type) ref front() const; + + ref value_type back(); + + pointer begin(); + + pointer end(); + + //just const until c++11 + bool empty() const; + + size_type size() const; + + size_type max_size() const nothrow; + + void clear(); + + //insert halted for now + + void push_back(ref const Type val); + + void pop_back(); + + void push_front(ref const value_type val); + + void pop_front(); + + void swap(ref const list!Type other); + + void merge( ref const list!Type other); + + void merge(U)(ref const list!Type other, U comp); + + void remove(const ref value_type val); + + void reverse(); + + void sort(); + + size_type unique(); + + void sort(U)(U comp); + + private struct node + { + node* prev; + node* next; + } + // pre-c++11 doesn't keep track of its size + private node A; + } + else // !GLIBCXX_USE_CXX98_ABI + { + this(def) + { + allocator!Type alloc_instance = allocator!(Type).init; + this(alloc_instance); + } + + //allocator ctor + this(ref const allocator!Type); + + // Copy constructor + this(ref const list!Type __x); + + //list(n,value) ctor + this(size_type __n, ref const value_type value, ref const allocator!Type); + + extern(D) this(size_type n, const value_type element) + { + allocator!Type alloc_instance = allocator!(Type).init; + this(n, element, alloc_instance); + } + + this(ref const list!Type other, ref const allocator!Type); + + //list(n) ctor + this(size_type __n, ref const allocator!Type); + + extern(D) this(size_type n) + { + allocator!Type alloc_instance = allocator!(Type).init; + this(n, alloc_instance); + } + + extern(D) void assign(size_type n, const value_type item) + { + this.assign(n, item); + } + + extern(D) void push_back(const Type item) + { + this.push_back(item); + } + + extern(D) void push_front(const Type item) + { + this.push_front(item); + } + + extern(D) void resize(size_type n, const value_type item) + { + this.resize(n, item); + } + + extern(D) void remove(const value_type item) + { + this.remove(item); + } + + ref list opAssign(ref const list!Type other); + + void assign(size_type count, ref const value_type value); + + //const nothrow since C++11 + allocator_type get_allocator() const nothrow; + + ref value_type front(); + + ref value_type back(); + + pointer begin() nothrow; + + pointer end() nothrow; + + //const nothrow since c++11 + bool empty() const nothrow; + + size_type size() const nothrow; + + void clear() nothrow; + + void push_back(ref const Type val); + + void pop_back(); + + void push_front(ref const value_type val); + + void pop_front(); + + void resize(size_type count); + + void resize(size_type count, ref const value_type val); + + void swap(ref const list!Type other) nothrow; + + void merge( ref const list!Type other); + + void merge(U)(ref const list!Type other, U comp); + + void remove(const ref value_type val); + + void reverse() nothrow; + + void sort(); + + void sort(U)(U comp); + + void unique(); + + void unique(U)(U p); + + size_type unique(); + + size_type unique(U)(U p); + + private struct node + { + node* prev; + node* next; + } + private node A; + private size_type _M_size; //new list keeps track of it's size + } + } + else version (CppRuntime_Microsft) + { + static assert(0, "Not yet supported!"); + } +} diff --git a/source/stdcpp/string.d b/source/stdcpp/string.d index 7d2584c..13e2918 100644 --- a/source/stdcpp/string.d +++ b/source/stdcpp/string.d @@ -108,10 +108,22 @@ extern(C++, (StdNamespace)) struct char_traits(CharT) } } -// I don't think we can have these here, otherwise symbols are emit to druntime, and we don't want that... -//alias std_string = basic_string!char; +/** + * An alias for `std::string` + * + * See_Also: https://cplusplus.com/reference/string/string/ + */ +alias std_string = basic_string!char; + //alias std_u16string = basic_string!wchar; // TODO: can't mangle these yet either... -//alias std_u32string = basic_string!dchar; + +/** + * An alias for `std::u32string` + * + * See_Also: https://cplusplus.com/reference/string/u32string/ + */ +alias std_u32string = basic_string!dchar; + //alias std_wstring = basic_string!wchar_t; // TODO: we can't mangle wchar_t properly (yet?) /** @@ -1406,8 +1418,6 @@ extern(D): } else { - pragma(msg, "libstdc++ std::__cxx11::basic_string is not yet supported; the struct contains an interior pointer which breaks D move semantics!"); - //---------------------------------------------------------------------------------- // GCC/libstdc++ modern implementation //---------------------------------------------------------------------------------- diff --git a/source/stdcpp/test/list.d b/source/stdcpp/test/list.d new file mode 100644 index 0000000..e38fba4 --- /dev/null +++ b/source/stdcpp/test/list.d @@ -0,0 +1,37 @@ +/******************************************************************************* + + Tests for std::list + +*******************************************************************************/ + +module stdcpp.test.list; + +import stdcpp.list; + +unittest +{ + auto p = list!int(5); + p.push_back(5); + assert(p.sizeof == 24); + assert(p.size() == 6); + assert(p.front() == 0); + assert(p.back() == 5); + p.push_front(7); + assert(p.front() == 7); + p.clear(); + assert(p.size() == 0); + p.assign(5,5); + assert(p.size == 5); + p.pop_front(); + assert(p.size == 4); + p.resize(3); + assert(p.size == 3); + + list!int cp_obj = p; //opAssign + assert(cp_obj.size == 3); + cp_obj.clear(); + cp_obj.push_back(45); + cp_obj.push_back(56); + assert(cp_obj.front == 45); + assert(cp_obj.back == 56); +} diff --git a/source/stdcpp/test/string.d b/source/stdcpp/test/string.d new file mode 100644 index 0000000..e95e2ca --- /dev/null +++ b/source/stdcpp/test/string.d @@ -0,0 +1,18 @@ +/******************************************************************************* + + Tests for `std::string` + +*******************************************************************************/ + +module stdcpp.test.string; +import stdcpp.string; + +unittest +{ + auto a = std_string("hello"); + a.push_back('a'); + assert(a.size() == 6); + assert(std_string.sizeof == 32); + // verifying small string optimization + assert(a.capacity == 15); +} diff --git a/source/stdcpp/test/vector.d b/source/stdcpp/test/vector.d index 55c995b..c5995f5 100644 --- a/source/stdcpp/test/vector.d +++ b/source/stdcpp/test/vector.d @@ -10,8 +10,63 @@ import stdcpp.vector; unittest { - vector!int vec; + auto vec = vector!int(4); vec.push_back(42); - assert(vec.length == 1); - assert(vec[0] == 42); + assert(vec.length == 5); + assert(vec[4] == 42); + assert(vec.at(3) == 0); + vec.pop_back(); + assert(vec.length == 4); + vec.clear(); + assert(vec.empty == 1); + vec.push_back(7); + vector!int new_vec = vec; //opAssign test + auto it = new_vec.begin(); + assert(*(it) == 7); +} + +unittest +{ + auto p = vector!int(4,9); + assert(p.capacity() == 4); + p.reserve(6); + assert(p.capacity() == 6); + //3 push backs to reallocate memory by 2 after initially capacity fills up + p.push_back(9); + p.push_back(9);//capacity is 6 now + p.push_back(9); + assert(p.capacity() == 12); + p.resize(5); + assert(p.length == 5); + assert(p.sizeof == 24);//verifying three pointers + p.assign(3,8); + assert(p.length == 3); + p.push_back(4); + p.push_back(9); + auto iter = p.begin(); + //single iteration movements + assert(*(iter) == 8); + iter++; + assert(*(iter) == 8); + iter++; + assert(*(iter) == 8); + iter++; + assert(*(iter) == 4); + iter++; + assert(*(iter) == 9); // 8,8,8,4,9 in that order +} + +unittest +{ + import stdcpp.allocator; + allocator!int alloc_instance = allocator!(int).init; + auto q = vector!int(alloc_instance); + q.push_back(4); + assert(q[0] == 4); + assert(q.length == 1); + auto cp_ctor = vector!int(q); // copy constructor + assert(cp_ctor[0] == 4); + assert(cp_ctor.length == 1); + auto iter = cp_ctor.begin(); + assert(*(iter) == 4); // first element in vector } diff --git a/source/stdcpp/vector.d b/source/stdcpp/vector.d index 82018a6..1cbda66 100644 --- a/source/stdcpp/vector.d +++ b/source/stdcpp/vector.d @@ -23,6 +23,11 @@ module stdcpp.vector; import stdcpp.allocator; +version(CppRuntime_Gcc) + version = Non_microsoft; +else version(CppRuntime_Clang) + version = Non_microsoft; + enum DefaultConstruct { value } /// Constructor argument for default construction @@ -71,14 +76,6 @@ extern(D): /// ref vector opAssign(U)(auto ref vector!(U, Alloc) s) { opAssign(s.as_array); return this; } - /// - ref vector opAssign(T[] array) - { - clear(); - reserve(array.length); - insert(0, array); - return this; - } /// void opIndexAssign()(auto ref T val, size_t index) { as_array[index] = val; } @@ -102,19 +99,12 @@ extern(D): /// void opIndexOpAssign(string op)(T[] val) { mixin("as_array[] " ~ op ~ "= val[];"); } + /// ref inout(T) front() inout pure nothrow @safe @nogc { return as_array[0]; } /// ref inout(T) back() inout pure nothrow @safe @nogc { return as_array[$-1]; } - /// - ref vector opOpAssign(string op : "~")(auto ref T item) { push_back(forward!item); return this; } - /// - ref vector opOpAssign(string op : "~")(T[] array) { insert(length, array); return this; } - - /// - void append(T[] array) { insert(length, array); } - /// Performs elementwise equality check. bool opEquals(this This, That)(auto ref That rhs) if (is(immutable That == immutable vector)) { return as_array == rhs.as_array; } @@ -130,10 +120,6 @@ extern(D): // Modifiers /// - void push_back(U)(auto ref U element) - { - emplace_back(forward!element); - } version (CppRuntime_Microsoft) { @@ -179,8 +165,24 @@ extern(D): } /// - ~this() { _Tidy(); } + ~this() { _Tidy(); } + + /// + ref vector opAssign(T[] array) + { + clear(); + reserve(array.length); + insert(0, array); + return this; + } + /// + ref vector opOpAssign(string op : "~")(auto ref T item) { push_back(forward!item); return this; } + /// + ref vector opOpAssign(string op : "~")(T[] array) { insert(length, array); return this; } + + /// + void append(T[] array) { insert(length, array);} /// ref inout(Alloc) get_allocator() inout pure nothrow @safe @nogc { return _Getal(); } @@ -200,6 +202,12 @@ extern(D): /// ref inout(T) at(size_type i) inout pure nothrow @trusted @nogc { return _Get_data()._Myfirst[0 .. size()][i]; } + /// + void push_back(U)(auto ref U element) + { + emplace_back(forward!element); + } + /// ref T emplace_back(Args...)(auto ref Args args) { @@ -750,9 +758,85 @@ extern(D): inout(T)[] as_array() inout pure nothrow @trusted @nogc { return null; } ref inout(T) at(size_type i) inout pure nothrow @trusted @nogc { data()[0]; } } - else + else version (Non_microsoft) { - static assert(false, "C++ runtime not supported"); + pointer _M_start; + pointer _M_finish; + pointer _M_end_of_storage; + + //defining size for Gcc runtime + size_t size() const pure nothrow @safe @nogc + { + return size_type(this._M_finish - this._M_start); + } + + extern(D) void push_back(const T item) + { + return this.push_back(item); + } + + extern(D) void assign(size_t n, const T x) + { + return this.assign(n,x); + } + + size_t capacity() const @safe nothrow pure @nogc + { + return size_type(this._M_end_of_storage - this._M_start); + } + + ref inout(T) at(size_t __n) inout pure nothrow @nogc {return this._M_start[0 .. size()][__n];} + + + extern(D) this(size_t n) + { + allocator!T alloc_instance = allocator!(T).init; + this (n, alloc_instance); + } + + extern(D) this(size_t n, const T value) + { + allocator!T alloc_instance = allocator!(T).init; + this(n, value, alloc_instance); + } + + inout(T)[] as_array() inout pure nothrow @trusted @nogc {return this._M_start[0 .. size()];} + + extern(C++) vector opAssign( const ref vector!T); + + //binding push_back which takes only lvalues + extern(C++) void push_back(ref const T __x); + + //vector(n) constructor + extern(C++) this(size_t __n, const ref allocator!T); + + //copy constructor + extern(C++) this(ref const vector!(T, allocator!T)); + + //vector(allocator) constructor + extern(C++) this(const ref allocator!T allocc); + + + //vector(n, value) constructor + extern(C++) this(size_t __n, const ref T, const ref allocator!T); + + extern(C++) void reserve(size_t __n); + + extern(C++) void resize(size_t __n); + + extern(C++) bool empty() const @safe @nogc; + + extern(C++) void clear(); + + extern(C++) void pop_back(); + + extern(C++) void assign(size_t __n, ref const T __x); + + extern(C++) void swap(ref vector!(T) x); + + extern(C++) pointer begin() nothrow; + + extern(C++) pointer end() nothrow; } }