www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Some thoughts about C and D, return data through parameters

reply cosinus <dummy dummy.dummy> writes:
Recently I've asked my self why `C` isn't capable of returning 
multiple values at once. And I thought that the return-statement 
was primarally used only for error-handling and all the valuable 
data has been returned through the parameter-list.
If this was true then all `C`-like languages had abused the 
return-statement till  now.

This is the way most programmers are doing it:

```C
int add(int a, int b);
// ...
int c = add(a, b);
```

This is the way I think `C` was designed to:

```C
int add(int *c, int a, int b);
// ...
int c;
if(add(&c, a, b)) {
     printf("error!");
}
```

This isn't good example but think about how you are doing it with 
huge structs or even arrays.

I think the main reason why most people are using the first 
example is because it looks more like a function in math or you 
need less code to call the function or we think the 
parameter-list is for inputs only.
But the second one is faster especially with huge junks of data. 
I think a lot of unnecessary allocations has been done just to be 
able to call the function like the first example. Think about 
`std::string` in c++.

So my question is would it be a good idea to have some kind of 
implicit declarations of variables that are used as parameters:

```D
int add(decl ref int c, int a, int b);

// ...

// c is unknown here

if(add(c, 123, 456)) {
     writeln("error!");
}
// c is implicit declared at the function-call above.
assert(c == 579);
```

The good things out of this are:

* The function becomes easier to be called
* The variable `c` does not need to be default-initialized to 
keep it save
* It's like the `auto`-declaration we can already use to declare 
a variable that keeps the return-value
* It solves the problem with multiple return-values.

A second thought that came up was:
Shouldn't there be a compiler-error if someone is ignoring the 
return-value of a function?

I saw this C-code:

```C
(void)printf("Hello World!");
```

It cast's the return-value to void to tell the compiler and other 
programmer's that the return-value can be ignored.
Dec 18 2017
next sibling parent reply Petar Kirov [ZombineDev] <petar.p.kirov gmail.com> writes:
On Tuesday, 19 December 2017 at 01:01:57 UTC, cosinus wrote:
 Recently I've asked my self why `C` isn't capable of returning 
 multiple values at once. And I thought that the 
 return-statement was primarally used only for error-handling 
 and all the valuable data has been returned through the 
 parameter-list.
 If this was true then all `C`-like languages had abused the 
 return-statement till  now.

 This is the way most programmers are doing it:

 ```C
 int add(int a, int b);
 // ...
 int c = add(a, b);
 ```

 This is the way I think `C` was designed to:

 ```C
 int add(int *c, int a, int b);
 // ...
 int c;
 if(add(&c, a, b)) {
     printf("error!");
 }
 ```

 This isn't good example but think about how you are doing it 
 with huge structs or even arrays.

 I think the main reason why most people are using the first 
 example is because it looks more like a function in math or you 
 need less code to call the function or we think the 
 parameter-list is for inputs only.
 But the second one is faster especially with huge junks of 
 data. I think a lot of unnecessary allocations has been done 
 just to be able to call the function like the first example. 
 Think about `std::string` in c++.

 So my question is would it be a good idea to have some kind of 
 implicit declarations of variables that are used as parameters:

 ```D
 int add(decl ref int c, int a, int b);

 // ...

 // c is unknown here

 if(add(c, 123, 456)) {
     writeln("error!");
 }
 // c is implicit declared at the function-call above.
 assert(c == 579);
 ```

 The good things out of this are:

 * The function becomes easier to be called
 * The variable `c` does not need to be default-initialized to 
 keep it save
 * It's like the `auto`-declaration we can already use to 
 declare a variable that keeps the return-value
 * It solves the problem with multiple return-values.

 [snip]
Reminds me of C#7's out variable declarations: https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7#out-variables However multiple return values are much better implemented through language-integrated tuples: https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7#tuples
Dec 19 2017
parent Nick Treleaven <nick geany.org> writes:
On Tuesday, 19 December 2017 at 08:54:38 UTC, Petar Kirov 
[ZombineDev] wrote:
 Reminds me of C#7's out variable declarations: 
 https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7#out-variables

 However multiple return values are much better implemented 
 through language-integrated tuples: 
 https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7#tuples
In D we don't have tuple unpacking syntax, but if we supported inline variable declarations for out arguments, we could do it with a library function: void unpack(T, A...)(T tuple, out A args); tuple(4, false).unpack(auto first, auto second);
Dec 21 2017
prev sibling next sibling parent Petar Kirov [ZombineDev] <petar.p.kirov gmail.com> writes:
On Tuesday, 19 December 2017 at 01:01:57 UTC, cosinus wrote:
 [snip]

 A second thought that came up was:
 Shouldn't there be a compiler-error if someone is ignoring the 
 return-value of a function?

 I saw this C-code:

 ```C
 (void)printf("Hello World!");
 ```

 It cast's the return-value to void to tell the compiler and 
 other programmer's that the return-value can be ignored.
