Есть ли лучший способ прокомментировать направление параметров в коде? - PullRequest
4 голосов
/ 02 марта 2012

Я хочу улучшить читаемость кода.Поэтому я прокомментировал направление параметра в коде следующим образом:

#define IN
#define OUT

void Add(IN int Para1, IN int Para2, OUT int& Result);

Но я думаю, что компилятор заменит каждый экземпляр IN и OUT пустым, и это может иногда вызывать проблемы.

Так есть ли лучший способ?Спасибо.

(я использую C ++.)

Ответы [ 9 ]

9 голосов
/ 02 марта 2012

Вы можете попробовать поместить это в комментарии.Это намного лучше и читабельнее.

void Add(/*IN*/ int Para1, /*IN*/ int Para2, /*OUT*/ int& Result);
6 голосов
/ 02 марта 2012

Да: забудь об этих вещах и используй постоянство.Это будет работать до тех пор, пока у вас нет параметров «in» и «out», которые используются и должны использоваться редко.

void foo(int i, const std::string& s, std::vector<char>& out_buf);
// i and s are obviously "in" variables, while out_buf could be both, 
// but you can easily show that by giving the parameter a proper name.

Редактировать: и правильность const не означает создание значений параметровсопз!Это не дает вызывающей стороне никакой дополнительной информации, так как вы не можете изменить его переменные в любом случае.

3 голосов
/ 02 марта 2012

Я могу придумать два простых способа для этого ..

1.

        /*Description : Function for adding the two variables.
        * Returns : Nothing 
        * Parameters : Para1 and Para2  are **IN** parameter and 
        * Result is an **OUT** parameter
        * @author : <put ur name here> 
        */
    void Add(IN int Para1, IN int Para2, OUT int& Result);

Помимо этой основной информации вы также можете хранить такую ​​информацию, как номер версии, дата создания и т. Д.

2. Вы также можете встраивать информацию о типе параметра с именем переменной, т.е. inPara1, inPara2 и outResult. например

 void Add(int inPara1,int inPara2,int& outResult);

еще одна вещь, которую я бы порекомендовал использовать в качестве имени переменной и букв функции в качестве имени верблюда, т.е. Para1 может быть как para1 и т. Д., Это поможет вам в будущем.

1 голос
/ 02 марта 2012

Учитывая, что это стилистический вопрос, он вызовет некоторые субъективные ответы, в том числе и мой.: -)

Существуют процедурные языки, которые требуют, чтобы параметры функции были определены как входные или выходные.В C ++ это в значительной степени не нужно.

Общее мышление, которое должен иметь современный разработчик C ++, - это такое, которое больше фокусируется на изменчивых и неизменяемых и взаимодействует с необработанными типами и данными.

Когда мыпосмотрите на такую ​​функцию:

void f(int x);

... разработчик C ++ сразу скажет вам, что для ввода используется «x».Зачем?Это передается по значению.Не существует способа изменить 'x' так, чтобы это оказало какое-либо влияние на вызывающего, и, следовательно, любой аргумент, переданный этой функции, не будет изменен.

Это также верно для любой ссылки на const:

void f(const Foo& read_only_foo);

Выше определенно является строгим входным параметром.Когда мы смотрим на такую ​​функцию:

void f(int& x);

Обычно мы можем предположить, что f собирается изменить x (это не гарантируется, но знание того, что делает f, должно устранить любые сомнения).

С пользовательскими типами он становится немного более туманным.

ostream& operator<<(ostream& os, const Foo& foo);

Здесь мы точно знаем, что 'foo' является входным параметром, поскольку он неизменен.Но как насчет «ОС»?Это выходной параметр, вход, оба?В строгих процедурных языках выходной параметр обычно подразумевает, что параметр будет изменен, но мы также читаем из него здесь, так что это будет и то и другое.Хотя мы будем вызывать методы в 'os', которые влияют на его состояние, это не совсем то, что мы думаем о выходном параметре в процедурных языках, которые изначально поддерживают параметры ввода / вывода.

