Dlaczego main () powinien być krótki?


87

Programuję od ponad 9 lat i zgodnie z radą mojego pierwszego nauczyciela programowania zawsze utrzymuję main()bardzo krótką funkcję.

Na początku nie miałem pojęcia, dlaczego. Po prostu posłuchałem bez zrozumienia, ku zadowoleniu moich profesorów.

Po zdobyciu doświadczenia zdałem sobie sprawę, że jeśli poprawnie zaprojektowałem swój kod, posiadanie krótkiej main()funkcji po prostu się wydarzyło. Pisanie modularnego kodu i przestrzeganie zasady pojedynczej odpowiedzialności pozwoliło na zaprojektowanie mojego kodu w „pęczkach” i main()służyło jedynie jako katalizator do uruchomienia programu.

Szybko do przodu kilka tygodni temu, patrzyłem na kod Souce Pythona i znalazłem main()funkcję:

/* Minimal main program -- everything is loaded from the library */

...

int
main(int argc, char **argv)
{
    ...
    return Py_Main(argc, argv);
}

Tak, python. Krótka main()funkcja == Dobry kod.

Nauczyciele programowania mieli rację.

Chcąc przyjrzeć się głębiej, spojrzałem na Py_Main. W całości jest on zdefiniowany w następujący sposób:

/* Main program */

