www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - A comparison between C++ and D

reply maik klein <maikklein googlemail.com> writes:
Direct link: https://maikklein.github.io/post/CppAndD/
Reddit link: 
https://www.reddit.com/r/programming/comments/49lna6/a_comparison_between_c_and_d/

If you spot any mistakes, please let me know.
Mar 08 2016
next sibling parent reply Chris Wright <dhasenan gmail.com> writes:
On Wed, 09 Mar 2016 01:18:26 +0000, maik klein wrote:
 If you spot any mistakes, please let me know.
 structs in D don’t have a default constructor because every type
 needs exception free default construction
Rather, declaring a variable should never throw an exception. Declaring a variable shouldn't create any nontrivial work. It's trivial to create a type in D that doesn't have exception free default construction: class A { this() { throw new Exception(); } }
 C++ has a static_cast while D does not.
D has one explicit cast operator. It can do much of what static_cast does, though D does not let you downcast without a runtime check.
 Exceptions in D require the GC.
You don't need the GC; you just need storage that's not on a stack frame below where you're catching the exception. You can malloc an exception and throw it. You can allocate space for an exception in the static data region and throw it from there, like with OutOfMemoryError. You can allocate space for an exception on the stack in main() and pass it down the call chain.
 To get foo.bar.baz you can create baz.d inside the bar folder and bar
 inside the foo folder.
That's the canonical way of doing it, but with dub, I'm seeing people generally adding the project name before that. So, for instance, I have module "roguelike.levelgen" in source file "source/levelgen.d". It works.
 D can print any type at compile time or runtime with
 writeln(SomeType.stringof) or writeln(typeof(somevar).stringof)
stringof evaluates an expression or type and produces a string representation for that value. That string representation is a compile- time constant. It doesn't print it at compile time or runtime. Does writeln print values at compile time? That would be kind of strange. Maybe useful in the context of CTFE, but still a little unexpected.
Mar 08 2016
next sibling parent maik klein <maikklein googlemail.com> writes:
On Wednesday, 9 March 2016 at 02:14:34 UTC, Chris Wright wrote:
 On Wed, 09 Mar 2016 01:18:26 +0000, maik klein wrote:
 [...]
 [...]
Rather, declaring a variable should never throw an exception. Declaring a variable shouldn't create any nontrivial work. It's trivial to create a type in D that doesn't have exception free default construction: class A { this() { throw new Exception(); } }
That is why I have limited it to `structs`, but I see that I have used `every type` before which is just wrong. Not sure why I even wrote it.
 [...]
D has one explicit cast operator. It can do much of what static_cast does, though D does not let you downcast without a runtime check.
I should probably be more clear that this is about compile time.
 [...]
You don't need the GC; you just need storage that's not on a stack frame below where you're catching the exception. You can malloc an exception and throw it. You can allocate space for an exception in the static data region and throw it from there, like with OutOfMemoryError. You can allocate space for an exception on the stack in main() and pass it down the call chain.
Thanks, I did not know this. Will fix it asap.
 [...]
That's the canonical way of doing it, but with dub, I'm seeing people generally adding the project name before that. So, for instance, I have module "roguelike.levelgen" in source file "source/levelgen.d". It works.
 [...]
stringof evaluates an expression or type and produces a string representation for that value. That string representation is a compile- time constant. It doesn't print it at compile time or runtime. Does writeln print values at compile time? That would be kind of strange. Maybe useful in the context of CTFE, but still a little unexpected.
I should be more clear. I meant writeln for runtime printing and pragma(msg) for compile time printing.
Mar 08 2016
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 3/8/2016 6:14 PM, Chris Wright wrote:
 D does not let you downcast without a runtime check.
You can by casting to void* first, then the downcast.
Mar 08 2016
parent reply maik klein <maikklein googlemail.com> writes:
On Wednesday, 9 March 2016 at 04:15:39 UTC, Walter Bright wrote:
 On 3/8/2016 6:14 PM, Chris Wright wrote:
 D does not let you downcast without a runtime check.
You can by casting to void* first, then the downcast.
Thanks I'll update the post later. Does this mean I could do any cast at compile time? In encountered the problem here to give some context https://stackoverflow.com/questions/35694701/is-it-possible-to-cast-foo-to-ubytesize-at-compile-time Basically I wanted to cast some T to ubyte[size] at compile time. It seems that casting to void* is not a problem, but then upcasting from void* to ubyte[size] is still not allowed at compile time. If that is not possible maybe I can just use a union instead.
Mar 08 2016
parent Adam D. Ruppe <destructionator gmail.com> writes:
On Wednesday, 9 March 2016 at 04:37:47 UTC, maik klein wrote:
 Does this mean I could do any cast at compile time?