Дело в том,что такой строгий подход к вводу-выводу о параметрах может привести к путанице с этими высокоуровневыми интерфейсами и объектно-ориентированным проектированием.Более полезный способ взглянуть на вещи здесь, как правило, заключается в том, является ли объект, реализующий интерфейс, изменчивым или неизменным.Здесь 'os' является изменчивым.Функция обычно говорит, что она будет вызывать некоторые функции, которые изменяют ее состояние.

Как насчет этого?

// fills the specified list with stuff
void some_list(list<int>& out_list);

Здесь она имеет тенденцию идти, возможно, более естественно, квид семантики мы ожидаем от выходного параметра.Что-то наподобие заполнения списка, мы склонны думать об этом более интуитивно, как о функции, выводящей результат через список.Я даже добавил к имени «out», чтобы подчеркнуть это.Но на самом деле, и особенно с C ++ 11, мы не должны писать такие вещи, как подчеркивает Страуструп:

// returns a new list filled with stuff
list<int> some_list();

Это фактически оставляет очень мало мест, где различие входа / выхода может быть очень полезнымвообще (и не избыточно с уже предоставленными средствами для пометки параметров как изменяемых / неизменяемых, по значению, ссылке, указателю или ссылке r-значения).

В сочетании с четкой документацией о том, что делает функция,как правило, нет никакой двусмысленности относительно того, как он работает со своими параметрами, поэтому соглашения о входе / выходе имеют тенденцию делать очень мало, но добавляют много дополнительного кода и могут способствовать формированию более ориентированного на данные мышления, от которого вы должны пытаться уйти.

В общем, я бы посоветовал попытаться избежать этого соглашения все вместе.Даже если есть веские аргументы в пользу этого, это просто не то, что люди склонны делать в C ++.И если вы хотите, чтобы люди получали удовольствие от работы с вашим кодом и не расстраивались, вы должны научиться понимать, что обычные массы склонны понимать и любить.Все, что слишком экзотично, отпугнет людей.

Если вы абсолютно, фанатично привязаны к тому, чтобы обозначать все как в или в , я рекомендую минимально навязчивое решение, такое какстиль документации или соглашение об именах.Определенно избегайте макросов, которые ничего не делают.Это потребует от ваших читателей проверки каждый раз, когда эти макросы действительно ничего не делают.Соглашение об именах или стиль документации не требуют такой проверки.

Наконец, небольшая цитата от самого создателя C ++:

Первое правило, касающееся макросов: не используйте их без необходимости. Почти каждый макрос демонстрирует недостаток в языке программирования, в в программе или в программаторе. - Бьярне Страуструп

1 голос
/ 02 марта 2012

Я использую что-то вроде этого

void Add(
    /* input parameters */
    int Para1,
    int Para2,
    /* output parameters */
    int& Result
);

Это упрощает добавление новых параметров в функцию, поскольку вам не нужно отмечать каждый из них input или output , они могут просто перейти в соответствующий раздел .

1 голос
/ 02 марта 2012

Вы можете сделать переменные 'IN' const, подразумевая, что они никогда не будут изменены, поэтому должны быть переменные только для ввода.Ссылка без a const также может означать, что ее содержимое будет изменено, поэтому она должна быть переменной OUT.Но на самом деле, достаточно просто следовать хорошему соглашению об именовании переменных.Вызов параметра «Result» подразумевает, что он сам по себе будет переменной «OUT».

РЕДАКТИРОВАТЬ: Как справедливо отмечалось другими, передача значений типа int в виде const vars, вероятно, не очень хорошая идея.Тем не менее, вы должны иметь возможность сделать вывод, является ли переменная выходной или нет, просто потому, что они всегда должны быть ссылками (или указателями).Делая ссылки, которые являются входными значениями const, это означает, что все ссылки, которые не являются const, должны быть выходными значениями.