int
Py_Main(int argc, char **argv)
{
    int c;
    int sts;
    char *command = NULL;
    char *filename = NULL;
    char *module = NULL;
    FILE *fp = stdin;
    char *p;
    int unbuffered = 0;
    int skipfirstline = 0;
    int stdin_is_interactive = 0;
    int help = 0;
    int version = 0;
    int saw_unbuffered_flag = 0;
    PyCompilerFlags cf;

    cf.cf_flags = 0;

    orig_argc = argc;           /* For Py_GetArgcArgv() */
    orig_argv = argv;

#ifdef RISCOS
    Py_RISCOSWimpFlag = 0;
#endif

    PySys_ResetWarnOptions();

    while ((c = _PyOS_GetOpt(argc, argv, PROGRAM_OPTS)) != EOF) {
        if (c == 'c') {
            /* -c is the last option; following arguments
               that look like options are left for the
               command to interpret. */
            command = (char *)malloc(strlen(_PyOS_optarg) + 2);
            if (command == NULL)
                Py_FatalError(
                   "not enough memory to copy -c argument");
            strcpy(command, _PyOS_optarg);
            strcat(command, "\n");
            break;
        }

        if (c == 'm') {
            /* -m is the last option; following arguments
               that look like options are left for the
               module to interpret. */
            module = (char *)malloc(strlen(_PyOS_optarg) + 2);
            if (module == NULL)
                Py_FatalError(
                   "not enough memory to copy -m argument");
            strcpy(module, _PyOS_optarg);
            break;
        }

        switch (c) {
        case 'b':
            Py_BytesWarningFlag++;
            break;

        case 'd':
            Py_DebugFlag++;
            break;

        case '3':
            Py_Py3kWarningFlag++;
            if (!Py_DivisionWarningFlag)
                Py_DivisionWarningFlag = 1;
            break;

        case 'Q':
            if (strcmp(_PyOS_optarg, "old") == 0) {
                Py_DivisionWarningFlag = 0;
                break;
            }
            if (strcmp(_PyOS_optarg, "warn") == 0) {
                Py_DivisionWarningFlag = 1;
                break;
            }
            if (strcmp(_PyOS_optarg, "warnall") == 0) {
                Py_DivisionWarningFlag = 2;
                break;
            }
            if (strcmp(_PyOS_optarg, "new") == 0) {
                /* This only affects __main__ */
                cf.cf_flags |= CO_FUTURE_DIVISION;
                /* And this tells the eval loop to treat
                   BINARY_DIVIDE as BINARY_TRUE_DIVIDE */
                _Py_QnewFlag = 1;
                break;
            }
            fprintf(stderr,
                "-Q option should be `-Qold', "
                "`-Qwarn', `-Qwarnall', or `-Qnew' only\n");
            return usage(2, argv[0]);
            /* NOTREACHED */

        case 'i':
            Py_InspectFlag++;
            Py_InteractiveFlag++;
            break;

        /* case 'J': reserved for Jython */

        case 'O':
            Py_OptimizeFlag++;
            break;

        case 'B':
            Py_DontWriteBytecodeFlag++;
            break;

        case 's':
            Py_NoUserSiteDirectory++;
            break;

        case 'S':
            Py_NoSiteFlag++;
            break;

        case 'E':
            Py_IgnoreEnvironmentFlag++;
            break;

        case 't':
            Py_TabcheckFlag++;
            break;

        case 'u':
            unbuffered++;
            saw_unbuffered_flag = 1;
            break;

        case 'v':
            Py_VerboseFlag++;
            break;

#ifdef RISCOS
        case 'w':
            Py_RISCOSWimpFlag = 1;
            break;
#endif

        case 'x':
            skipfirstline = 1;
            break;

        /* case 'X': reserved for implementation-specific arguments */

        case 'U':
            Py_UnicodeFlag++;
            break;
        case 'h':
        case '?':
            help++;
            break;
        case 'V':
            version++;
            break;

        case 'W':
            PySys_AddWarnOption(_PyOS_optarg);
            break;

        /* This space reserved for other options */

        default:
            return usage(2, argv[0]);
            /*NOTREACHED*/

        }
    }

    if (help)
        return usage(0, argv[0]);

    if (version) {
        fprintf(stderr, "Python %s\n", PY_VERSION);
        return 0;
    }

    if (Py_Py3kWarningFlag && !Py_TabcheckFlag)
        /* -3 implies -t (but not -tt) */
        Py_TabcheckFlag = 1;

    if (!Py_InspectFlag &&
        (p = Py_GETENV("PYTHONINSPECT")) && *p != '\0')
        Py_InspectFlag = 1;
    if (!saw_unbuffered_flag &&
        (p = Py_GETENV("PYTHONUNBUFFERED")) && *p != '\0')
        unbuffered = 1;

    if (!Py_NoUserSiteDirectory &&
        (p = Py_GETENV("PYTHONNOUSERSITE")) && *p != '\0')
        Py_NoUserSiteDirectory = 1;

    if ((p = Py_GETENV("PYTHONWARNINGS")) && *p != '\0') {
        char *buf, *warning;

        buf = (char *)malloc(strlen(p) + 1);
        if (buf == NULL)
            Py_FatalError(
               "not enough memory to copy PYTHONWARNINGS");
        strcpy(buf, p);
        for (warning = strtok(buf, ",");
             warning != NULL;
             warning = strtok(NULL, ","))
            PySys_AddWarnOption(warning);
        free(buf);
    }

    if (command == NULL && module == NULL && _PyOS_optind < argc &&
        strcmp(argv[_PyOS_optind], "-") != 0)
    {
#ifdef __VMS
        filename = decc$translate_vms(argv[_PyOS_optind]);
        if (filename == (char *)0 || filename == (char *)-1)
            filename = argv[_PyOS_optind];

#else
        filename = argv[_PyOS_optind];
#endif
    }

    stdin_is_interactive = Py_FdIsInteractive(stdin, (char *)0);

    if (unbuffered) {
#if defined(MS_WINDOWS) || defined(__CYGWIN__)
        _setmode(fileno(stdin), O_BINARY);
        _setmode(fileno(stdout), O_BINARY);
#endif
#ifdef HAVE_SETVBUF
        setvbuf(stdin,  (char *)NULL, _IONBF, BUFSIZ);
        setvbuf(stdout, (char *)NULL, _IONBF, BUFSIZ);
        setvbuf(stderr, (char *)NULL, _IONBF, BUFSIZ);
#else /* !HAVE_SETVBUF */
        setbuf(stdin,  (char *)NULL);
        setbuf(stdout, (char *)NULL);
        setbuf(stderr, (char *)NULL);
#endif /* !HAVE_SETVBUF */
    }
    else if (Py_InteractiveFlag) {
#ifdef MS_WINDOWS
        /* Doesn't have to have line-buffered -- use unbuffered */
        /* Any set[v]buf(stdin, ...) screws up Tkinter :-( */
        setvbuf(stdout, (char *)NULL, _IONBF, BUFSIZ);
#else /* !MS_WINDOWS */
#ifdef HAVE_SETVBUF
        setvbuf(stdin,  (char *)NULL, _IOLBF, BUFSIZ);
        setvbuf(stdout, (char *)NULL, _IOLBF, BUFSIZ);
#endif /* HAVE_SETVBUF */
#endif /* !MS_WINDOWS */
        /* Leave stderr alone - it should be unbuffered anyway. */
    }
#ifdef __VMS
    else {
        setvbuf (stdout, (char *)NULL, _IOLBF, BUFSIZ);
    }
#endif /* __VMS */

#ifdef __APPLE__
    /* On MacOS X, when the Python interpreter is embedded in an
       application bundle, it gets executed by a bootstrapping script
       that does os.execve() with an argv[0] that's different from the
       actual Python executable. This is needed to keep the Finder happy,
       or rather, to work around Apple's overly strict requirements of
       the process name. However, we still need a usable sys.executable,
       so the actual executable path is passed in an environment variable.
       See Lib/plat-mac/bundlebuiler.py for details about the bootstrap
       script. */
    if ((p = Py_GETENV("PYTHONEXECUTABLE")) && *p != '\0')
        Py_SetProgramName(p);
    else
        Py_SetProgramName(argv[0]);
#else
    Py_SetProgramName(argv[0]);
#endif
    Py_Initialize();

    if (Py_VerboseFlag ||
        (command == NULL && filename == NULL && module == NULL && stdin_is_interactive)) {
        fprintf(stderr, "Python %s on %s\n",
            Py_GetVersion(), Py_GetPlatform());
        if (!Py_NoSiteFlag)
            fprintf(stderr, "%s\n", COPYRIGHT);
    }

    if (command != NULL) {
        /* Backup _PyOS_optind and force sys.argv[0] = '-c' */
        _PyOS_optind--;
        argv[_PyOS_optind] = "-c";
    }

    if (module != NULL) {
        /* Backup _PyOS_optind and force sys.argv[0] = '-c'
           so that PySys_SetArgv correctly sets sys.path[0] to ''
           rather than looking for a file called "-m". See
           tracker issue #8202 for details. */
        _PyOS_optind--;
        argv[_PyOS_optind] = "-c";
    }

    PySys_SetArgv(argc-_PyOS_optind, argv+_PyOS_optind);

    if ((Py_InspectFlag || (command == NULL && filename == NULL && module == NULL)) &&
        isatty(fileno(stdin))) {
        PyObject *v;
        v = PyImport_ImportModule("readline");
        if (v == NULL)
            PyErr_Clear();
        else
            Py_DECREF(v);
    }

    if (command) {
        sts = PyRun_SimpleStringFlags(command, &cf) != 0;
        free(command);
    } else if (module) {
        sts = RunModule(module, 1);
        free(module);
    }
    else {

        if (filename == NULL && stdin_is_interactive) {
            Py_InspectFlag = 0; /* do exit on SystemExit */
            RunStartupFile(&cf);
        }
        /* XXX */

        sts = -1;               /* keep track of whether we've already run __main__ */

        if (filename != NULL) {
            sts = RunMainFromImporter(filename);
        }

        if (sts==-1 && filename!=NULL) {
            if ((fp = fopen(filename, "r")) == NULL) {
                fprintf(stderr, "%s: can't open file '%s': [Errno %d] %s\n",
                    argv[0], filename, errno, strerror(errno));

                return 2;
            }
            else if (skipfirstline) {
                int ch;
                /* Push back first newline so line numbers
                   remain the same */
                while ((ch = getc(fp)) != EOF) {
                    if (ch == '\n') {
                        (void)ungetc(ch, fp);
                        break;
                    }
                }
            }
            {
                /* XXX: does this work on Win/Win64? (see posix_fstat) */
                struct stat sb;
                if (fstat(fileno(fp), &sb) == 0 &&
                    S_ISDIR(sb.st_mode)) {
                    fprintf(stderr, "%s: '%s' is a directory, cannot continue\n", argv[0], filename);
                    fclose(fp);
                    return 1;
                }
            }
        }

        if (sts==-1) {
            /* call pending calls like signal handlers (SIGINT) */
            if (Py_MakePendingCalls() == -1) {
                PyErr_Print();
                sts = 1;
            } else {
                sts = PyRun_AnyFileExFlags(
                    fp,
                    filename == NULL ? "<stdin>" : filename,
                    filename != NULL, &cf) != 0;
            }
        }

    }

    /* Check this environment variable at the end, to give programs the
     * opportunity to set it from Python.
     */
    if (!Py_InspectFlag &&
        (p = Py_GETENV("PYTHONINSPECT")) && *p != '\0')
    {
        Py_InspectFlag = 1;
    }

    if (Py_InspectFlag && stdin_is_interactive &&
        (filename != NULL || command != NULL || module != NULL)) {
        Py_InspectFlag = 0;
        /* XXX */
        sts = PyRun_AnyFileFlags(stdin, "<stdin>", &cf) != 0;
    }

    Py_Finalize();
#ifdef RISCOS
    if (Py_RISCOSWimpFlag)
        fprintf(stderr, "\x0cq\x0c"); /* make frontend quit */
#endif

#ifdef __INSURE__
    /* Insure++ is a memory analysis tool that aids in discovering
     * memory leaks and other memory problems.  On Python exit, the
     * interned string dictionary is flagged as being in use at exit
     * (which it is).  Under normal circumstances, this is fine because
     * the memory will be automatically reclaimed by the system.  Under
     * memory debugging, it's a huge source of useless noise, so we
     * trade off slower shutdown for less distraction in the memory
     * reports.  -baw
     */
    _Py_ReleaseInternedStrings();
#endif /* __INSURE__ */

    return sts;
}

