开发者

What goes into main function?

I am looking for a best practice tip of what goes into the main function of a program using c++. Currently I think two approaches are possible. (Although the "margins" of those approaches can be arbitrarily close to each other)

1: Write a "Master"-class that receives the parameters passed to the main function and handle the complete program in that "Master"-class (Of course you also make use of other classes). Therefore the main function would be reduced to a minimum of lines.

#include "MasterClass.h"
int main(int args, char* argv[])
{
MasterCla开发者_StackOverflow中文版ss MC(args, argv);
}

2: Write the "complete" program in the main function making use of user defined objects of course! However there are also global functions involved and the main function can get somewhat large.

I am looking for some general guidelines of how to write the main function of a program in c++. I came across this issue by trying to write some unit test for the first approach, which is a little difficult since most of the methods are private.


Why would you have one master class? What is its single area of responsibility?

"Master" or "application" classes tend to become big blobs that do way too many different things. Ultimately, what's the point? What did it buy you?

Why not use the main function to provide this main functionality? Define the high-level application lifecycle in main. It takes some command line arguments and parses them (preferably by delegating this out to another function or class), and then it calls some setup functionality, and then it might enter some kind of main loop, before doing some cleanup. All in all, this might give you a main function of perhaps 10-15 lines, but probably not more than that. It should delegate out to other classes and functions as much as possible. so main itself is kept short and sweet, but still has a purpose.

Putting this kind of super high-level flow in main means it's easy to find, because main is your starting point anyway. It is where you're going to start looking if you want to understand the code. So put in it what a reader wants to know when trying to understand the code.

Sure, you could take all that and put it in a "main class", and you'd have gained absolutely nothing, other than satisfying all the Java-luddites out there who feel that "everything must be in a class".


You are describing two "extreme" approaches, neither of which sounds right to me. Neither having a single God Class, nor having a single God Function is the right way to implement any nontrivial program intended for real use.

It can be OK to have a single call to MasterClass within your main() (although I would prefer partitioning functionality better, e.g. doing any command line-specific processing in main(), to decouple MasterClass from the details of command line parameters). However, if that class is hard to unit test, it is a sign of a design problem, and usually the solution is to delegate part or all of its testable functionality to other classes where it can be easily unit tested in separation, via public interfaces.

Your second approach can again be problematic to unit test, so you should strive to extract methods from it (and then preferably move them into a separate class) to make fine grained unit tests possible.

The sweet spot where you want to be is thus somewhere between the two extremes, constrained by the requirement of making your code testable.

It is worth thinking about how to structure your programs in general, not just in context of main(). The basic idea is to partition it into "chunks" (classes and methods) which are

  • small enough to be understood, tested and maintained easily,
  • logically cohesive.


I would do the analysis of the arguments of the process in the main routine, then create a class instance by passing more readable parameters than argc and argv.


First of: I rarely use C++, but I assume this is not really a language specific issue.
Well, I guess this boils down to taste aside from some practicality issues. I personally tend to use layout #1, but don't put the commandline parsing routines in the MasterClass. For me, that clearly belongs into the main. The MasterClass should get the parsed arguments (integers, FileStreams, whatever).


Usually I call a main function (whith the same signature) in my application's namespace:

namespace current_application {
    int main( int argc, char **argv ) {
        // ...
    }
}
int main( int argc, char **argv ) {
    return current_application::main( argc, argv );
}

And then I usually use my actual main (the one in the namespace) to initialize application wise things:

  • set locales on standard input/output/error)

  • parse application parameters

  • instantiate on object of my main class, if present (the equivalent of something like QApplication)

  • call the main function, if present (the equivalent of something like QApplication::run)

and usually prefer to add a try catch block there in order to print more debugging info in case of crash.


All of this is highly subjective, however; it's part of your coding style.


Your first approach is very common, although the class tends to be named 'Application' (or at least contain the word 'Application') so do that.


If an exception doesn't have a handler, then it's unspecified whether destructors of local objects are called before std::terminate.

If you want to make that behavior predictable, then main is a good place to have a top-most exception handler, with some kind of reporting.

Usually that's all that I put in main, which otherwise just calls a cppMain... ;-)

Cheers & hth.,


Typically, I perform necessary platform-specific setup action and then delegate to an object. There's little advantage of an object over a function, but objects can inherit from interfaces which is good if you have say platform-independent libraries that use interface implementation for callbacks.


I prefer an IOC (Inversion of Control) approach to my programs.

My "main" therefore uses the command arguments to determine the "options" and the "configuration" of the program. By options I mean interpreting certain flags passed in the command-line and by configuration I mean loading of configuration files.

The object it uses to load the configuration files then has a command to "run" but what is run (if anything) depends also on command-line arguments.


The example you have given just moves the main function inside a namespace. I don't see the advantage here. A middle of the road approach with a slight tendency towards the Master Class model works for me. The Master Class object would usually be huge and best created on the heap. I have the main function create the the object and handle any errors that might occur here.

class MasterClass {
public:
static MasterClass* CreateInstance( int argc, char **argv );
    // ...
}

int main(int argc, char** argv)
{
    try
    {
         MasterClass mc = MC::CreateInstance(argc, argv);
    }
    catch(...)
    {
        // ...
    }
}

This also has the advantage that any processing which does not have anything to do with the actual program logic, such as, reading the environment, etc does not have to be put in the MasterClass. They can go in main(). A good example is a server add-in task for the Lotus Domino system. Here the task should only run when control is handed over to it by the Domino scheduler task. Here the main will probably look as below:

STATUS LNPUBLIC AddInMain(HMODULE hModule, int argc, char far *argv[])
{
     MasterClass mc = MC::CreateInstance(argc, argv);
     while(/* wait for scheduler to give control */)
     {
          STATUS s = mc->RunForTimeSlice();
          if (s != SUCCESS)
               break;
     }
     // clean up
}

All the logic for interacting with the scheduler is thus in main and the rest of the program does not have to handle any of it.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