0 голосов
/ 12 февраля 2019

Вы можете использовать нотацию Microsoft SAL для C ++:

https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/ms235402(v=vs.100)

Взято из документов:

_In_

Функция будет читать только из буфера. Вызывающая сторона должна предоставить буфер и инициализировать его.

_Out_

Функция будет записывать только в буфер. Вызывающая сторона должна предоставить буфер, и функция его инициализирует.

_Inout_

Функция может свободно читать и записывать в буфер. Вызывающая сторона должна предоставить буфер и инициализировать его.

_Deref_out_

Применяется к выходным параметрам, на которые нет ссылок. При заданном параметре p * p является указателем буфера. p не должно быть NULL.

Функция будет записывать только в буфер. Функция предоставит буфер и инициализирует его.

0 голосов
/ 02 марта 2012

Используйте правильные языковые конструкции, а не артефакты.

Использование IN и OUT не означает, что они действительно IN и OUT.Это похоже на венгерскую нотацию, которая вызывает WPARAM что-то, что было word, а сегодня это long.

  • передача по значению (int) - это IN, который вы можете изменить
  • передача по const ref (const int&) - это IN, который ведет себя как константа в функции
  • передача по ссылке (int&) - это OUT, которая должна быть там
  • передать указатель const (const int*) - это IN, который также может быть не задан (нулевой указатель)
  • передать указатель (int*) - это OUT, который также может быть не задан (нулевой)указатель)

Преимущество использования правильной языковой конструкции состоит в том, что неправильное использование приведет к ошибке компиляции (так что вы вынуждены исправить проблему), в то время как IN и OUT никогда не приведут к какой-либо ошибкев коде, и вы рискуете ввести формальное соглашение, которое - после определенного количества выпусков обслуживания - может быть даже ложью для себя.

0 голосов
/ 02 марта 2012

У этого есть две стороны: подпись функции и сайт вызова.Вы сосредоточены на подписи, поэтому я рассмотрю это в первую очередь.Примечания:

  • const ness сообщает и применяет потенциальные изменения с помощью функции
  • Наличие согласованных соглашений API для библиотеки может помочь программистам библиотеки и клиентского кода, например:
    • Возвращение вывода (что может означать необходимость использования кортежа, контейнера или пользовательской структуры) с сохранением параметров функции для входных данных.
    • Обозначение чего-либо о функции через идентификаторы и упорядочение параметров;лично, если я напишу функцию, которая может изменить параметр, я назову эту функцию loadXXX () и поставлю вывод первым.Другие библиотеки, которые я видел, последовательно выводили результаты в последнюю очередь.Это произвольный выбор, но помогает последовательность.Точно так же вы могли бы по-разному именовать выходные параметры ....
  • Комментарии / документация

На сайте вызова клиента при передаче неконстантного аргумента этонеясно, что может с ним случиться.

  • Опять же, loadXXX, группа выводит сначала / последний, и предпочитают-return -выход (-ы) - getXXX - соглашения, упомянутые вышепомогите и клиенту.
  • Передача выходных параметров по указателю является проверенной временем, но противоречивой практикой, которая IMO не особенно популярна среди современных профессиональных программистов на C ++, но умеет хорошо читать и добавлять подлинную ценность.FAQ по C ++ решает большинство проблем, но приходит к другим выводам, чем я.
  • Вы можете привести не-1028 * параметры к const, чтобы сообщить на сайте вызовов, что они не будут изменены,но это многословно и утомительно, и компилятор не может применить эту практику (компилятор предотвратит вызываемую функцию, изменяющую эти параметры, но не заставит вызывающую функцию, предоставляющую параметр, принимаемый функцией как const, явно привести ее к const).

Некоторые из этих вещей можно «форсировать» с помощью прокси-объектов, но это сделает ваш код практически нечитаемым и не поддерживаемым.

...