Dobry Boże Wszechmogący ... jest wystarczająco duży, aby zatopić Titanica.

Wygląda na to, że Python wykonał sztuczkę „Intro to Programming 101” i po prostu przeniósł cały main()kod do innej funkcji, nazywając go czymś bardzo podobnym do „main”.

Oto moje pytanie: czy ten kod jest strasznie napisany, czy też istnieją inne powody, aby mieć krótką główną funkcję?

W obecnym stanie nie widzę absolutnie żadnej różnicy między zrobieniem tego a Py_Main()ponownym przeniesieniem kodu main(). Czy mylę się myśląc o tym?


4
czy nie byłoby lepiej dla codereviews.stackexchange.com ?
foobar

38
@Luzhin, no. Nie proszę nikogo o sprawdzenie kodu źródłowego Pythona. To jest pytanie programistyczne.
riwalk

3
TBH, połowa kodu to przetwarzanie opcji, a za każdym razem, gdy twój program obsługuje wiele opcji, a ty piszesz niestandardowy procesor, to właśnie kończysz ...
Nim,

7
@Star Nie, Programmers.SE jest również dla najlepszych praktyk, stylów kodowania itp. Właściwie to po to odwiedzam stronę.
Mateen Ulhaq,

4
@Nim, rozumiem, że to, co robi, ale nie ma powodu, aby nie pisać go jako options = ParseOptionFlags(argc,argv)gdzie optionsjest to struct, że zawiera zmienne Py_BytesWarningFlag, Py_DebugFlagitp ...
riwalk

