Click here to Skip to main content
15,887,853 members
Articles / Programming Languages / C++

Parameter Passing: by Train vs. by Truck

Rate me:
Please Sign up or sign in to vote.
3.09/5 (11 votes)
19 Jul 2010CPOL4 min read 50.4K   8   31
An article highlighting the advantages of using a composite datatype for passing parameters to functions.

Introduction

This article describes the advantages and disadvantages of the form or format that is used for passing parameters to functions. The useful technique described in this article can be used in languages like C, C++, as well as in any language which supports pointers and composite data-types like struct.

Ways of parameter passing to a function

Conceptually, parameters can be passed to a function in two ways, including the combination of these two:

  1. by State: This way of parameter passing specifies whether the modifications made to parameters by the called function will be available to the caller of the function. Thus, this manner of parameter passing focuses on the state\ value of parameters, before and after the function call.
  2. Under this, there are different actual techniques\ methods to pass parameters to a function:

    • by Value: Modification made to parameters by the called function cannot be seen by the caller of the function.
    • C++
      //------ called function ------//
      int func(int P1, char P2)
      {
          P1 = 20;  //...tried to modify
          P2 = 'b'; //...tried to modify
          
          return 1;
      }
      //------ called function ------//
      
      
      //-------- caller code --------//
      //original data
      int Data_P1 = 10;
      char Data_P2 = 'a';
      
      func(Data_P1, Data_P2);
      
      //Data_P1 = 10 ...not modified, since passed by Value
      cout << "Data_P1 = " << Data_P1 << endl;
      //Data_P2 = a  ...not modified, since passed by Value
      cout << "Data_P2 = " << Data_P2 << endl;
      //-------- caller code --------//
    • by Reference: Modification made to the parameters by the called function can be seen by the caller of the function.
    • C++
      //------ called function ------//
      int func(int &P1, char *P2)
      {
          P1 = 20;   //...tried to modify
          *P2 = 'b'; //...tried to modify
          
          return 1;
      }
      //------ called function ------//
      
      //-------- caller code --------//
      //original data
      int Data_P1 = 10;
      char Data_P2 = 'a';
      
      func(Data_P1, &Data_P2);
      
      //Data_P1 = 20 ...modified, since passed by Reference
      cout << "Data_P1 = " << Data_P1 << endl;
      //Data_P2 = b  ...modified, since passed by Reference
      cout << "Data_P2 = " << Data_P2 << endl;
      //-------- caller code --------//
    • There are other techniques as well like by Name etc., the description of which can be easily found on the Internet.
  3. by Format: This way of passing parameters, by Format, is not something new. In fact, it is used in routine development. This way of parameter passing specifies whether the parameters are passed to a function individually (horizontally) or collectively (vertically).
  4. Therefore, the word Format in the current discussion can be horizontally or vertically.

    Under this, there are different actual techniques\methods to pass parameters to a function:

    • by Train: Passing parameters individually (horizontally) or in horizontal format can be imagined as if parameters are passed in the form of a train, since a train carries its load (coaches\ bogies) one after the other in horizontal format.
    • by Truck: Passing parameters collectively (vertically) or in vertical format can be imagined as if parameters are passed in the form of a truck, since a truck carries its load by keeping things one above the other in vertical format.

Let's see this "by Format" parameters passing method in detail with its pros and cons.

Parameter passing "by train"

parapass_1.png

C++
int func(int P1, float P2, abc P3, char P4, xyz P5);

As can be seen from the image, there are a number of parameters that are passed to the function func one after the other, horizontally in the form of a Train, and hence the name "parameter passing by train".

Disadvantages

  1. When a parameter in the function parameter list gets added, deleted, or modified, the function signature gets modified.
    1. If parameter P1's data-type gets modified from int to float, the function signature gets modified.
    2. C++
      int func(/*int*/ float P1, float P2, abc P3, char P4, xyz P5);
      //...signature is modified
    3. If parameter P2 gets deleted, the function signature gets modified.
    4. C++
      int func(int P1, /*float P2,*/ abc P3, char P4, xyz P5);
      //...signature is modified
    5. If a new parameter P6 gets added to the parameter list, the function signature gets modified
    6. C++
      int func(int P1, float P2, abc P3, char P4, xyz P5, pqr P6);
      //...signature is modified

Advantages

  1. It is easy to pass some parameters by Value and some parameters by Reference.
  2. C++
    //...passing P1 by Value
    //...passing P2 by Reference
    //...passing P3 by Value
    //...passing P4 by Reference
    //...passing P5 by Value
    
    int func(int P1, float &P2, abc P3, char *P4, xyz P5);

Parameter passing "by truck"

parapass_2.png

C++
int func(PARAMETERTRUCK stParam);

//where PARAMETERTRUCK is defined as :
struct PARAMETERTRUCK
{
    int P1;
    float P2;
    abc P3;
    char P4;
    xyz P5;
};