No, compile time has other restrictions. http://dlang.org/spec/function.html#interpretation "Any pointer may be cast to void * and from void * back to its original type. Casting between pointer and non-pointer types is prohibited. " I don't think compile time execution allows unions, either.
Mar 08 2016
prev sibling next sibling parent reply Jack Stouffer <jack jackstouffer.com> writes:
On Wednesday, 9 March 2016 at 01:18:26 UTC, maik klein wrote:
 Direct link: https://maikklein.github.io/post/CppAndD/
 Reddit link: 
 https://www.reddit.com/r/programming/comments/49lna6/a_comparison_between_c_and_d/

 If you spot any mistakes, please let me know.
 D moves objects with a bitwise copy, this means you should not 
 have internal pointers.
Unless you define this(this) right?
 version(YourKeywork){...}.
Should be "Keyword".
Mar 08 2016
next sibling parent maik klein <maikklein googlemail.com> writes:
On Wednesday, 9 March 2016 at 03:04:31 UTC, Jack Stouffer wrote:
 On Wednesday, 9 March 2016 at 01:18:26 UTC, maik klein wrote:
 Direct link: https://maikklein.github.io/post/CppAndD/
 Reddit link: 
 https://www.reddit.com/r/programming/comments/49lna6/a_comparison_between_c_and_d/

 If you spot any mistakes, please let me know.
 D moves objects with a bitwise copy, this means you should not 
 have internal pointers.
Unless you define this(this) right
I don't think so. You can read more about it here https://dlang.org/phobos/std_algorithm_mutation.html#move
 version(YourKeywork){...}.
Should be "Keyword".
Thanks not sure why this wasn't highlighted as an error.
Mar 08 2016
prev sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 3/8/16 10:04 PM, Jack Stouffer wrote:
 On Wednesday, 9 March 2016 at 01:18:26 UTC, maik klein wrote:
 Direct link: https://maikklein.github.io/post/CppAndD/
 Reddit link:
 https://www.reddit.com/r/programming/comments/49lna6/a_comparison_between_c_and_d/


 If you spot any mistakes, please let me know.
 D moves objects with a bitwise copy, this means you should not have
 internal pointers.
Unless you define this(this) right?
No. It's perfectly legal to move a struct without calling the postblit AFAIK. -Steve
Mar 10 2016
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 03/08/2016 08:18 PM, maik klein wrote:
 Direct link: https://maikklein.github.io/post/CppAndD/
 Reddit link:
 https://www.reddit.com/r/programming/comments/49lna6/a_comparison_between_c_and_d/


 If you spot any mistakes, please let me know.
Nice work, thanks! -- Andrei
Mar 09 2016
prev sibling next sibling parent reply bigsandwich <bigsandwich gmail.com> writes:
On Wednesday, 9 March 2016 at 01:18:26 UTC, maik klein wrote:
 Direct link: https://maikklein.github.io/post/CppAndD/
 Reddit link: 
 https://www.reddit.com/r/programming/comments/49lna6/a_comparison_between_c_and_d/

 If you spot any mistakes, please let me know.
C++ as well as D have anonymous functions. C++: [](auto a, auto b){ return a + b;} , D: (a, b) => a + b or (a, b){return a + b;}. As far as I know capturing other variables requires the GC in D. In C++ you can explicitly capture variables by copy, ref or move. Lambda functions in D can not return references. C++17 will also make lambda functions available with constexpr. Lambda functions can also be used at compile time in D. Is this really true? Couldn't the closure be stored internally somewhere like std::function<> does in C++?
Mar 09 2016
next sibling parent maik klein <maikklein googlemail.com> writes:
On Wednesday, 9 March 2016 at 18:26:01 UTC, bigsandwich wrote:
 On Wednesday, 9 March 2016 at 01:18:26 UTC, maik klein wrote:
 [...]