Odpowiedzi:


137

Nie można eksportować mainz biblioteki, ale można eksportować Py_Main, a następnie każdy korzystający z tej biblioteki może „wywoływać” Pythona wiele razy z różnymi argumentami w tym samym programie. W tym momencie pythonstaje się tylko kolejnym konsumentem biblioteki, niewiele więcej niż opakowaniem dla funkcji biblioteki; wzywa Py_Maintak jak wszyscy inni.


1
Dobra odpowiedź.
riwalk

26
Przypuszczam, że dokładniej byłoby powiedzieć, że nie możesz go zaimportować , @Shoosh. Standard C ++ zabrania wywoływania go z własnego kodu. Poza tym jego powiązanie jest zdefiniowane w implementacji. Ponadto, wracając z mainefektywnych wywołań exit, których zwykle nie chce biblioteka.
Rob Kennedy,

3
@ Koder, patrz C ++ 03 §3.6.1 / 5: „Instrukcja return mainpowoduje pozostawienie funkcji głównej… i wywołanie exitz wartością zwracaną jako argumentem”. Zobacz także § 18.3 / 8, który wyjaśnia, że ​​„obiekty o statycznym czasie przechowywania są niszczone” i „wszystkie otwarte strumienie C… są opróżniane” podczas wywoływania exit. C99 ma podobny język.
Rob Kennedy