As can be seen from the image, only one (composite\ container-like) parameter is passed to the function func. This passed parameter can be considered as a Truck which loads all the parameters in it, hence the name "parameter passing by truck".

Advantages

  1. Even if a parameter in the function parameter list gets added, deleted, or modified, the function signature does not get modified.
    1. The parameter P1's data-type gets modified from int to float, but the function signature does not get modified.
    2. C++
      struct PARAMETERTRUCK
      {
          /*int*/ float P1;
          float P2;
          abc P3;
          char P4;
          xyz P5;
      };
      
      int func(PARAMETERTRUCK stParam); //...signature not modified
    3. The parameter P2 gets deleted, but the function signature does not get modified.
    4. C++
      struct PARAMETERTRUCK
      {
          int P1;
          /*float P2;*/
          abc P3;
          char P4;
          xyz P5;
      };
      
      int func(PARAMETERTRUCK stParam); //...signature not modified
    5. A new parameter P6 gets added to the parameter list, but the function signature does not get modified.
    6. C++
      struct PARAMETERTRUCK
      {
          int P1;
          float P2;
          abc P3;
          char P4;
          xyz P5;
          pqr P6;
      };
      
      int func(PARAMETERTRUCK stParam); //...signature not modified

Disadvantages

  1. All of the parameters can be passed either by Value or by Reference.
  2. C++
    int func(PARAMETERTRUCK stParam);  //by Value
    int func(PARAMETERTRUCK &stParam); //by Reference
    int func(PARAMETERTRUCK *stParam); //by Reference

We can overcome this disadvantage and allow the mixture of passing some parameters by Value and some parameters by Reference.

Let's see how:

C++
//------ called function ------//
//Define the Parameter Truck as follows :
struct PARAMETERTRUCK
{
    int   P1;  //...parameter that needs to be passed by value
    char *P2;  //...use pointer for parameter
               //   that needs to be passed by Reference
};

//... always passing Parameter Truck "by Value".
//... still preventing the function signature from getting modified.
int func(PARAMETERTRUCK stParam) 
{
    stParam.P1 = 20;   //...tried to modify
    *stParam.P2 = 'b'; //...tried to modify
    
    return 1;
}
//------ called function ------//


//-------- caller code --------//
//original data
int Data_P1 = 10;
char Data_P2 = 'a';

PARAMETERTRUCK stData;
stData.P1 = Data_P1;    //...pass by Value
stData.P2 = &(Data_P2); //...pass by Reference

func(stData);

//continue using original data some of which 
//are modified in function func as per requirement

//Data_P1 = 10 ...not modified, since passed by Value
cout << "Data_P1 = " << Data_P1 << endl;
//Data_P2 = b  ...modified, since passed by Reference
cout << "Data_P2 = " << Data_P2 << endl;
//-------- caller code --------//

Conclusion

By using the technique of passing parameters by truck, we can prevent the signature of a function from getting modified when its parameter list gets modified. At the same time, we can pass some parameters by Value and some parameters by Reference through the use of pointers.

Therefore, use a horizontal list of parameters the least, and pass parameters using a struct, i.e., "Truck".

There is one strong reason to support the above line. Since, good programming practices (Single Responsibility Principle) suggest that an entity ("function" in our case) should be responsible for perform only one task\ cohesive functionality, all the parameters that will be received by a function should participate or contribute towards the goal\ functionality which the function is trying to provide. Therefore, it is better to pass all the parameters under one name\ goal.

History

  • Version 1.0: Initial uploading of article.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Engineer
