0

What is the cleanest way to write a function (not a procedure) ?

Does the 2nd solution is told to have "side effect" ?

struct myArea
{
  int t[10][10]; // it could by 100x100...
};

Solution 1 : pass by value

 double mySum1(myArea a)
 {
   // compute and return the sum of elements
 }

Solution 2 : pass by const reference

double mySum2(const myArea & a)
{
  // compute and return the sum of elements
}

My prefered is the first one (clean function) although it is less effective. But when there are a lot of data to copy, it can be time-consuming.

Thank you for feedback.

Patrick Dezecache
  • 225
  • 1
  • 2
  • 9

3 Answers3

2

I have a number of quibbles with your terminology:

  1. There's no such thing as a "procedure" in C or C++. At best, there are functions that return no value: "void"

  2. Your example has no "side effect".

  3. I'm not sure what you mean by "clean function" ... but I HOPE you don't mean "less source == cleaner code". Nothing could be further from the truth :(

TO ANSWER YOUR ORIGINAL QUESTION:

  1. In your example, double mySum1(myArea a) incurs the space and CPU overhead of a COMPLETELY UNNECESSARY COPY. Don't do it :)

  2. To my mind, double mySum1(myArea & a) or double mySum1(myArea * a) are equivalent. Personally, I'd prefer double mySum1(myArea * a) ... but most C++ developers would (rightly!) prefer double mySum1(myArea & a).

  3. double mySum1 (const myArea & a) is best of all: it has the runtime efficiency of 2), and it signals your intent that it WON'T modify the array.

PS: I generated assembly output from the following test:

struct myArea {
  int t[10][10];
};

double mySum1(myArea a) {
  double sum = 0.0;
  for (int i=0; i < 10; i++)
    for (int j=0; j<10; j++)
      sum += a.t[i][j];
  return sum;
}

double mySum2(myArea & a) {
  double sum = 0.0;
  for (int i=0; i < 10; i++)
    for (int j=0; j<10; j++)
      sum += a.t[i][j];
  return sum;
}

double mySum3(myArea * a) {
  double sum = 0.0;
  for (int i=0; i < 10; i++)
    for (int j=0; j<10; j++)
      sum += a->t[i][j];
  return sum;
}

double mySum4(const myArea & a) {
  double sum = 0.0;
  for (int i=0; i < 10; i++)
    for (int j=0; j<10; j++)
      sum += a.t[i][j];
  return sum;
}

mySum1, as you'd expect, had extra code to do the extra copy.

The output for mySum2, mySum3 and mySun4, however, were IDENTICAL:

_Z6mySum2R6myArea:
.LFB1:
    .cfi_startproc
    .cfi_personality 0x3,__gxx_personality_v0
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    pushq   %rbx
    movq    %rdi, -32(%rbp)
    movl    $0, %eax
    movq    %rax, -24(%rbp)
    movl    $0, -16(%rbp)
    jmp .L8
    .cfi_offset 3, -24
.L11:
    movl    $0, -12(%rbp)
    jmp .L9
.L10:
    movl    -16(%rbp), %eax
    movl    -12(%rbp), %edx
    movq    -32(%rbp), %rcx
    movslq  %edx, %rbx
    movslq  %eax, %rdx
    movq    %rdx, %rax
    salq    $2, %rax
    addq    %rdx, %rax
    addq    %rax, %rax
    addq    %rbx, %rax
    movl    (%rcx,%rax,4), %eax
    cvtsi2sd    %eax, %xmm0
    movsd   -24(%rbp), %xmm1
    addsd   %xmm1, %xmm0
    movsd   %xmm0, -24(%rbp)
    addl    $1, -12(%rbp)
.L9:
    cmpl    $9, -12(%rbp)
    setle   %al
    testb   %al, %al
    jne .L10
    addl    $1, -16(%rbp)
.L8:
    cmpl    $9, -16(%rbp)
    setle   %al
    testb   %al, %al
    jne .L11
    movq    -24(%rbp), %rax
    movq    %rax, -40(%rbp)
    movsd   -40(%rbp), %xmm0
    popq    %rbx
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
  <= mySum3 and mySum4 had different labels ... but identical instructions!

It's also worth noting that one of the benefits of "const" is that it can help the compiler perform several different kinds of optimizations, whenever possible. For example:

Community
  • 1
  • 1
paulsm4
  • 114,292
  • 17
  • 138
  • 190
  • i mean the cleanest is the closest of mathematics, so the more readable...(not in term of source code length)...but i understand that in professional programming, efficiency is the top priority... – Patrick Dezecache Feb 08 '16 at 20:15
  • @PatrickDezecache - efficiency is not necessarily the top priority. The problem is that wanting to write code that's "the closest [to] mathematics" is like trying to write English that's the closest to Russian. Native speakers will, at best, think that your accent is odd, and, at worst, won't understand you. – Pete Becker Feb 08 '16 at 20:44
  • @PeteBecker : i feel that declaring a function "f(r) = 3.14r2" is quite close to "double f(r) {return 3.14*r*r;]", isn't it ? ... sometimes it's a bit less evident i agree... – Patrick Dezecache Feb 08 '16 at 21:03
1

Please note there is no such thing as "procedure" in C++. Functions that don't return anything are still functions.

Now to the question: If your parameter is an output parameter or an input/output parameter, that is, you want the caller to see changes made inside the function to the object passed to it, then pass by reference. Otherwise if the type is small/trivially cheap to copy, pass by value. Otherwise, pass by reference to const. In your case I'd pass by reference to const.

Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434
  • yes but, the way we use non-void functions has nothing to do with void functions... – Patrick Dezecache Feb 08 '16 at 19:58
  • @PatrickDezecache There's nothing special about void functions other than that they don't return a value. There's no difference in the rules regarding parameters. –  Feb 08 '16 at 20:03
  • @hvd ok but function should not be able to have a look outside the box (only local data and local parameter values). – Patrick Dezecache Feb 08 '16 at 20:08
1

Parameter-passing is not a side effect in itself.

If a function does anything observable that isn't just returning a value, that would be a side effect.
(For example modifying a reference parameter, printing something, modifying any global state...)

That is, even if you pass by non-const reference, the presence of side effects depends on whether you modify the referenced object.

molbdnilo
  • 64,751
  • 3
  • 43
  • 82
  • yes but, with a non-const reference, you allow the function to potentially modify source parameters. – Patrick Dezecache Feb 08 '16 at 20:05
  • @PatrickDezecache Just because a function can _potentially_ have side effects, that doesn't mean it _always_ does. It only has side effects if it has an effect on something other than what it's primarily intended to affect, regardless of whether it has the potential to affect anything else or not. – Justin Time - Reinstate Monica Feb 08 '16 at 20:12
  • @PatrickDezecache Even if you pass all parameters by value, a function can modify globals or write to files, which are side effects. You can't tell from a function's parameters whether it has any side effects. – molbdnilo Feb 08 '16 at 20:37
  • @molbdnilo : i think a clean function (non-void) show only access his own data, parameters (const), that's all. No global data, no read and write of anything...like in the mathematical way... – Patrick Dezecache Feb 08 '16 at 20:44