1
@ Koder, czy exitliście nie mainmają znaczenia. Nie rozmawiamy o zachowaniu exit. Omawiamy zachowanie main. A zachowanie main obejmuje zachowanie exit, cokolwiek to może być. To sprawia, że ​​niepożądany jest import i dzwonienie main(jeśli wykonanie takiej czynności jest w ogóle możliwe lub dozwolone).
Rob Kennedy

3
@ Koder, jeśli powrót z mainnie powoduje wywołania exitkompilatora, oznacza to, że nie jest zgodny ze standardem. To, że standard dyktuje takie zachowanie, maindowodzi, że jest w tym coś wyjątkowego. Szczególną rzeczą mainjest to, że powrót z niego powoduje wywołanie exit. ( Jak to robi, zależy od autorów kompilatora. Kompilator może po prostu wstawić kod do epilogu funkcji, który niszczy obiekty statyczne, wywołuje atexitprocedury, opróżnia pliki i kończy działanie programu - co znowu nie jest czymś, czego chcesz w bibliotece .)
Rob Kennedy,

42

To nie tak, że mainnie powinien być długi tak jak należy unikać jakichkolwiek funkcji zbyt długo. mainto tylko specjalny przypadek funkcji. Dłuższe funkcje stają się bardzo trudne do opanowania, zmniejszają łatwość konserwacji i są ogólnie trudniejsze w obsłudze. Dzięki mainskróceniu funkcji (i ) ogólnie poprawiasz jakość kodu.

W twoim przykładzie nie ma żadnej korzyści z przeniesienia kodu main.


9
Złotym słowem może być „ponowne użycie”. Długi mainnie jest zbyt wielokrotnego użytku.
S.Lott,

1
@S - To jedno złote słowo. Kolejny to OMG !!! ADHD WŁĄCZYŁ SIĘ !!!! lub w kategoriach laika: czytelność.
Edward Strange,

3
main () ma również pewne ograniczenia, których nie mają inne funkcje.
Martin York,

1
Również main () nie ma prawdziwego znaczenia. Twój kod powinien coś znaczyć dla innego programisty. Używam main do parsowania argumentów i to wszystko - i nawet deleguję to, jeśli jest więcej niż kilka wierszy.
Bill K

@Bill K: Dobra uwaga: używanie main () tylko do analizowania argumentów (i uruchamianie reszty programu) również jest zgodne z zasadą pojedynczej odpowiedzialności.
Giorgio

28

Jednym z powodów main()skrócenia są testy jednostkowe. main()to jedna funkcja, która nie może być testowana jednostkowo, więc sensowne jest wydobycie większości zachowań do innej klasy, która może być testowana jednostkowo. Jest to zgodne z tym, co powiedziałeś

Pisanie modularnego kodu i przestrzeganie zasady pojedynczej odpowiedzialności pozwoliło na zaprojektowanie mojego kodu w „pęczkach”, a main () służył jedynie jako katalizator do uruchomienia programu.

Uwaga: stąd pomysł .


Kolejny dobry. Nigdy nie myślałem o tym aspekcie.
riwalk

16

Rzadko dobrym pomysłem mainjest być długim; jak w przypadku dowolnej funkcji (lub metody), jeśli jest długa, prawdopodobnie brakuje Ci możliwości refaktoryzacji.

W konkretnym przypadku, o którym wspomniałeś powyżej, mainjest krótki, ponieważ cała ta złożoność jest uwzględniona Py_Main; jeśli chcesz, aby Twój kod zachowywał się jak powłoka Pythona, możesz po prostu użyć tego kodu bez zbędnego manipulowania. (Trzeba to tak uwzględnić, ponieważ nie działa dobrze, jeśli umieścisz mainw bibliotece; jeśli to zrobisz, zdarzają się dziwne rzeczy).

