Thread: Passing many dozens of arguments to a function in C++

  1. #1
    Registered User FortranLevelC++'s Avatar
    Join Date
    May 2013
    Location
    United States
    Posts
    81

    Passing many dozens of arguments to a function in C++

    The C++ functions that I have been designing often receive many dozens of arguments that are control parameters with complicated data structures, and this is in addition to the data that will be processed by these functions.

    So I will appreciate some philosophical advice about the sort of organizational strategy experienced C++ programmers would use in such contexts.

    Since the numerical data is often larger than many gigabytes, destined to approach hundreds of gigabytes in size, I have already been storing all data in binary files in the hard disk, and one of the "arguments" that the function receives is the name of the data file. The functions efficiently read the basic numerical data from the hard drive accordingly, but separately, there are still many dozens of control parameters that must be passed to the function as arguments. Even declaring these parameters as global variables to reduce the number of arguments that the functions receive, is beginning to cause unexpected difficulties, because in order to make this work, I still need to create many data structures to store the parameters in such a way that each function will know which parameters to use ( vectors of vectors of many different types (the types being various structs, etc, like vector < vector <Type1> > , where Type1 is a struct which might have to contain vectors also.)

    Right now, one remedy I am about to try is to stop trying to make the main program read and organize the control parameters from the disk file that stores the control parameters, and instead, to let each function read its own control parameters from the original text file buried in the hard disk. This would make the data structures of the control parameters a lot simpler, since each function will only have to read its own parameters, so that instead of the main program having to construct a vector of vectors like vector < vector<Type1> > , each function will internally need to use a only one dimensional vector of the form vector<Type1> (even if Type1 is in fact a structure that might contain a vector of Type2 among other things, this is still a lot simpler to initialize, fill and access, since the complexity of having to store simultaneously all the control parameters of all functions is eliminated.) This is because there are many functions which get organized in a vector of functions or a map of functions, and this method of making each function read its control parameters directly from the disk file will make the complexity of the data structures simpler. In fact, maybe for parallel programming to let all functions work independently, this trick of letting them read their control parameters from a disk file might also work.

    Any suggestions will be appreciated. Basically, the situation is more complicated than passing dozens of arguments of the same type or simple types.
    Last edited by FortranLevelC++; 06-05-2013 at 11:21 PM.

  2. #2
    Registered User
    Join Date
    May 2010
    Posts
    4,632
    Have you considered a class or structure to hold these "many dozens of arguments"?

    It seems like maybe you're trying to do too much in your functions, think about more smaller functions. Try breaking the problem into smaller more manageable pieces.

    Don't rely on global variables, they may seem to work at first but they'll make your program brittle and much harder to maintain in future versions.

    Jim

  3. #3
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Code:
    struct ControlParameters
    {
         // your dozens of control parameters here
    };
    
    void YourFunction(const std::string &filename,  ControlParameters &parameters)
    {
        // do whatever is needed, in nice, small, well-defined pieces, including separate supporting functions.
    }
    If the function does not need to modify and return the control parameters, the second argument can be const.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  4. #4
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Your parameters most likely can be placed into some kind of hierarchical representation. That is, some of them are probably related to each other, and some of them could be seen as "children" of other parameters. Realize these relationships in code. Match the domain, instead of flattening it into a mind-numbing list of numbers.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  5. #5
    Registered User rogster001's Avatar
    Join Date
    Aug 2006
    Location
    Liverpool UK
    Posts
    1,472
    of all the published software that has been written - obviously many of it with massive complexity - how much of it do you imagine was / is finally implemented with functions that require(d) dozens of arguments? That must tell you something.
    Last edited by rogster001; 06-06-2013 at 02:40 PM.
    Thought for the day:
    "Are you sure your sanity chip is fully screwed in sir?" (Kryten)
    FLTK: "The most fun you can have with your clothes on."

    Stroustrup:
    "If I had thought of it and had some marketing sense every computer and just about any gadget would have had a little 'C++ Inside' sticker on it'"

  6. #6
    Registered User FortranLevelC++'s Avatar
    Join Date
    May 2013
    Location
    United States
    Posts
    81
    Quote Originally Posted by grumpy View Post
    Code:
    struct ControlParameters
    {
         // your dozens of control parameters here
    };
    
    void YourFunction(const std::string &filename,  ControlParameters &parameters)
    {
        // do whatever is needed, in nice, small, well-defined pieces, including separate supporting functions.
    }
    If the function does not need to modify and return the control parameters, the second argument can be const.

    Many thanks for the suggestion, and initially this was what I tried to do, but now I realize that this will be difficult also, because of the fact that there will be many functions in the inventory (currently many dozens, but destined to grow to hundreds of functions), and I have already decided to construct a map of functions to select only a few dozens of functions out of hundreds that will be called by the main program (this subset of functions is also part of the control parameters written in a text file.)

    And because I will have many functions, I would have to create a vector whose elements are of the type "struct ControlParameters" as you wrote, thus the arguments would be stored in:
    vector < ControlParameters > vec_ControlParameters; // for various functions


    Thus the structure of the program will essentially look like this kind of code:

    Code:
    int main()
    {
         TestChosenFunctions( ); 
    }
    
    
    void TestChosenFunctions( )
    {                     
      
      GetControlFunctionNamesFromDiskFile(); 
    
        SetUpMapOfFunctionsToActualFunctions();
    
       // put one separate struct of the form ControlParameters into  vec_ControParameters for each function:
        PrepareVectorOfControlParameters( vec_ControlParameters);
     
    
    
    
        cout << "We selectively call only those functions whose names were written in the text file:" << endl;
        for( auto itr=vec_NamesOfFunctions.begin(), end=vec_NamesOfFunctions.end() ; itr != end; ++itr)
        {    
            size_t Index = itr - vec_NamesOfFunctions.begin();
    
     
             if( Map_NamesOfFunctionsToActualFunctions.count(*itr) != 0 )
             {
                  Map_NamesOfFunctionsToActualFunctions[ *itr ]( vec_ControlParameters[Index] )  ;
             }
             else
            {
                 cout << "Error: Function name missing from the map!" << endl;
            }
        }
        cout << "------------------------------------" << endl;
    
    
     }
    However, it seems to me that this method will be a little difficult to organize in practice, because the struct of each function is already complicated (it already contains vectors of control parameters, and so we will end up creating a vector which is essentially composed of sub-vectors of various types, a vector of vectors! And both the filling and accessing of vectors of vectors (2D vectors) is a little trickier than filling and accessing arrays. For example it would essentially look like this:

    Code:
    vector < vector<int> >vec;
    
    for (int i=0; i<10; i++) {
      vec.push_back(vector<int>())// initialize
    }
    
    for(int j=0; j<20; j++) {
      for( int i=0; i<vec.size(); i++) {
        vec[i].push_back( struct (i, j,), etc, etc, );
      }
    } /// It will be difficult to organize and debug this kind of double vector, as the structs have vectors in them!!
    So what solution would you choose?

    1) It seems to me that one way to reduce the complexity of the data is to avoid preparing all the control parameters together at the beginning for all the functions together, and to decentralize a little bit, to prepare only one struct ControlParameters at a time in real time, just before the function call inside the for loop that iterates over the chosen functions. This would reduce the dimension of the vector from 2 to 1, ending up only with one struct which contains one-dimensional vectors, making it easier to organize.

    2) Another method that I am even more inclined to choose right now, is to abandon preparing these ControlParameters arguments altogether, and to let EACH function read ONLY its parameters that it needs from the original disk file that contains the parameters. This would make the code even more robust and easier to maintain. Do you agree that this crude method is robust and easier to maintain. This is very unorthodox C++ programming, but it seems to me that it requires less work, although each of the functions will have to read the control file once, adding a few lines of extra code to each function, it seems that this is still easier...

  7. #7
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    Many thanks for the suggestion, and initially this was what I tried to do, but now I realize that this will be difficult also, because of the fact that there will be many functions in the inventory (currently many dozens, but destined to grow to hundreds of functions)
    That is very odd. Why so many? Functions are always supposed to do something; they are not countless points of data that you need to structure and refine information from.

    I would like to know what you are really doing as opposed to what your broken idea is.

  8. #8
    Registered User rogster001's Avatar
    Join Date
    Aug 2006
    Location
    Liverpool UK
    Posts
    1,472
    Any suggestions will be appreciated. Basically, the situation is more complicated than passing dozens of arguments of the same type or simple types.
    I agree with whiteflags - Just what exactly is the 'situation' ? If you describe the program goal - tell us what your application is supposed to do and offer some relevant code you will allow people to better understand and offer more accurate advice.
    Thought for the day:
    "Are you sure your sanity chip is fully screwed in sir?" (Kryten)
    FLTK: "The most fun you can have with your clothes on."

    Stroustrup:
    "If I had thought of it and had some marketing sense every computer and just about any gadget would have had a little 'C++ Inside' sticker on it'"

  9. #9
    Registered User FortranLevelC++'s Avatar
    Join Date
    May 2013
    Location
    United States
    Posts
    81
    Quote Originally Posted by whiteflags View Post
    That is very odd. Why so many? Functions are always supposed to do something; they are not countless points of data that you need to structure and refine information from.

    I would like to know what you are really doing as opposed to what your broken idea is.
    Basically, deciding to build many dozens of functions is just my primitive way of making sure that no function will have more than 1,000 lines of code, or else I won't be able to debug it in general: In fact, the average function has only 500 lines. Each function makes calculations, sometimes solves various differential and integral equations by approximate means, and tries to find patterns in the data by these means. By decentralizing and then comparing the results of many alternative calculation methods, one can build the entire project step by step, with only small setbacks along the way.

    Anyway, a central theme in all these functions, is to make calculations in many sub-ranges of data, and in many scales. For instance, although the functions use significantly different methods of calculating, each function uses at least the following control parameters that are initially written in a text file (the text file is either manually modified or it gets modified by means of a GUI interface):

    Code:
    1) // The binary random access file name and the key file name that provides the information about 
        // how the data was partitioned.
    
    2) // TestTime; // a time location t=T_0 within the available range of time in data
    
    3)  //Each function needs the time scale information as follows:
        vector<IntervalInfo> vec_Intervals; 
        // where IntervalInfo is of the form:
         // struct IntervalInfo {
            // int WidthOfInterval; // in discrete time units
            // int LocationOfInterval; // distance of the interval's (right or left) from origin
           //  vector<int> vec_DegreeOfPolynomial;
        }; 
        // Thus each of the time intervals (which may be nested or overlapping 
        // or disjoint), will have several polynomials of chosen degrees that will be 
        // applied to the data to extract a pattern in that interval.
         // (Usually least square polynomials.)
    
    4)     vector<ReferenceTrajectory> vec_ReferenceTrajectories;
            //where ReferenceTrajectory is of the form:
             // vector<double> ReferenceTrajectory;
            // and a ReferenceTrajectory is just a time-dependent curve in time in 
            //the  data file, which is pre-determined either from experiment or theory.
    So far, these 4 kinds of control parameters above, are for each function, but since often each function will use different a different configuration of control parameters, this means that a different argument will be passed to each function, even though the format of the arguments is similar. This means that since the dozens of functions were already organized in a map of functions (to make it possible to iterate over these functions), it follows that I will have to prepare the above 4 arguments just before the function call.

    Otherwise, if I read and prepare all the control parameters ahead of time before the dozens of functions are called in iteration, then I would have to build an even bigger vector which contains elements that are made of structs that contain the above 4 kinds of control parameters in a different configuration for each function.

    Of course, right now, in order to make the code more robust and decentralized, I am about to abandon preparing the types 3) and 4) arguments for the functions: instead I am going to let each function read its own parameters from the disk file independently. This will avoid trouble, and it will also avoid using global variables.

    But as you can see, even if I prepare the control parameter arguments separately for each function, I already have do construct and then decipher two-dimensional vectors already, and this is somewhat tricky.

    In any case, I must admit that the C++ book of Alex Allain has been extremely helpful: this book is full of very practical examples that are chosen in such a way that I often instantly find a way of using more advanced C++ methods to organize calculations I was unable to do before. I bought the latter book a few months ago and it dramatically improved my programming. (Even though I have accumulated two dozen C++ books during the last two decades, I have never used these in any serious way, always programming with for loops in general.)
    Last edited by FortranLevelC++; 06-07-2013 at 10:53 PM.

  10. #10
    Registered User
    Join Date
    May 2003
    Posts
    1,619
    Quote Originally Posted by FortranLevelC++ View Post
    Basically, deciding to build many dozens of functions is just my primitive way of making sure that no function will have more than 1,000 lines of code, or else I won't be able to debug it in general: In fact, the average function has only 500 lines.
    Holy crap. Granted, one of the two languages I primarily use is C# which in general is somewhat more terse than C++, but I aim to keep things below about 20 lines of code and a very long method would be around 100 LOC.

    Why do you need to read methods from a file? The whole idea smells a bit of the inner-platform effect, which is a common antipattern in software development. If you actually need to iterate over a lot of methods, I'd set this up as functors (aka function objects) all which derive from an ABC and organize your classes logically into namespaces, while refactoring any shared tasks into separate functions, perhaps in a base class. You can iterate over a vector of functors rather than iterating over a list of strings from a disk file.

    Also if you're doing a lot of complex math, you might simply try something more suited to that, like Maple, Mathematica, or Matlab. If you need to do a front-end and don't want to do it in their languages, all of the above I believe have DLLs you can use with C or C++.
    Last edited by Cat; 06-07-2013 at 11:28 PM.
    You ever try a pink golf ball, Wally? Why, the wind shear on a pink ball alone can take the head clean off a 90 pound midget at 300 yards.

  11. #11
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    O_o

    Okay.

    So, totally serial, why aren't you going to approach the design as has been explained?

    I've read the thread; it seems like you don't want to follow good design patterns because "It will be difficult."!?

    Who told you that programming was going to be anything other than difficult? I'd like to know just so that I may avoid such a clearly deranged individual.

    Anyway, you clearly need to centralize access to the configuration data. If your building a test harness for algorithmic statistics gathering, as it seems, passing all the configuration independently is extreme, wasteful, and foolish. You would not even need to know exactly what configuration data each function needs to use it!

    You don't even have to parse the data in advance if you don't want to build the configuration structure in advance.

    Code:
    // adapted from the code posted by grumpy
    class SControlParameters
    {
        SControlParameters
        (
            const std::string & fFilename
        );
        // ...
        std::string queryValue
        (
            const std::string & fLabel
        )
        {
            // ...
            return(parsed_value);
        }
        // ...
    };
     
    void YourFunction(const std::string &filename,  SControlParameters &parameters)
    {
        // do whatever is needed, in nice, small, well-defined pieces, including separate supporting functions.
    }
    That said, it sounds like you are in desperate need of a primer on functional decomposition and function composition because it sounds as if you've tried and failed horribly to get algorithmic polymorphisms.

    I don't know what you are doing exactly, but consider that each method of calculation can be a yield and each method of comparison can be provided these strategies through nested composition by client routines or tables. You don't have to build a specific function for each combination of comparison strategy, comparison property, and method of solution. You are already providing the data to a function as parameters, just go ahead and provide the method of determining patterns to functions as well.

    Yeah, this is an extreme example, but the point remains, if you have many dozens of functions ultimately only gluing components together, you can rather well simply glue them together as part of an expression.

    Soma

    Code:
    std::complex<double> SolveDifferentialEquation_DivideConquer(/*FIXED_INTERFACE*/);
    std::complex<double> SolveDifferentialEquation_RecursiveAppliance(/*FIXED_INTERFACE*/);
    // ...
    class SYield
    {
        class Iterator
        {
            // ...
        };
        SYield(/**/);
        // ...
    };
    // ...
    typedef /**/ FuzzyComparisonValue;
    // ...
    FuzzyComparisonValue Compare_Precision
    (
        SYield::Iterator fOriginType1
      , SYield::Iterator fTerminusType1
      , SYield::Iterator fOriginType2
      , SYield::Iterator fTerminusType2
    );
    FuzzyComparisonValue Compare_Distance
    (
        SYield::Iterator fOriginType1
      , SYield::Iterator fTerminusType1
      , SYield::Iterator fOriginType2
      , SYield::Iterator fTerminusType2
    )
    {
        FuzzyComparisonValue sResult;
        while(fOriginType1 != fTerminusType1) && (fOriginType2 != fTerminusType2))
        {
            // Compare *fOriginType1 with *fOriginType2 and accumulate the results.
        }
        return(sResult);
    }
    // ...
    FuzzyComparisonValue HarnessFuzzyResults(/**/);
    // ...
    {
        // ...
        std::vector<std::complex<double> > sData(parameters.loadData("dataname"));
        SYield sDivideConquer(SolveDifferentialEquation_DivideConquer, sData);
        SYield sRecursiveAppliance(SolveDifferentialEquation_RecursiveAppliance, sData);
        FuzzyComparisonValue sDifferentialEquation(HarnessFuzzyResults(Compare_Precision, sDivideConquer, sRecursiveAppliance));
        // ...
    }

  12. #12
    Registered User rogster001's Avatar
    Join Date
    Aug 2006
    Location
    Liverpool UK
    Posts
    1,472
    Basically, deciding to build many dozens of functions is just my primitive way of making sure that no function will have more than 1,000 lines of code, or else I won't be able to debug it in general
    I have never heard so much bollocks in my life - from what programming planet did you come.

    By decentralizing and then comparing the results of many alternative calculation methods, one can build the entire project step by step, with only small setbacks along the way
    yet more waffle

    In fact, the average function has only 500 lines.
    Oh thats ok then, i take it all back, haha, good grief, spit it out man - what are you trying to make?
    Last edited by rogster001; 06-08-2013 at 01:34 AM.
    Thought for the day:
    "Are you sure your sanity chip is fully screwed in sir?" (Kryten)
    FLTK: "The most fun you can have with your clothes on."

    Stroustrup:
    "If I had thought of it and had some marketing sense every computer and just about any gadget would have had a little 'C++ Inside' sticker on it'"

  13. #13
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    I have never heard so much bollocks in my life - from what programming planet did you come.
    O_o

    So... not a fan of "Fox News"?

    Soma

  14. #14
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    Quote Originally Posted by grumpy View Post
    Code:
    struct ControlParameters
    {
         // your dozens of control parameters here
    };
    
    void YourFunction(const std::string &filename,  ControlParameters ¶meters)
    {
        // do whatever is needed, in nice, small, well-defined pieces, including separate supporting functions.
    }
    If the function does not need to modify and return the control parameters, the second argument can be const.
    This is going half way to where it needs to. It's probably better to do this (depending on the details of what the OP is trying to do):

    Code:
    struct NameForWhatFunctionDoes{
         // your dozens of control parameters here
        void NameForWhatFunctionDoes::YourFunction(const std::string &filename);
    };
    
    void NameForWhatFunctionDoes::YourFunction(const std::string &filename)
    {
        // function body
    }
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  15. #15
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    This is going half way to where it needs to. It's probably better to do this (depending on the details of what the OP is trying to do):
    O_o

    Okay.

    I realize you added the disclaimer, but I wonder, how is the alternative "half way" or this "better"?

    You've only taken a structure which exists solely to pack context used by multiple functions and thrown it into a class serving to bind a specific function to such context.

    Sure, you've eliminated an explicit parameter, but you've now bound a specific function to context requiring the creation of as many classes as existing functions.

    Am I reading too much into the names? Was this just a poorly named example?

    Code:
    struct ContextSet1{
         // your dozens of control parameters here
        void aFunction(const std::string &filename);
        // ...
        void zFunction(const std::string &filename);
    };
     
    void ContextSet1::aFunction(const std::string &filename)
    {
        // function body
    }
    
    void ContextSet1::zFunction(const std::string &filename)
    {
        // function body
    }
    Maybe?

    *shrug*

    I hope you were intending more than a class per function relationship, and also that I was reading too much into the names.

    Soma

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. passing arguments to a function
    By cfanatic in forum C Programming
    Replies: 2
    Last Post: 07-01-2012, 06:50 AM
  2. Help passing arguments to function
    By HAssan in forum C Programming
    Replies: 2
    Last Post: 11-26-2007, 02:15 PM
  3. need function help, passing arguments
    By infernosnow in forum Game Programming
    Replies: 18
    Last Post: 07-18-2006, 02:45 AM
  4. Passing arguments to function...
    By alvifarooq in forum C++ Programming
    Replies: 8
    Last Post: 09-24-2004, 12:50 PM