Ignoring the return value is mainly useful when the primary use of the function are it's side effects, not it's return value. In many languages derived from C, people prefer using 'void' return types + exceptions for error handling. It is an error to ignore the result of function with no side effects (D models such functions with the 'pure' attribute): https://dlang.org/spec/function.html#pure-functions
Dec 19 2017
prev sibling parent reply ketmar <ketmar ketmar.no-ip.org> writes:
cosinus wrote:

 So my question is would it be a good idea to have some kind of implicit 
 declarations of variables that are used as parameters:

 ```D
 int add(decl ref int c, int a, int b);

 // ...

 // c is unknown here

 if(add(c, 123, 456)) {
      writeln("error!");
 }
 // c is implicit declared at the function-call above.
 assert(c == 579);
 ```
D already has one such construct, `foreach`. and it is a huge misdesign. it is *absolutely* not clear from the code that `c` is introduced by a function call. exactly like with `foreach (c; ...)`, any sane person expects that just using variable name without any type prepended means "let's use already declared variable `c`". the ONLY place where this uniformity is broken now is `foreach`, and this is already bad. note that D devs stubbornly insists that `auto` is not a type placeholder, but a storage qualifier, like `static`, so you can't write `foreach (auto c; ...)` (and compiler rejects such code). yet, `if (auto c = ...)` is perfectly allowed, and here `auto` is type placeholder. talking about consistency, yeah. anyway, adding yet another broken promise (aka illogical inconsistency) into language won't do any good. each inconsistency makes code harder to read and understand.
Dec 19 2017
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 12/19/17 6:27 AM, ketmar wrote:

 note that D devs stubbornly insists that `auto` is not a type 
 placeholder, but a storage qualifier, like `static`, so you can't write 
 `foreach (auto c; ...)` (and compiler rejects such code). yet, `if (auto 
 c = ...)` is perfectly allowed, and here `auto` is type placeholder. 
 talking about consistency, yeah.
I insist that auto is a storage qualifier, AND I think it should be allowed as the storage qualifier for the foreach variable. foreach(ref c; ...) works. I don't see why foreach(auto c; ...) shouldn't. -Steve
Dec 19 2017
parent reply ketmar <ketmar ketmar.no-ip.org> writes:
Steven Schveighoffer wrote:

 I insist that auto is a storage qualifier
what is the rationale to have `auto` as storage qualifier? except of keeping it in line with the ancient C feature (and most C programmers don't even know what `auto` does in C). i bet that most people are sure that `auto` in D is a type placeholder. it works like type placeholder, it looks like type placeholder, why don't make it a type placeholder? ok, i can see *one* place where it won't be consistent: `foo (auto ref Type v)`. which can be left as a logical exception ('cmon, `enum a = 20;`) is not declaring an enum too, it is used to declare "inline constant". also: auto int n = 42; yay: "Error: variable n storage class 'auto' has no effect if type is not inferred, did you mean 'scope'?" even D compiler knows that `auto` is used as *type* *placeholder*. 'cmon, let's promote it to actual type placeholder!
Dec 19 2017
parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 12/19/17 9:35 AM, ketmar wrote:
 Steven Schveighoffer wrote:
 
 I insist that auto is a storage qualifier
what is the rationale to have `auto` as storage qualifier? except of keeping it in line with the ancient C feature (and most C programmers don't even know what `auto` does in C).
Because it's consistent. D says that types are inferred when the type is missing and there is a storage class specified. Making auto a storage class fits this definitely perfectly.
 
 i bet that most people are sure that `auto` in D is a type placeholder. 
 it works like type placeholder, it looks like type placeholder, why 
 don't make it a type placeholder?
It could be specified that way, but then it's another thing to explain, which doesn't add any value. The spec can read "The type of a variable is inferred from the initializer if you specify a storage class" or it can read "The type of a variable is inferred from the initializer if you specify a storage class, or if you use 'auto' as the type placeholder." I don't see a gain there.
 ok, i can see *one* place where it won't be consistent: `foo (auto ref 
 Type v)`. which can be left as a logical exception ('cmon, `enum a = 
 20;`) is not declaring an enum too, it is used to declare "inline 
 constant".
auto ref, while a nice feature, is a horrible syntax. It's an exception no matter what we do!
 
 also:
 
      auto int n = 42;
 
 yay: "Error: variable n storage class 'auto' has no effect if type is 
 not inferred, did you mean 'scope'?" >
 even D compiler knows that `auto` is used as *type* *placeholder*. 
 'cmon, let's promote it to actual type placeholder!
Another inconsistency that should be fixed (and allowed). -Steve
Dec 19 2017