EDYCJA:
Aby wyjaśnić, mainnie może znajdować się w bibliotece statycznej, ponieważ nie ma do niej wyraźnego łącza, więc nie zostanie poprawnie połączony (chyba że umieścisz go w pliku obiektowym z czymś, do czego się odwołuje, co jest po prostu okropne !) Biblioteki współdzielone są zwykle traktowane jako podobne (ponownie, aby zapobiec nieporozumieniom), chociaż na wielu platformach dodatkowym czynnikiem jest to, że biblioteka współdzielona jest tylko plikiem wykonywalnym bez sekcji ładowania (której mainjest tylko ostatnią i najbardziej widoczną częścią ).


1
Krótko mówiąc, nie umieszczaj mainw bibliotece. To albo nie zadziała, albo strasznie cię zdezorientuje. Ale delegowanie praktycznie całej pracy do funkcji znajdującej się w bibliotece lib jest często rozsądne.
Donal Fellows,

6

Główna powinna być krótka z tego samego powodu, dla którego dowolna funkcja powinna być krótka. Ludzki mózg ma trudności z utrzymaniem w pamięci dużych ilości niepodzielonych danych. Podziel go na logiczne części, aby inni programiści (tak jak i Ty!) Mogli to łatwo przeanalizować.

I tak, twój przykład jest okropny i trudny do odczytania, nie mówiąc już o utrzymaniu.


Tak, zawsze podejrzewałam, że sam kod był okropny (choć pytanie dotyczyło umiejscowienia kodu, a nie samego kodu). Obawiam się, że moja wizja natury Python został uszkodzony w wyniku ...
riwalk

1
@stargazer: Nie wiem, czy sam kod jest okropny, tylko że nie jest dobrze zorganizowany do spożycia przez ludzi. To powiedziawszy, jest mnóstwo „brzydkiego” kodu, który działa dobrze i działa świetnie. Piękno kodu to nie wszystko, chociaż zawsze powinniśmy starać się pisać najczystszy możliwy kod.
Ed S.

meh Dla mnie są jednym i tym samym. Czysty kod wydaje się być bardziej stabilny.
riwalk

Kod nie jest straszny, głównie są przypadki przełączników i obsługa wielu platform. Co dokładnie uważasz za okropne?
Francesco

@Francesco: Niestety, „Straszny” z punktu widzenia konserwacji i czytelności, a nie funkcjonalny.
Ed S.,

1

Niektóre osoby korzystają z ponad 50 funkcji, które nie robią nic więcej, ale nawiązują połączenie z inną funkcją. Wolałbym normalną funkcję główną, która wykonuje logikę programu głównego. Oczywiście dobrze skonstruowany.

int main()
{
CheckInstanceCountAndRegister();
InitGlobals();
ProcessCmdParams();
DoInitialization();
ProgramMainLoopOrSomething();
DeInit();
ClearGlobals();
UnregisterInstance();
return 0; //ToMainCRTStartup which cleans heap, etc.
}

Nie widzę żadnego powodu, dla którego powinienem zawijać coś takiego w opakowaniu.

To czysto osobisty gust.


1
Ponieważ dokument jest kodem. Możesz pisać kod w ten sposób, bez potrzeby (prawie) pisania komentarzy. A kiedy zmienisz kod, dokumentacja zmienia się automatycznie :-).
Oliver Weiler

1

Najlepszą praktyką jest, aby WSZYSTKIE funkcje były krótkie, a nie tylko główne. Jakkolwiek „krótki” jest subiektywny, zależy od wielkości twojego programu i używanego języka.


0

Nie ma wymogu, mainaby być dowolnej długości, poza standardami kodowania. mainjest funkcją jak każda inna i jako taka jej złożoność powinna być mniejsza niż 10 (lub cokolwiek, co mówią twoje standardy kodowania). To wszystko, wszystko inne jest raczej kłótliwe.

edytować