C++ as well as D have anonymous functions. C++: [](auto a, auto b){ return a + b;} , D: (a, b) => a + b or (a, b){return a + b;}. As far as I know capturing other variables requires the GC in D. In C++ you can explicitly capture variables by copy, ref or move. Lambda functions in D can not return references. C++17 will also make lambda functions available with constexpr. Lambda functions can also be used at compile time in D. Is this really true? Couldn't the closure be stored internally somewhere like std::function<> does in C++?
I think this is actually not true, but I wrote it because of this http://dpaste.dzfl.pl/dd6b935df5ce I run into this problem a couple of times.
Mar 09 2016
prev sibling next sibling parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Wednesday, 9 March 2016 at 18:26:01 UTC, bigsandwich wrote:
 Is this really true?  Couldn't the closure be stored internally 
 somewhere like std::function<> does in C++?
Lambdas in C++ creates a regular class with a call-operator, the class is instantiated and the fields filled with the "captured" variables. Isn't std::function<> just for supporting passing of lambdas etc when using separate compilation? I've never used it since I assume it is very slow. I always use function objects with a templated type in C++. But I believe D is pointing to the activation record (stack frame) and not the individual variables. So I don't think D can capturing anything by value. Or is this wrong?
Mar 09 2016
prev sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Wednesday, 9 March 2016 at 18:26:01 UTC, bigsandwich wrote:
 As far as I know capturing other variables requires the GC in D.
