Thread: How big should main() be?

  1. #1
    Registered User
    Join Date
    Oct 2010
    Posts
    30

    How big should main() be?

    I am familiar with the concept of breaking down a really big, complicated function into smaller functions, keeping things simpler and easier to maintain. It's all about "divide and conquer". I know how to organize classes into their own header and .cpp files--again, helping to logically compartmentalize each part of a program. Also, I personally aim to keep every function I write able to completely fit into a single screen; no scrolling required. It keeps things organized and simpler by making the code "small" (in the sense that individual parts are smaller and therefore easier to understand, modify, and so forth).

    So my question is: how big should the "main" function be? I find tons of example code, FAQs, open source repositories, and such where the "main" function is absolutely massive. I don't know if this is because of its name of "main" which people just get used to actually using as a main/central function. I don't know if this is because this is the only way to do things (as I am a very novice programmer). In the case of example code, maybe this approach lends itself towards teaching C++ more easily.

    I have never done it any other way myself--I basically follow the practices of online tutorials and textbooks. But, I think I would like to change my practices and consistently make main() a small function--able to fit in full into a single screen. Does anybody else implement this practice? If so, what is the best way to do it?

  2. #2
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    If your girlfriend giggles, it is too small.

    Soma

  3. #3
    Registered User
    Join Date
    Oct 2008
    Posts
    1,262
    This is not only a subjective question, but also one extremely dependent on what you are actually coding. If I make a simple application that takes up only several dozen lines, I might not even bother to create functions if it's obvious and simple enough. Some people only have one line - something like this:
    Code:
    ApplicationMain app(argc, argv);
    Personally, I find this dreadful; I see no added bonus of wrapping the application main into a class, it only makes it more difficult to find out what's going on.

    Generally you should keep it small "enough". Yes, as subjective as it can be, but there's nothing more to it. Whatever you feel is more readable and suitable for your application. For most of my application, that involves parsing the command line arguments (though even that might be delegated sometimes), calling a function, often wrapped in a try/catch block to catch all exceptions thrown and simply output them to std::err.

  4. #4
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Size of main() or any other function isn't that important. Complexity is.

    If it is excessively difficult to read through a function and understand what it does, then that function is too complex. Yes, that is subjective, but simpler is better.

    EVOEx's example is a case of making the function small, but making it meaningless. There is no point in having a one-line main() function, if that one line is so meaningless that it is necessary to dig through other source files to work out what that line does. [That is actually one common weakness of a lot of OO code, where lots of small functions delegate to other small functions, which makes it hard to follow what is happening].

    Generally, one needs to follow a Goldilocks-style approach (not too big, not too small, just right) where "just right" means meaningful but not unnecessarily complex.

    As a rule of thumb, try to minimise complexity of control structure in a program. Look carefully at every if() statement, loop construct, goto, thrown exception, etc to see if some way can be found to avoid doing it. If you have multiple distinct loop constructs, move each one into an (appropriately named) function of its own. If you have nested constructs (for example, nested for loops) consider moving the inner construct into a appropriately named function of its own. If your code tests a complex set of conditions, then write write a dedicated and self-contained function to do that test.

    However, don't go overboard: the goal is understandable code, not small functions. If moving a nested for loop into a separate function makes the code harder to follow - or you can't come up with a suitably descriptive function name capturing what that inner loop does - then leave it as a nested loop.

    Apart from being the entry point to a program, main() is nothing special - it is a function like any other. So it should be treated like any other function.
    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.

  5. #5
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Quote Originally Posted by phantomotap View Post
    If your girlfriend giggles, it is too small.
    or too big
    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.

  6. #6
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    'main' doesn't deserve any special rules around how large it is. One can easily make main tiny by copying the entire contents of a huge main into a different function and then only calling that. However then you simply have a large function elsewhere with a different name.
    Whatever is a good size for any other function is a good size for main also. Perhaps just no larger than the viewable height of a maximised window within the IDE.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  7. #7
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Eh, manasij7479, if you did not notice, something went seriously wrong with your code formatting.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  8. #8
    [](){}(); manasij7479's Avatar
    Join Date
    Feb 2011
    Location
    *nullptr
    Posts
    2,657
    Yes, there seems to be a bug, when the code is pasted from a previous post.. \
    (Had to manually put in whitespaces this time..perhaps the webmaster would be interested in this?)
    Trying again:

    Look at this:
    This illustrates what I learnt a few months ago in face of the same dilemma.
    A moderately large program(A Calculator , evaluating infix and rpn style input) is logically fit into the following main()
    Code:
    #include <iostream>
    #include <string>
    #include "token.h"
    #include "rpn.h"
    #include "shunt_y.h"
    using namespace std;
    int main()
    { 
        deque<token> dt;
        char q;
        string input;
        while(1)
        { 
             cout<<"Enter the expression:\n>"; 
    
             getline(cin,input); 
    
             dt = tokenize(input);  //tokenizes the input
    
             dt = infix_to_rpn(dt);  //converts it into rpn notation if it isn't already in 
    
             cout<<rpn_eval(dt)<<endl<<"Quit?\n>";   //Evaluates
             
             cin>>q;   //Loop Controls
             cin.ignore(numeric_limits<streamsize>::max(),'\n');
              if (q == 'y' )
                  break;
             else continue;
       } 
       return 0;
    }
    Last edited by manasij7479; 07-04-2011 at 08:09 PM.

  9. #9
    Registered User
    Join Date
    Oct 2008
    Posts
    1,262
    Quote Originally Posted by manasij7479 View Post
    Yes, there seems to be a bug, when the code is pasted from a previous post.. \
    (Had to manually put in whitespaces this time..perhaps the webmaster would be interested in this?)
    Trying again:

    Look at this:
    This illustrates what I learnt a few months ago in face of the same dilemma.
    A quite large program(A Calculator , evaluating infix and rpn style input) is logically fit into the following main()
    Code:
    #include <iostream>
    #include <string>
    #include "token.h"
    #include "rpn.h"
    #include "shunt_y.h"
    using namespace std;
    int main()
    { 
        deque<token> dt;
        char q;
        string input;
        while(1)
        { 
             cout<<"Enter the expression:\n>"; 
    
             getline(cin,input); 
    
             dt = tokenize(input);  //tokenizes the input
    
             dt = infix_to_rpn(dt);  //converts it into rpn notation if it isn't already in 
    
             cout<<rpn_eval(dt)<<endl<<"Quit?\n>";   //Evaluates
             
             cin>>q;   //Loop Controls
             cin.ignore(numeric_limits<streamsize>::max(),'\n');
              if (q == 'y' )
                  break;
             else continue;
       } 
       return 0;
    }
    Actually, I don't think that main is done the best possible. In this case, all I would probably do in main is a loop asking whether you want to enter another expression or not, and actually read the expression in another function.

    (Note that it is highly objective, but that's my opinion)

  10. #10
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    Main should be rather small. It should be easy to read what your program does by reading main. It should not contain much logic, mostly showing program flow. But as others have said, don't make main a one liner.

    Examples and small programs can have a large main, because dividing it into functions does not provide a significant amount of clarity, but this is not a good mold for larger programs.

    I do not think that manasij7479 code is a good general example. Except for small projects, main shouldn't have direct calls to cin and cout. Here's a better version:
    Code:
    using namespace std;
    int main()
    { 
        do { 
             string input = read_expression();
             deque<token> tokens = tokenize(input); 
             infix_to_rpn(tokens);
             double result = evaluate(tokens);
             print_result(result);
       } while( continue_prompt() );
       return 0;
    }
    Notice also how I don't need any comments.

    On a similar note, I suggest the coding practice of writing main first, unless you need a test suit.
    Last edited by King Mir; 07-04-2011 at 04:02 PM.
    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.

  11. #11
    [](){}(); manasij7479's Avatar
    Join Date
    Feb 2011
    Location
    *nullptr
    Posts
    2,657
    and actually read the expression in another function
    Except for small projects, main shouldn't have direct calls to cin and cout
    Could you explain the reasoning behind that ?..
    I thought it(doing most,if not all io in main()) helped to make all the I/O transparent so that no extra interactivity is added inside the other functions..
    Last edited by manasij7479; 07-04-2011 at 08:33 PM.

  12. #12
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    Quote Originally Posted by manasij7479 View Post
    Could you explain the reasoning behind that ?..
    I thought it(doing most,if not all io in main()) helped to make all the I/O transparent so that no extra interactivity is added inside the other functions..
    It is good to separate IO from computation, but my code does that as well as yours. You don't need to put all the IO into main, you just need to have separate functions for computation and IO.

    You want main to read like the program flow. Putting the details of how IO is done into main can interfere with that. In your example the IO of the loop control logic is not readable at a glance. Having a separate function for it with an easy interface changes that.
    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.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 3
    Last Post: 06-01-2011, 03:08 AM
  2. Why do people still use main() or main(void)?
    By Christopher2222 in forum C Programming
    Replies: 18
    Last Post: 06-06-2007, 06:34 PM
  3. Circular main <- main.o dependency dropped.
    By Queatrix in forum C++ Programming
    Replies: 4
    Last Post: 10-21-2005, 02:32 PM
  4. A question about void(main) and int(main)
    By edd1986 in forum C Programming
    Replies: 2
    Last Post: 03-05-2005, 03:18 PM
  5. void main(), int main(), argc, argv[]????
    By Jonny M in forum C Programming
    Replies: 3
    Last Post: 03-06-2002, 09:12 AM