mainnie powinno być krótkie. Lub długo. Powinien obejmować funkcje wymagane do wykonania w oparciu o projekt i przestrzegać standardów kodowania.

Co do konkretnego kodu w twoim pytaniu - tak, to jest brzydkie.

Co do twojego drugiego pytania - tak, mylisz się . Przeniesienie całego tego kodu z powrotem do głównego nie pozwala na używanie go jako biblioteki poprzez linkowanie Py_Mainz zewnątrz.

Czy teraz jestem czysty?


Nie pytałem, czy może być długo. Zapytałem, dlaczego nie powinno być długo.
riwalk

„Złożoność poniżej 10”? Czy jest na to jednostka miary?
Donal Fellows,

@ Stargazer712 Długość funkcji jest zwykle regulowana również przez standardy kodowania. Jest to kwestia czytelności (i złożoności, zwykle długie funkcje są rozgałęzione, dzięki czemu złożoność jest znacznie powyżej 20) i, jak powiedziałem - mainnie różni się od żadnej innej funkcji w tym zakresie.
littleadv

@Donal - tak, kliknij link.
littleadv

Będę musiał głosować za tym jednym pączkiem. Całkowicie brakuje ci sensu pytania.
riwalk

0

Oto nowy pragmatyczny powód, aby trzymać się z dala od Listy zmian GCC 4.6.1 :

W większości obiektów docelowych z obsługą nazwanych sekcji funkcje używane tylko podczas uruchamiania (konstruktory statyczne i główne ), funkcje używane tylko przy wyjściu i funkcje wykryte jako zimne są umieszczane w osobnych podsekcjach segmentu tekstowego . To rozszerza funkcję -freorder-funkcje i jest kontrolowane przez ten sam przełącznik. Celem jest skrócenie czasu uruchamiania dużych programów w C ++.

Podświetlanie dodane przeze mnie.


0

Nie zakładaj, że tylko dlatego, że trochę oprogramowania jest dobre, cały kod stojący za tym oprogramowaniem jest dobry. Dobre oprogramowanie i dobry kod to nie to samo, a nawet jeśli dobre oprogramowanie jest poparte dobrym kodem, nieuniknione jest, że w dużym projekcie pojawią się miejsca, w których standardy się pogarszają.

Dobrą praktyką jest posiadanie krótkiej mainfunkcji, ale tak naprawdę jest to tylko szczególny przypadek ogólnej zasady, że lepiej mieć krótkie funkcje. Krótkie funkcje są łatwiejsze do zrozumienia i łatwiejsze do debugowania, a także lepiej zachowują się w stylu „jednego celu”, który sprawia, że ​​programy są bardziej wyraziste. mainjest być może ważniejszym miejscem do przestrzegania tej reguły, ponieważ każdy, kto chce zrozumieć program, musi zrozumieć, mainpodczas gdy bardziej niejasne zakątki bazy kodu mogą być odwiedzane rzadziej.

Ale baza kodów Pythona nie wypycha kodu Py_Main, aby zagrać w tę regułę, ale ponieważ nie można eksportować mainz biblioteki ani wywoływać jej jako funkcji.


-1

Istnieje kilka technicznych odpowiedzi powyżej, odłóżmy to na bok.

Główny powinien być krótki, ponieważ powinien to być bootstrap. Główny powinien utworzyć instancję niewielkiej liczby obiektów, często takich, które wykonują pracę. Podobnie jak gdziekolwiek indziej, obiekty te powinny być dobrze zaprojektowane, spójne, luźno połączone, zamknięte ...

Chociaż mogą istnieć techniczne powody, aby mieć jedno-liniowe wywołanie główne innej metody potwora, w zasadzie masz rację. Z punktu widzenia inżynierii oprogramowania nic nie zostało uzyskane. Jeśli wybór jest między linią główną z jedną linią wywołującą metodę potwora, a samą magistralą będącą metodą potwora, ta druga jest ułamkowo słabsza.


Zakładasz, że „kod C ++ powinien używać obiektów i tylko obiektów”. To nieprawda, C ++ jest językiem wieloparadigmowym i nie zmusza wszystkiego do formy OO, jak niektóre inne języki.
Ben Voigt,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.