diff --git a/README.md b/README.md index d8011c3..2c50235 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ This library includes all (almost?) "aliases" to `core.stdc.*` modules in respec # Custom modules - [x] format (exists in C++20 specs, but my version currently is for emulating c's format function) -- [ ] conv (just your normal type conversion) +- [ ] conv (just your normal type conversion, right now contains only c++ "casts") ## On classes D's keyword `new` can't be used with noGC and to "fix" that there's two functions: `_new` and `_free` in `clib.memory` which can be used to create classes with noGC. @@ -84,7 +84,7 @@ class DClass {} class DChild: DClass{} DChild dprt; if (typeid(dprt) != typeid(DClass)) printf("Not same\n"); -if (typeid(DClass).isBaseOf(typeid(DChild))) printf("Is child\n"); +if (typeid(DClass).is_base_of(typeid(DChild))) printf("Is child\n"); // Clib way: @@ -100,38 +100,48 @@ class CChild: CClass{ CChild cprt; if (_typeid(cprt) != _typeid!CClass) printf("Not same\n"); -if (_typeid!CClass().isBaseOf(cprt)) printf("Is child\n"); +if (_typeid!CClass().is_base_of(cprt)) printf("Is child\n"); ``` `clib.typeinfo.reinterpret_cast` can be used to work around [known bug](https://issues.dlang.org/show_bug.cgi?id=21690). ```d -import core.stdc.stdio; -import clib.typecast; +import clib.stdio; +import clib.typeinfo; import clib.memory; +import clib.conv; extern(C++) class ICpp: CppObject { - void baseFunc() @nogc nothrow { printf("ICpp func\n"); } + void base_func() @nogc nothrow { printf("ICpp func\n"); } } extern(C++) class CppClass: ICpp { mixin RTTI!ICpp; - override void baseFunc() @nogc nothrow { printf("CppClass func\n"); } + override void base_func() @nogc nothrow { printf("CppClass func\n"); } } extern(C) int main() @nogc nothrow { CppClass c = _new!CppClass(); - void testBaseFunc(ICpp base) { - base.baseFunc(); - reinterpret_cast!ICpp(base).baseFunc(); // doesn't matter as it's already ICpp + void test_base_func(ICpp base) { + base.base_func(); + reinterpret_cast!ICpp(base).base_func(); // doesn't matter as it's already ICpp } - testBaseFunc(c); // will case segfault!!! - testBaseFunc(reinterpret_cast!ICpp(c)); // must be a cast - reinterpret_cast!ICpp(c).testBaseFunc(); // or treat it as member + test_base_func(c); // will case segfault!!! + test_base_func(reinterpret_cast!ICpp(c)); // must be a cast + reinterpret_cast!ICpp(c).test_base_func(); // or treat it as member } ``` +## Disable GC + +Before `main` function or entrance point put: + +```d +import clib.memory: DISABLE_GC; +mixin DISABLE_GC; +``` + ## Development - [dmd / ldc / gdc](https://dlang.org/) - D compiler - [dub](https://code.dlang.org/) - D package manager diff --git a/src/clib/conv.d b/src/clib/conv.d new file mode 100644 index 0000000..848dbae --- /dev/null +++ b/src/clib/conv.d @@ -0,0 +1,39 @@ +module clib.conv; +// TODO: add separate cast versions for C++ linkage and D classes + +/// Interprets F as T (dangerous, use mainly as workaround to bug 21690) +T reinterpret_cast(T, F)(F t) @nogc nothrow { + return ( cast(T) cast(void*) t ); +} + +/// Downcasts F to T (CAN RETURN NULL IF UNABLE TO DOWNCAST) +T dynamic_cast(T, F)(F t) @nogc nothrow if (IS_CLASS!T && IS_CLASS!F) { + if (_typeid!(F)().is_base_of!(T)()) { + return ( cast(T) cast(void*) t ); + } else { + return null; + } +} + +/// Downcasts F to T or converts scalar types (CAN RETURN NULL IF UNABLE TO DOWNCAST) +T static_cast(T, F)(F t) @nogc nothrow +if ((IS_CLASS!T && IS_CLASS!F) || (IS_SCALAR!T && IS_SCALAR!F)) { + if (IS_SCALAR_TYPE!T) { + return cast(T) t; + } else { + return dynamic_cast!(T, F)(t); + } +} + +/// Performs basic type casting +T const_cast(T, F)(F t) @nogc nothrow { + return cast(T) t; +} + +private enum bool IS_SCALAR(T) = __traits(isScalar, T) && is(T : real); + +private enum bool IS_CLASS(T) = + (is(T == U*, U) && (is(T == class) || is(T == interface))) || + (is(T == class) || is(T == interface)); + + diff --git a/src/clib/format.d b/src/clib/format.d index 9d199fb..c435ba7 100644 --- a/src/clib/format.d +++ b/src/clib/format.d @@ -26,7 +26,7 @@ scope cstring format(A...)(const(char)[] fmt, A args) @nogc nothrow { char* buffer = cast(char*) malloc(len); snprintf(buffer, len, fmt.ptr, args); cstring str; - str.assignPointer(buffer, len); + str.assign_pointer(buffer, len); return str; } diff --git a/src/clib/memory.d b/src/clib/memory.d index a380f20..b8de6e9 100644 --- a/src/clib/memory.d +++ b/src/clib/memory.d @@ -8,6 +8,10 @@ module clib.memory; import clib.stdlib: malloc, realloc, free; +mixin template DISABLE_GC() { + extern(C) __gshared string[] rt_options = [ "gcopt=disable:1" ]; +} + /++ Used to allocate/deallocate memory for classes diff --git a/src/clib/package.d b/src/clib/package.d index bd87c25..bb60a05 100644 --- a/src/clib/package.d +++ b/src/clib/package.d @@ -30,6 +30,7 @@ public import clib.assert_, clib.complex, clib.ctype, + clib.conv, clib.errno, clib.exception, clib.fenv, diff --git a/src/clib/typeinfo.d b/src/clib/typeinfo.d index 87a4ff6..ff2eb9c 100644 --- a/src/clib/typeinfo.d +++ b/src/clib/typeinfo.d @@ -10,6 +10,7 @@ version(CLIB_USE_TYPEINFO): import clib.string: strcmp; import clib.traits; +import clib.conv; import clib.memory; @@ -64,11 +65,11 @@ struct type_info { /// Fully qualified type name const char* name = "\0".ptr; /// Is type a pointer - const bool isPointer = false; + const bool IS_POINTER = false; /// Is type a function - const bool isFunction = false; - private const char[] _strName; - private bool _isACppObject = false; + const bool is_function = false; + private const char[] _str_name; + private bool _is_a_cpp_object = false; /// Returns fully qualified type name const(char*) toString() const { return name; } @@ -95,18 +96,18 @@ struct type_info { } /// Is T a child of type_info owner - bool isBaseOf(T)(T t) @nogc nothrow { - return isBaseOf!T; + bool is_base_of(T)(T t) @nogc nothrow { + return is_base_of!T; } /// Ditto - bool isBaseOf(T)() @nogc nothrow { - if (isPointer || isFunction) return false; + bool is_base_of(T)() @nogc nothrow { + if (IS_POINTER || is_function) return false; import clib.string: strlen, memcmp; - if (!isSubclassOfCppObject!T) return false; - if (_isACppObject) return true; + if (!IS_SUBCLASS_OF_CPP_OBJECT!T) return false; + if (_is_a_cpp_object) return true; char[200] s = ' '; - char[] t = cast(char[]) _strName; + char[] t = cast(char[]) _str_name; s[0..2] = cast(char[]) "__"; @@ -131,45 +132,45 @@ struct type_info { } /// Queries information about type -type_info _typeid(T)(T t) @nogc nothrow if (isSuitableForTypeID!T) { +type_info _typeid(T)(T t) @nogc nothrow if (IS_SUITABLE_FOR_TYPEID!T) { return _typeid!T(); } /// Ditto -type_info _typeid(T)() @nogc nothrow if (isSuitableForTypeID!T) { +type_info _typeid(T)() @nogc nothrow if (IS_SUITABLE_FOR_TYPEID!T) { type_info t = { - cast(const(char*)) __traits(fullyQualifiedName, Unqual!T).ptr, - isPointer!T, - isFunctionPointer!T || isDelegate!T, - cast(const(char[])) __traits(fullyQualifiedName, Unqual!T), + cast(const(char*)) __traits(fullyQualifiedName, UNQUAL!T).ptr, + IS_POINTER!T, + IS_FUNCTION_POINTER!T || IS_DELEGATE!T, + cast(const(char[])) __traits(fullyQualifiedName, UNQUAL!T), is(T == CppObject) }; return t; } -private bool isSuitableForTypeID(T)() @nogc nothrow { +private bool IS_SUITABLE_FOR_TYPEID(T)() @nogc nothrow { static if (is(T == interface)) return true; - const bool isPtr = isAnyPointerType!T; + const bool isPtr = IS_ANY_POINTER_TYPE!T; static if (!isPtr) { - return isSubclassOfCppObject!T; + return IS_SUBCLASS_OF_CPP_OBJECT!T; } else { return true; } } -private bool isSubclassOfCppObject(T)() @nogc nothrow if (!isAnyPointerType!T) { +private bool IS_SUBCLASS_OF_CPP_OBJECT(T)() @nogc nothrow if (!IS_ANY_POINTER_TYPE!T) { bool isCpp = __traits(getLinkage, T) == "C++"; bool isObj = __traits(hasMember, T, "__clib_cpp_object_identifier"); return isCpp && isObj; } -private bool isAnyPointerType(T)() @nogc nothrow { - return isPointer!T || isFunctionPointer!T || isDelegate!T; +private bool IS_ANY_POINTER_TYPE(T)() @nogc nothrow { + return IS_POINTER!T || IS_FUNCTION_POINTER!T || IS_DELEGATE!T; } /// Injects RunTime Type Information (allows type_info to see inheritance) -mixin template RTTI(T) if ((isSubclassOfCppObject!T || is(T == interface)) && !is(T == CppObject)) { +mixin template RTTI(T) if ((IS_SUBCLASS_OF_CPP_OBJECT!T || is(T == interface)) && !is(T == CppObject)) { mixin( __cpp_class_inheritance_generator!T() ); } @@ -193,40 +194,3 @@ char[200] __cpp_class_inheritance_generator(T)() { return s; } -// TODO: add separate cast versions for C++ linkage and D classes - -/// Interprets F as T (dangerous, use mainly as workaround to bug 21690) -T reinterpret_cast(T, F)(F t) @nogc nothrow { - return ( cast(T) cast(void*) t ); -} - -/// Downcasts F to T (CAN RETURN NULL IF UNABLE TO DOWNCAST) -T dynamic_cast(T, F)(F t) @nogc nothrow if (isClass!T && isClass!F) { - if (_typeid!(F)().isBaseOf!(T)()) { - return ( cast(T) cast(void*) t ); - } else { - return null; - } -} - -/// Downcasts F to T or converts scalar types (CAN RETURN NULL IF UNABLE TO DOWNCAST) -T static_cast(T, F)(F t) @nogc nothrow -if ((isClass!T && isClass!F) || (isScalar!T && isScalar!F)) { - if (isScalarType!T) { - return cast(T) t; - } else { - return dynamic_cast!(T, F)(t); - } -} - -/// Performs basic type casting -T const_cast(T, F)(F t) @nogc nothrow { - return cast(T) t; -} - -private enum bool isScalar(T) = __traits(isScalar, T) && is(T : real); - -private enum bool isClass(T) = - (is(T == U*, U) && (is(T == class) || is(T == interface))) || - (is(T == class) || is(T == interface)); - diff --git a/src/clib/vector.d b/src/clib/vector.d index f4389c8..30c691c 100644 --- a/src/clib/vector.d +++ b/src/clib/vector.d @@ -128,7 +128,7 @@ struct vector(T, A: IAllocator!T = allocator!T) if (!is(T == bool)) { // _size = other._size; // _capacity = other._capacity; // _refCounter[0] += 1; - assignCopy(other._data, other._size); + assign_copy(other._data, other._size); } ~this() @nogc nothrow { @@ -146,7 +146,7 @@ struct vector(T, A: IAllocator!T = allocator!T) if (!is(T == bool)) { /// Returns copy of this vector scope vector!(T, A) clone() @nogc nothrow { vector!(T, A) v; - v.assignCopy(_data, _size); + v.assign_copy(_data, _size); return v; } @@ -294,7 +294,7 @@ struct vector(T, A: IAllocator!T = allocator!T) if (!is(T == bool)) { DO NOT USE ON STACK-ALLOCATED MEMORY (WILL SEGFAULT)! +/ - void assignPointer( T* p_data, size_t p_size ) @nogc nothrow { + void assign_pointer( T* p_data, size_t p_size ) @nogc nothrow { if (_allocator is null) _allocator = _new!A(); if (_capacity != 0 || _data !is null) _allocator.deallocate(_data); _size = p_size; @@ -303,7 +303,7 @@ struct vector(T, A: IAllocator!T = allocator!T) if (!is(T == bool)) { } /// Copies data to vector and sets size and capacity to `p_size` - void assignCopy( T* p_data, size_t p_size ) @nogc nothrow { + void assign_copy( T* p_data, size_t p_size ) @nogc nothrow { if (_allocator is null) _allocator = _new!A(); resize(p_size); _size = p_size; @@ -318,16 +318,16 @@ struct vector(T, A: IAllocator!T = allocator!T) if (!is(T == bool)) { if (p_size <= _capacity) return true; if (_allocator is null) _allocator = _new!A(); - void* newData; + void* new_data; if (_data is null) { - newData = _allocator.allocate(p_size * T.sizeof); + new_data = _allocator.allocate(p_size * T.sizeof); } else { - newData = _allocator.reallocate(_data, p_size * T.sizeof); + new_data = _allocator.reallocate(_data, p_size * T.sizeof); } - if (newData is null) return false; - _data = cast(T*) newData; + if (new_data is null) return false; + _data = cast(T*) new_data; _capacity = p_size; @@ -338,16 +338,16 @@ struct vector(T, A: IAllocator!T = allocator!T) if (!is(T == bool)) { /// Returns true on success bool resize(size_t p_size, const T p_val = T.init) @nogc nothrow { if (_allocator is null) _allocator = _new!A(); - void* newData; + void* new_data; if (_data is null) { - newData = _allocator.allocate(p_size * T.sizeof); + new_data = _allocator.allocate(p_size * T.sizeof); } else { - newData = _allocator.reallocate(_data, p_size * T.sizeof); + new_data = _allocator.reallocate(_data, p_size * T.sizeof); } - if (newData is null) return false; + if (new_data is null) return false; - _data = cast(T*) newData; + _data = cast(T*) new_data; _capacity = p_size; if (p_size > _size) { @@ -374,7 +374,7 @@ struct vector(T, A: IAllocator!T = allocator!T) if (!is(T == bool)) { /// Pushes new element to beginning of vector. /// If `newSize + 1 > capacity` then it will `reserve(capacity * 2 + 2)` - void pushFront(T val) @nogc nothrow { + void push_front(T val) @nogc nothrow { if (_size >= _capacity) reserve((_capacity + 1) * 2); // for (size_t i = _size; i > 0; --i) _data[i] = _data[i - 1]; memcpy(&_data[1], _data, _size * T.sizeof); @@ -384,7 +384,7 @@ struct vector(T, A: IAllocator!T = allocator!T) if (!is(T == bool)) { /// Pushes new elements to beginning of vector. /// If `newSize > capacity` then it will `reserve(capacity * 2 + newSize + 2)` - void pushFront(T[] vals...) @nogc nothrow { + void push_front(T[] vals...) @nogc nothrow { if (_size + vals.length >= _capacity) reserve(_capacity * 2 + vals.length + 2); memcpy(&_data[vals.length], _data, _size * T.sizeof); memcpy(_data, vals.ptr, vals.length * T.sizeof); @@ -432,11 +432,11 @@ struct vector(T, A: IAllocator!T = allocator!T) if (!is(T == bool)) { if (_allocator is null) _allocator = _new!A(); if (_data !is null) { - void* newData; - newData = _allocator.reallocate(_data, _size * T.sizeof); - if (newData is null) return false; + void* new_data; + new_data = _allocator.reallocate(_data, _size * T.sizeof); + if (new_data is null) return false; import std.traits; - _data = cast(T*) newData; + _data = cast(T*) new_data; _capacity = _size; } @@ -444,7 +444,7 @@ struct vector(T, A: IAllocator!T = allocator!T) if (!is(T == bool)) { } /// Returns first element or `T.init` if `size == 0` and removes it from vector - T popFront() @nogc nothrow { + T pop_front() @nogc nothrow { if (_size == 0) return T.init; T val = _data[0]; erase(0); @@ -485,15 +485,15 @@ struct vector(T, A: IAllocator!T = allocator!T) if (!is(T == bool)) { /// Destroys vector data and sets size to 0 void clear() @nogc nothrow { if (_allocator is null) _allocator = _new!A(); - void* newData = _allocator.allocate(_capacity * T.sizeof); - if (newData is null) return; + void* new_data = _allocator.allocate(_capacity * T.sizeof); + if (new_data is null) return; if (_data !is null) { - freeData(); + free_data(); _allocator.deallocate(_data); } - _data = cast(T*) newData; + _data = cast(T*) new_data; _size = 0; } @@ -501,7 +501,7 @@ struct vector(T, A: IAllocator!T = allocator!T) if (!is(T == bool)) { void free() @nogc nothrow { if (_allocator is null) _allocator = _new!A(); if (_data !is null) { - freeData(); + free_data(); _allocator.deallocate(_data); } if (_allocator !is null) _allocator._free(); @@ -510,7 +510,7 @@ struct vector(T, A: IAllocator!T = allocator!T) if (!is(T == bool)) { _capacity = 0; } - private void freeData() @nogc nothrow { + private void free_data() @nogc nothrow { if (_data !is null) { for (size_t i = 0; i < _size; ++i) destroy!false(_data[i]); } @@ -586,7 +586,7 @@ unittest { unittest { int[4] data = [1, 2, 3, 4]; vector!int k; - k.assignCopy(data.ptr, 4); + k.assign_copy(data.ptr, 4); assert(k[0..$] == [1, 2, 3, 4]); k.free(); @@ -595,7 +595,7 @@ unittest { int* d = cast(int*) malloc(4 * int.sizeof); memcpy(d, data.ptr, 4 * int.sizeof); vector!int v; - v.assignPointer(d, 4); + v.assign_pointer(d, 4); assert(v[0..$] == [1, 2, 3, 4]); v.free(); } @@ -620,9 +620,9 @@ unittest { assert(v.back == 3); v.push(4, 5); assert(v.array == [1, 2, 3, 4, 5]); - v.pushFront(0); + v.push_front(0); assert(v.array == [0, 1, 2, 3, 4, 5]); - v.pushFront(-2, -1); + v.push_front(-2, -1); assert(v.array == [-2, -1, 0, 1, 2, 3, 4, 5]); } @@ -657,10 +657,10 @@ unittest { unittest { vector!int v = vector!int(0, 1, 2, 3, 4, 5, 6); v.pop(); - v.popFront(); + v.pop_front(); assert(v.array == [1, 2, 3, 4, 5]); v.clear(); - assert(v.popFront() == int.init); + assert(v.pop_front() == int.init); assert(v.pop() == int.init); assert(v.size == 0); }