You can easily do it yourself with a struct. That's all the c++ lambda is anyway, a bit of syntax sugar over a struct. Then you pass the struct itself if you want the variables carried with it, or allocate it somewhere and pass a reference to the call operator as a plain delegate. Let me go into some detail: --- import std.stdio; nogc int delegate(int) dg; int helper() nogc { int a = 50; dg = (b) { return a + b; }; return dg(10); } void main() { writeln(helper()); } --- That won't compile because it wants to allocate a closure for the escaped variable, `a`. How do we get around it? While keeping the delegate: --- import std.stdio; nogc int delegate(int) dg; int helper() nogc { int a = 50; struct MyFunctor { int a; nogc this(int a) { this.a = a; } // the function itself nogc int opCall(int b) { return a+b; } } // capture a by value // WARNING: I stack allocated here but // set it to a global var, this is wrong; // it should probably be malloc'd, but since // I know I am just using it here, it is OK auto myfunc = MyFunctor(a); dg = &myfunc.opCall; return dg(10); } void main() { writeln(helper()); } --- Like the comment in there says, if you are actually going to be storing that delegate, you need to tend to the lifetime of the object somehow. That's a big reason why the GC makes this a lot *nicer* because it alleviates that concern, but it isn't *required* - you can still manage that object manually if you use the right kind of manual caution. But better is probably to forget the delegate itself (except maybe passing it to some places) and actually store that object. Then you can pass it by value or refcount it or whatever traditional technique you want. With opCall, this thing counts as functor and std.algorithm will treat it as a function too. What if you want some syntax sugar? Eeeeh, I don't have a full solution for that right now, but you could kinda do a mixin template or a string thing or something, but I think just doing the struct yourself isn't really that bad.
Mar 09 2016
next sibling parent reply bigsandwich <bigsandwich gmail.com> writes:
On Wednesday, 9 March 2016 at 20:14:13 UTC, Adam D. Ruppe wrote:
 On Wednesday, 9 March 2016 at 18:26:01 UTC, bigsandwich wrote:
 [...]
You can easily do it yourself with a struct. That's all the c++ lambda is anyway, a bit of syntax sugar over a struct. [...]
Right, I used to this sort of thing in C++ prior to C++11. I think not having an RAII wrapper for lambdas similar to std::function<> is an oversight for D, especially for people averse to GC. That little bit of syntactic sugar makes a huge difference.
Mar 09 2016
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Wednesday, 9 March 2016 at 20:41:35 UTC, bigsandwich wrote:
 Right,  I used to this sort of thing in C++ prior to C++11.  I 
 think not having an RAII wrapper for lambdas similar to 
 std::function<> is an oversight for D, especially for people 
 averse to GC.  That little bit of syntactic sugar makes a huge 
 difference.
std::function<> is a lot more than syntactic sugar, it is a runtime-heavy construct. From http://en.cppreference.com/w/cpp/utility/functional/function : «Class template std::function is a general-purpose polymorphic function wrapper. Instances of std::function can store, copy, and invoke any Callable target -- functions, lambda expressions, bind expressions, or other function objects, as well as pointers to member functions and pointers to data members.» I don't think you want this...
Mar 09 2016
parent reply bigsandwich <bigsandwich gmail.com> writes:
On Wednesday, 9 March 2016 at 22:05:28 UTC, Ola Fosheim Grøstad 
wrote:
 On Wednesday, 9 March 2016 at 20:41:35 UTC, bigsandwich wrote:
 Right,  I used to this sort of thing in C++ prior to C++11.  I 
 think not having an RAII wrapper for lambdas similar to 
 std::function<> is an oversight for D, especially for people 
 averse to GC.  That little bit of syntactic sugar makes a huge 
 difference.
std::function<> is a lot more than syntactic sugar, it is a runtime-heavy construct. From http://en.cppreference.com/w/cpp/utility/functional/function : «Class template std::function is a general-purpose polymorphic function wrapper. Instances of std::function can store, copy, and invoke any Callable target -- functions, lambda expressions, bind expressions, or other function objects, as well as pointers to member functions and pointers to data members.» I don't think you want this...
Yes, I do. std::function<> uses type erasure to store a "function". If its small enough, its stored internally, otherwise it goes on the heap. It uses RAII to manage the lifetime of the lambda. D is using the GC for managing the lifetime. D doesn't have a way of doing this without the GC.
Mar 09 2016
next sibling parent Atila Neves <atila.neves gmail.com> writes:
On Wednesday, 9 March 2016 at 22:54:13 UTC, bigsandwich wrote:
 On Wednesday, 9 March 2016 at 22:05:28 UTC, Ola Fosheim Grøstad 
 wrote:
 [...]
Yes, I do. std::function<> uses type erasure to store a "function". If its small enough, its stored internally, otherwise it goes on the heap. It uses RAII to manage the lifetime of the lambda. D is using the GC for managing the lifetime. D doesn't have a way of doing this without the GC.
D can do it too, there's just no syntactic sugar for it. Atila
Mar 09 2016
prev sibling parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Wednesday, 9 March 2016 at 22:54:13 UTC, bigsandwich wrote:
 Yes, I do.  std::function<> uses type erasure to store a 
 "function".  If its small enough, its stored internally, 
 otherwise it goes on the heap.
Yes, I believe that is said to be the common implementation, but people also claim it comes with significant overhead. I haven't seen any numbers, have you seen any benchmarks on the difference between a templated "C++ functor" on a parameter and std::function<>?
 It uses RAII to manage the lifetime of the lambda.  D is using 
 the GC for managing the lifetime.  D doesn't have a way of 
 doing this without the GC.
I completely agree that D needs a lot of work to get the GC out of the way. I personally don't think std::function<> is doing the right thing.
Mar 10 2016
prev sibling parent reply John Colvin <john.loughran.colvin gmail.com> writes:
On Wednesday, 9 March 2016 at 20:14:13 UTC, Adam D. Ruppe wrote:
 ---
 import std.stdio;

  nogc int delegate(int) dg;

 int helper()  nogc {
 	int a = 50;

 	struct MyFunctor {
 		int a;

 		 nogc this(int a) { this.a = a; }

 		// the function itself
 		 nogc int opCall(int b) { return a+b; }
 	}

 	// capture a by value
         // WARNING: I stack allocated here but
         // set it to a global var, this is wrong;
         // it should probably be malloc'd, but since
         // I know I am just using it here, it is OK
 	auto myfunc = MyFunctor(a);

 	dg = &myfunc.opCall;

 	return dg(10);
 }

 void main() {
 	writeln(helper());
 }
typeof(&myfunc.opCall) == int delegate(int b) nogc what magic is this? I had no idea that taking the address of opCall would give me a delegate.
Mar 09 2016
parent Adam D. Ruppe <destructionator gmail.com> writes:
On Thursday, 10 March 2016 at 00:31:00 UTC, John Colvin wrote:
 what magic is this? I had no idea that taking the address of 
 opCall would give me a delegate.
opCall is a red herring: taking the address of *any* non-static member function in a class or struct object will give you a delegate. (Addresses of static member function will give you a function pointer, and so will taking the address of ClassName.memberName. But classObject.memberName is a delegate because there is a this pointer attached.)
Mar 09 2016
prev sibling parent Namespace <rswhite4 gmail.com> writes:
On Wednesday, 9 March 2016 at 01:18:26 UTC, maik klein wrote:
 Direct link: https://maikklein.github.io/post/CppAndD/
 Reddit link: 
 https://www.reddit.com/r/programming/comments/49lna6/a_comparison_between_c_and_d/

 If you spot any mistakes, please let me know.
I'm missing the wildcard modifier inout (even if it's a ugly name), which inherits constness automatically. That is really awesome.
Mar 09 2016