India India
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
General[My vote of 1] Obfuscation at its Best Pin
JeffBilkey30-Jul-10 19:29
JeffBilkey30-Jul-10 19:29 
GeneralMy vote of 2 Pin
S.H.Bouwhuis26-Jul-10 22:31
S.H.Bouwhuis26-Jul-10 22:31 
GeneralRe: My vote of 2 Pin
Sameerkumar Namdeo27-Jul-10 19:42
Sameerkumar Namdeo27-Jul-10 19:42 
GeneralMy vote of 2 Pin
Emile van Gerwen26-Jul-10 22:25
Emile van Gerwen26-Jul-10 22:25 
GeneralRe: My vote of 2 Pin
Sameerkumar Namdeo27-Jul-10 18:34
Sameerkumar Namdeo27-Jul-10 18:34 
GeneralParameter Passing [modified] Pin
geoyar26-Jul-10 13:10
professionalgeoyar26-Jul-10 13:10 
GeneralRe: Parameter Passing Pin
Member 336656726-Jul-10 17:01
Member 336656726-Jul-10 17:01 
GeneralRe: Parameter Passing Pin
Sameerkumar Namdeo26-Jul-10 19:05
Sameerkumar Namdeo26-Jul-10 19:05 
GeneralRe: Parameter Passing Pin
geoyar27-Jul-10 13:13
professionalgeoyar27-Jul-10 13:13 
GeneralRe: Parameter Passing Pin
Sameerkumar Namdeo27-Jul-10 18:23
Sameerkumar Namdeo27-Jul-10 18:23 
JokeRe: Parameter Passing Pin
geoyar28-Jul-10 9:12
professionalgeoyar28-Jul-10 9:12 
GeneralRe: Parameter Passing Pin
Sameerkumar Namdeo29-Jul-10 2:00
Sameerkumar Namdeo29-Jul-10 2:00 
GeneralMy vote of 4 Pin
Aescleal22-Jul-10 2:44
Aescleal22-Jul-10 2:44 
GeneralMy vote of 1 Pin
Tom G. Huang20-Jul-10 5:30
Tom G. Huang20-Jul-10 5:30 
GeneralRe: My vote of 1 Pin
Sameerkumar Namdeo20-Jul-10 22:26
Sameerkumar Namdeo20-Jul-10 22:26 
GeneralRe: My vote of 1 Pin
Tom G. Huang21-Jul-10 0:06
Tom G. Huang21-Jul-10 0:06 
GeneralRe: My vote of 1 Pin
Sameerkumar Namdeo21-Jul-10 19:55
Sameerkumar Namdeo21-Jul-10 19:55 
Question[My vote of 2] Perhaps the article doesn't go far enough? Pin
Aescleal20-Jul-10 2:56
Aescleal20-Jul-10 2:56 
AnswerRe: [My vote of 2] Perhaps the article doesn't go far enough? Pin
Sameerkumar Namdeo20-Jul-10 21:51
Sameerkumar Namdeo20-Jul-10 21:51 
GeneralRe: [My vote of 2] Perhaps the article doesn't go far enough? Pin
Aescleal21-Jul-10 0:39
Aescleal21-Jul-10 0:39 
I wasn't saying that at all - I was saying if you're collecting data into structures and there are common operations on those structures bind them together. Your discussion was all around currying data into functions. This even shows in the example you've given. If I'd have seen that I'd be asking what was the thing that had a name and age that the register and unregister function were taking.

class Lab
{
    public:
        int Register(string Name, int Age);
        int Unregister(string Name, int Age);
};

makes me ask Name of what? Age of what? If you said they're the names and ages of students signing up for a lab course at a university:

class lab_course
{
    public:
        int register_student( string name_of_student, int age_of_student );
        int unregister_student( string name_of_student, int age_of_student );
};

this is just saying "Oooo, missing abstraction, rewrite me! rewrite me!":

class student
{
    public:
        student( const std::string &name, unsigned age );

        // Other bits according to what students can do
};

class lab_course
{
    public:
        int register( student to_register );
        int unregister( student to_unregister );
};

If it's samples in a analytical lab or something else I can't imagine at the mo, you can follow the same pattern. The lab then acts on the student and doesn't maul about the raw bits of a student.

As a complete asside, you can now wrap registrations up into their own class if the fact of registration is important to you (say you wanted the scope of registration to be rollable back automatically in the face of an exception):

class course
{
    public:
        virtual int register( student to_register );
        virtual int unregister( student to_unregister );
};

class lab_course : public course
{
    public:
        int register( student to_register );
        int unregister( student to_unregister );
};

class course_registration
{
    public:
        course_registration( course *c, student *s ) : c_( c ), s_( s )
        {
            c_->register( *s_ );
        }

        ~course_registration()
        {
            if( c_ && s_ ) c_->unregister( *s_ );
        }

        void finalise_registration()
        {
            c_ = 0; s_ = 0;
        }

    private:
        course *c_;
        student *s_;
};

and you'd use it along the lines of:

bool try_registration_for_course( course *c, student *s )
{
    course_registration provisional_registration( c, s );

    // Do much good stuff to finalise the registration...

    if( finalised ) provisional_registration.finalise();
}

This might be overkill for this type of system but for ones that have some financial sensitivity (or things like locking threading primitives) it can be really handy.

Cheers,

Ash
GeneralRe: [My vote of 2] Perhaps the article doesn't go far enough? Pin
Sameerkumar Namdeo21-Jul-10 19:49
Sameerkumar Namdeo21-Jul-10 19:49 
GeneralToo much emphasis on keeping signatures the same without giving a strong rationale Pin
Marcus Schommler19-Jul-10 21:52
Marcus Schommler19-Jul-10 21:52 
GeneralRe: Too much emphasis on keeping signatures the same without giving a strong rationale Pin
Sameerkumar Namdeo20-Jul-10 22:50
Sameerkumar Namdeo20-Jul-10 22:50 
General[My vote of 2] Not quite true Pin
Richard MacCutchan19-Jul-10 4:12
mveRichard MacCutchan19-Jul-10 4:12 
GeneralRe: [My vote of 2] Not quite true Pin
Rick York19-Jul-10 6:59
mveRick York19-Jul-10 6:59 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.