1

Тема: Особливості доступу до аргументів функції

Доброго часу доби на Форумі.

Вирішив написати невеликий пост стосовно деяких особливостей роботи з аргументами функцій у C++. Ця інформація може стати у пригоді тим, хто поки не стикався з проблемою підтримки старого коду та еволюції програми.

Задача стосується використання аргументів функції та необхідності використання цих аргументів. Класичний варіант використання функції переважно виглядає згідно одного з наступних шаблонів:

void fooA(int x, int y){} /* Два обов'язкових аргумента */
void fooB(int x, int y=0){} /* Один - обов'язковий, інший - опціональний аргументи */
void fooC(int x, ...){} /* Один - обов'язковий, інші аргументи - у довільному обсязі */
void fooD(int x=0, int y=2){} /* Усі аргументи - опціональні */

Проте часто виникає ситуація, коли деякі аргументи функцій стають непотрібними з розвитком програми, але їх видалення з коду є проблемним у зв'язку з необхідністю зворотньої підтримки коду та ін. Характерний приклад - еволюція програм на WinAPI, де значущість втрачають обов'язкові аргументи (hPrevInstance у WinMain(), абощо), які, до того ж, стоять не в кінці списку, а тому не можуть стати опціональними де-факто. При цьому їхнє використання у програмі з етичної та практичної точок зору доцільно було би заблокувати.

Спосіб вирішення проблеми демонструється наступним прикладом:

#include <stdio.h>

int fooOptionals(int = 0, int x=2)
{
    return x*x;
}

int fooRequired(int, int x)
{
    return x+x;
}

Функція fooRequired() демонструє шаблон "усі аргументи - обов'язкові". Перший з двох аргументів всередині функції недоступний, хоча передається в обов'язковому порядку.
У функції fooOptionals() фактично втілений шаблон "усі аргументи - опціональні". При цьому перший з двох параметрів, хоча й передається до функції, всередині неї не може бути використаний.

I belong to the Dead Generation.
Подякували: Ярослав, Replace, incred, leofun01, koala5

2

Re: Особливості доступу до аргументів функції

int = 0

Супер як на мою думку.

3

Re: Особливості доступу до аргументів функції

а ви б не могли привести реальний приклад на будь-якій функції з WinApi де можна застосувати таку оптимізацію.

Щоб зрозуміти рекурсію потрібно спочатку зрозуміти рекурсію.
int fac(int n) { return n < 2 ? 1 : n*fac(n-1); }

4

Re: Особливості доступу до аргументів функції

Patron написав:

а ви б не могли привести реальний приклад на будь-якій функції з WinApi де можна застосувати таку оптимізацію.

Той же WinMain() сьогодні не потребує параметра hPrevInstance функціонально, але потребує за структурою. Для чого тоді нам у функції бронювати ім'я та місце для параметра, який нам де-факто непотрібний?
Код має бути мінімальним і достатнім.

Стаття з прикладом для бібліотеки WTL:

LRESULT OnSize(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
{
    if(wParam != SIZE_MINIMIZED)
    {
        T* pT = static_cast<T*>(this);
        pT->UpdateLayout();
    }
 
    bHandled = FALSE;
    return 1;
}

У коді вище програмісту видно відсутність необхідності у параметрах uMsg (тип повідомлення - визначається назвою методу) та lParam (якісь допоміжні дані). Їхня наявність у прототипі зумовлена, думаю, якимись вимогами WinAPI, бо метод функціонально є обгорткою WndProc() для конкретної події.

I belong to the Dead Generation.
Подякували: Patron, Ярослав, leofun013

5

Re: Особливості доступу до аргументів функції

Ще приклад.

Під час розбору бібліотеки wxWidgets виявив такий собі макрос WXUNUSED, який каже компілятору не скиглити з приводу невикористаних ідентифікаторів. Скажімо, подія закриття вікна:

void MyWindow::OnQuit(wxCommandEvent& event)
{
    Close(true);
}

Скигління компілятора припиняються макросом:

void MyWindow::OnQuit(wxCommandEvent& WXUNUSED(event))
{
    Close(true);
}

Але для чого у даному разі цей хитрий макрос, якщо можна написати простіше?

void MyWindow::OnQuit(wxCommandEvent&)
{
    Close(true);
}

Джерело: хелловорд.

I belong to the Dead Generation.

6 Востаннє редагувалося -=ЮрА=- (07.12.2015 09:07:54)

Re: Особливості доступу до аргументів функції

Bartash,

LRESULT OnSize(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
{
    if(wParam != SIZE_MINIMIZED)
    {
        T* pT = static_cast<T*>(this);
        pT->UpdateLayout();
    }

    bHandled = FALSE;
    return 1;
}

Гадаю що подібний приклад не зовсім вірний(у вас там по коду віртуальний метод), багато методів наприклад у MFC мають виклик методу з батьківського класу

void cmpwiz::OnSize(UINT nType, int cx, int cy) 
{
    CDialog::OnSize(nType, cx, cy);
        //далі мій код
}

- взагалі при такому підході йде ралізація концепції - базове опрацювання + функціонал юзера, тобто до батьківського методу долучаэться деякий функціонал який прописав юзер.
Ось такі варіанти

LRESULT cmpwiz::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
{
    // TODO: Add your specialized code here and/or call the base class
    switch( message )
    {
    case WM_UPDATEDATA:
        UpdateData(wParam);
        break;
    }
    return CDialog::WindowProc(message, wParam, lParam);
}

- конають лише тому що метод віртуальний

virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam); 

тобто ми конкретно змінюємо опрацювання.
Я безумовно згоден з вами по концепції скривання параметрів але не згоден з цим

У коді вище програмісту видно відсутність необхідності у параметрах uMsg

- програміст може заблокувати параметри і не отримати скажім зміну розміру вікна. З того що там написано не видно слід чи не слуд вилучати парметри, треба навести прототип функції, яущо він віртуальний то можемо і обійтись без парметрів, якщо ні, то тіло функцію може потребувати виклику батьківького методу з конкретними параметрами, які ви "закрили"

Подякували: leofun011

7

Re: Особливості доступу до аргументів функції

-=ЮрА=- написав:

Я безумовно згоден з вами по концепції скривання параметрів але не згоден з цим

У коді вище програмісту видно відсутність необхідності у параметрах uMsg

- програміст може заблокувати параметри і не отримати скажім зміну розміру вікна. З того що там написано не видно слід чи не слуд вилучати парметри, треба навести прототип функції, яущо він віртуальний то можемо і обійтись без парметрів, якщо ні, то тіло функцію може потребувати виклику батьківького методу з конкретними параметрами, які ви "закрили"

Згоден - якщо ТЗ передбачає заборону зміни розміру вікна за певних умов, то це слід вказати явно у коді. Але у даному разі акцент на іншому: якщо параметри, які передаються у функцію, існують суто для сумісності/підтримки узагальненості коду і не мають бути там застосовані, то ліпше явно заборонити їх використання. Це і нерви на зневадженні зекономить, і звільнить від необхідності щоразу лізти до мануалів (MSDN у даному прикладі) на предмет кшалту "wParam чи lParam? Ось у чому питання!"

Даний підхід розрахований переважно на проги, які писалися за царя Панька цілком стереотипними італійцями або/та індусами і практично не піддається рефакторингу.

I belong to the Dead Generation.
Подякували: leofun011