Code style c

Code style c

Соглашения о написании кода на C#

Соглашения о написании кода предназначены для реализации следующих целей.

Соглашения об именах

При написании кода C# следует учитывать несколько соглашений об именовании.

Регистр Pascal

При именовании public элементов типов, таких как поля, свойства, события, методы и локальные функции, используйте регистр pascal.

При записи позиционных записей используйте регистр pascal для параметров, так как это открытые свойства записи.

Дело верблюда

Используйте регистр верблюда («camelCasing») при именовании private или internal полях, а также префикс их. _

При редактировании кода C#, который следует этим соглашениям об именовании в интегрированной среде разработки, поддерживающей завершение инструкций, при вводе _ будут показаны все члены области объекта.

При написании параметров метода используйте регистр верблюда.

Дополнительные сведения о соглашениях об именовании C# см. в разделе «Стиль написания кода на C#».

Дополнительные соглашения об именовании

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

Вам не нужно изменять имена объектов, созданных с помощью инструментов разработки Visual Studio, чтобы привести их в соответствие с другими рекомендациями.

Соглашения о макете

Чтобы выделить структуру кода и облегчить чтение кода, в хорошем макете используется форматирование. Примеры и образцы корпорации Майкрософт соответствуют следующим соглашениям.

Использование параметров редактора кода по умолчанию (логичные отступы, отступы по четыре символа, использование пробелов для табуляции). Дополнительные сведения см. в разделе «Параметры», «Текстовый редактор», C#, «Форматирование».

Запись только одного оператора в строке.

Запись только одного объявления в строке.

Если отступ для дополнительных строк не ставится автоматически, необходимо сделать для них отступ на одну позицию табуляции (четыре пробела).

Добавление по крайней мере одной пустой строки между определениями методов и свойств.

Использование скобок для ясности предложений в выражениях, как показано в следующем коде.

Соглашения о комментариях

Комментарий размещается на отдельной строке, а не в конце строки кода.

Текст комментария начинается с заглавной буквы.

Текст комментария завершается точкой.

Между разделителем комментария (/ /) и текстом комментария вставляется один пробел, как показано в следующем примере.

Не создавайте форматированные блоки из звездочек вокруг комментариев.

Убедитесь, что все открытые члены имеют необходимые XML-комментарии, предоставляющие соответствующие описания их поведения.

Рекомендации по использованию языка

В следующих подразделах описаны методики, которыми руководствуется команда C# для подготовки примеров и образцов кода.

Строковый тип данных

Для сцепления коротких строк рекомендуется использовать интерполяцию строк, как показано в следующем коде.

Для добавления строк в циклах, особенно при работе с текстами больших размеров, используйте объект StringBuilder.

Неявно типизированные локальные переменные

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

Если тип в правой части назначения не является очевидным, не рекомендуется использовать var. Не полагайтесь на то, что имя метода предоставляет информацию о типе. Тип переменной можно считать очевидным, если используется оператор new или явное приведение типа.

Не полагайтесь на имя переменной при указании ее типа. Имя может быть неверным. В следующем примере имя переменной inputInt вводит в заблуждение. На самом деле это строка.

Используйте неявное ввод, чтобы определить тип переменной цикла в for циклах.

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

Беззнаковые типы данных

Массивы

Если вы указали размер массива, нужно поочередно инициализировать все элементы.

Делегаты

Используйте Func<> и Action<> вместо определения типов делегатов. В классе определите метод делегата.

Если вы создаете экземпляры типа делегата, используйте сокращенный синтаксис. В классе определите тип делегата и метод с соответствующей сигнатурой.

Создайте экземпляр типа делегата и вызовите его. В следующем объявлении используется сокращенный синтаксис.

В следующем объявлении используется полный синтаксис.

Рекомендуется использовать оператор try-catch для обработки большей части исключений.

Операторы && и ||

Чтобы избежать возникновения исключений и увеличить производительность за счет пропуска необязательных сравнений, используйте && вместо & и || вместо | при выполнении сравнений, как показано в следующем примере.

Если делитель равен нулю, второе условие в операторе if вызовет ошибку времени выполнения. && Но оператор замыкается, если первое выражение имеет значение false. Это означает, что второе выражение не будет вычисляться. Оператор & вычисляет оба, что приводит к ошибке во время выполнения, если divisor значение равно 0.

Оператор new

Используйте одну из сокращенных форм создания экземпляров объектов, как показано в следующих объявлениях. Во втором примере используется синтаксис, который появился в версии C# 9.

Предыдущие объявления эквивалентны следующему объявлению.

Используйте инициализаторы объектов, чтобы упростить создание объектов, как показано в следующем примере.

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

Обработка событий

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

Лямбда-выражение сокращает приведенное ниже традиционное определение.

Статические члены

Для вызова статических членов следует использовать имя класса: ClassName.StaticMember. В этом случае код становится более удобочитаемым за счет четкого доступа. Не присваивайте статическому элементу, определенному в базовом классе, имя производного класса. Во время компиляции кода его читаемость нарушается, и если добавить статический член с тем же именем в производный классе, код может быть поврежден.

Запросы LINQ

Используйте значимые имена для переменных запроса. В следующем примере используется seattleCustomers для клиентов, находящихся в Сиэтле.

Рекомендуется использовать псевдонимы для уверенности в том, что в именах свойств анонимных типов верно используются прописные буквы при помощи правил использования прописных и строчных букв языка Pascal.

Рекомендуется использовать неявное типизирование в объявлении переменных запроса и переменных диапазона.

Используйте where предложения перед другими предложениями запросов, чтобы гарантировать, что последующие предложения запросов работают с уменьшенным отфильтрованным набором данных.

Используйте несколько from предложений вместо join предложения для доступа к внутренним коллекциям. Например, коллекция объектов Student может содержать коллекцию результатов тестирования. При выполнении следующего запроса возвращаются результаты, превышающие 90 балов, а также фамилии учащихся, получивших такие оценки.

Источник

Гайд по оформлению кода на С++ от Стэнфордского университета

Code style c. Смотреть фото Code style c. Смотреть картинку Code style c. Картинка про Code style c. Фото Code style c

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

Пробелы и отступы

Отделяйте пробелами фигурные скобки:

Ставьте пробелы между операторами и операндами:

Когда строка становится длиннее 100 символов, разделите её на две, сделав перевод на новую строку после оператора, и продолжайте писать:

Оставляйте пустые линии между функциями и между группами выражений:

Названия и переменные

Code style c. Смотреть фото Code style c. Смотреть картинку Code style c. Картинка про Code style c. Фото Code style c

Используйте текстовую строку, стандартную для C++, а не С. С++ путает тем, что имеет два вида текстовых строк: класс string из С++ и старый char* (массив символов) из С:

Если определенная константа часто используется в вашем коде, то обозначьте её как const и всегда ссылайтесь на данную константу, а не на её значение:

Никогда не объявляйте изменяемую глобальную переменную. Глобальными переменными должны быть только константы. Вместо того, чтобы делать значение глобальным, сделайте его параметром и возвращайте значение, когда необходимо:

Базовые выражения С++

Чрезмерность

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

Комментарии

Заглавный комментарий. Размещайте заглавный комментарий, который описывает назначение файла, вверху каждого файла. Предположите, что читатель вашего комментария является продвинутым программистом, но не кем-то, кто уже видел ваш код ранее.

Заголовок функции / конструктора. Разместите заголовочный комментарий на каждом конструкторе и функции вашего файла. Заголовок должен описывать поведение и / или цель функции.

Параметры / возврат. Если ваша функцию принимает параметры, то кратко опишите их цель и смысл. Если ваша функция возвращает значение — кратко опишите, что она возвращает.

Исключения. Если ваша функция намеренно выдает какие-то исключения для определенных ошибочных случаев, то это требует упоминания.

Комментарии на одной строке. Если внутри функции имеется секция кода, которая длинна, сложна или непонятна, то кратко опишите её назначение.

TODO. Следует удалить все // TODO комментарии перед тем, как заканчивать и сдавать программу.

Эффективность

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

Функции и процедурное проектирование

Хорошо спроектированная функция имеет следующие характеристики:

Когда требуется вернуть значение из функции, используйте значение return :

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

Используйте ссылочные переменные, а не указатели. Одна из причин — это то, что ссылочные переменные, в отличие от указателей, не могут принимать значение NULL :

Проектирование классов

Инкапсуляция. Отделяйте ваши объекты, делая все поля данных в вашем классе private :

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

Источник

C# Coding Conventions

Coding conventions serve the following purposes:

Naming conventions

There are several naming conventions to consider when writing C# code.

In the following examples, any of the guidance pertaining to elements marked public is also applicable when working with protected and protected internal elements, all of which are intended to be visible to external callers.

Pascal case

When naming public members of types, such as fields, properties, events, methods, and local functions, use pascal casing.

When writing positional records, use pascal casing for parameters as they’re the public properties of the record.

For more information on positional records, see Positional syntax for property definition.

Camel case

When editing C# code that follows these naming conventions in an IDE that supports statement completion, typing _ will show all of the object-scoped members.

When writing method parameters, use camel casing.

For more information on C# naming conventions, see C# Coding Style.

Additional naming conventions

Examples that don’t include using directives, use namespace qualifications. If you know that a namespace is imported by default in a project, you don’t have to fully qualify the names from that namespace. Qualified names can be broken after a dot (.) if they are too long for a single line, as shown in the following example.

You don’t have to change the names of objects that were created by using the Visual Studio designer tools to make them fit other guidelines.

Layout conventions

Good layout uses formatting to emphasize the structure of your code and to make the code easier to read. Microsoft examples and samples conform to the following conventions:

Use the default Code Editor settings (smart indenting, four-character indents, tabs saved as spaces). For more information, see Options, Text Editor, C#, Formatting.

Write only one statement per line.

Write only one declaration per line.

If continuation lines are not indented automatically, indent them one tab stop (four spaces).

Add at least one blank line between method definitions and property definitions.

Use parentheses to make clauses in an expression apparent, as shown in the following code.

Commenting conventions

Place the comment on a separate line, not at the end of a line of code.

Begin comment text with an uppercase letter.

End comment text with a period.

Insert one space between the comment delimiter (//) and the comment text, as shown in the following example.

Don’t create formatted blocks of asterisks around comments.

Ensure all public members have the necessary XML comments providing appropriate descriptions about their behavior.

Language guidelines

The following sections describe practices that the C# team follows to prepare code examples and samples.

String data type

Use string interpolation to concatenate short strings, as shown in the following code.

To append strings in loops, especially when you’re working with large amounts of text, use a StringBuilder object.

Implicitly typed local variables

Use implicit typing for local variables when the type of the variable is obvious from the right side of the assignment, or when the precise type is not important.

Don’t use var when the type is not apparent from the right side of the assignment. Don’t assume the type is clear from a method name. A variable type is considered clear if it’s a new operator or an explicit cast.

Don’t rely on the variable name to specify the type of the variable. It might not be correct. In the following example, the variable name inputInt is misleading. It’s a string.

Avoid the use of var in place of dynamic. Use dynamic when you want run-time type inference. For more information, see Using type dynamic (C# Programming Guide).

Use implicit typing to determine the type of the loop variable in for loops.

The following example uses implicit typing in a for statement.

Don’t use implicit typing to determine the type of the loop variable in foreach loops. In most cases, the type of elements in the collection isn’t immediately obvious. The collection’s name shouldn’t be solely relied upon for inferring the type of its elements.

The following example uses explicit typing in a foreach statement.

Be careful not to accidentally change a type of an element of the iterable collection. For example, it is easy to switch from System.Linq.IQueryable to System.Collections.IEnumerable in a foreach statement, which changes the execution of a query.

Unsigned data types

Arrays

If you specify an array size, you have to initialize the elements one at a time.

Delegates

Use Func<> and Action<> instead of defining delegate types. In a class, define the delegate method.

Call the method using the signature defined by the Func<> or Action<> delegate.

If you create instances of a delegate type, use the concise syntax. In a class, define the delegate type and a method that has a matching signature.

Create an instance of the delegate type and call it. The following declaration shows the condensed syntax.

The following declaration uses the full syntax.

Use a try-catch statement for most exception handling.

Simplify your code by using the C# using statement. If you have a try-finally statement in which the only code in the finally block is a call to the Dispose method, use a using statement instead.

You can do the same thing with a using statement.

In C# 8 and later versions, use the new using syntax that doesn’t require braces:

&& and || operators

To avoid exceptions and increase performance by skipping unnecessary comparisons, use && instead of & and || instead of | when you perform comparisons, as shown in the following example.

If the divisor is 0, the second clause in the if statement would cause a run-time error. But the && operator short-circuits when the first expression is false. That is, it doesn’t evaluate the second expression. The & operator would evaluate both, resulting in a run-time error when divisor is 0.

new operator

Use one of the concise forms of object instantiation, as shown in the following declarations. The second example shows syntax that is available starting in C# 9.

The preceding declarations are equivalent to the following declaration.

Use object initializers to simplify object creation, as shown in the following example.

The following example sets the same properties as the preceding example but doesn’t use initializers.

Event handling

If you’re defining an event handler that you don’t need to remove later, use a lambda expression.

The lambda expression shortens the following traditional definition.

Static members

Call static members by using the class name: ClassName.StaticMember. This practice makes code more readable by making static access clear. Don’t qualify a static member defined in a base class with the name of a derived class. While that code compiles, the code readability is misleading, and the code may break in the future if you add a static member with the same name to the derived class.

LINQ queries

Use meaningful names for query variables. The following example uses seattleCustomers for customers who are located in Seattle.

Use aliases to make sure that property names of anonymous types are correctly capitalized, using Pascal casing.

Rename properties when the property names in the result would be ambiguous. For example, if your query returns a customer name and a distributor ID, instead of leaving them as Name and ID in the result, rename them to clarify that Name is the name of a customer, and ID is the ID of a distributor.

Use implicit typing in the declaration of query variables and range variables.

Align query clauses under the from clause, as shown in the previous examples.

Use where clauses before other query clauses to ensure that later query clauses operate on the reduced, filtered set of data.

Use multiple from clauses instead of a join clause to access inner collections. For example, a collection of Student objects might each contain a collection of test scores. When the following query is executed, it returns each score that is over 90, along with the last name of the student who received the score.

Источник

MaJerle/c-code-style

Use Git or checkout with SVN using the web URL.

Work fast with our official CLI. Learn more.

Launching GitHub Desktop

If nothing happens, download GitHub Desktop and try again.

Launching GitHub Desktop

If nothing happens, download GitHub Desktop and try again.

Launching Xcode

If nothing happens, download Xcode and try again.

Launching Visual Studio Code

Your codespace will open once ready.

There was a problem preparing your codespace, please try again.

Latest commit

Git stats

Files

Failed to load latest commit information.

README.md

Recommended C style and coding rules

This document describes C code style used by Tilen MAJERLE in his projects and libraries.

The single most important rule

Let’s start with the quote from GNOME developer site.

The single most important rule when writing code is this: check the surrounding code and try to imitate it.

As a maintainer it is dismaying to receive a patch that is obviously in a different coding style to the surrounding code. This is disrespectful, like someone tromping into a spotlessly-clean house with muddy shoes.

So, whatever this document recommends, if there is already written code and you are patching it, keep its current style consistent even if it is not your favorite style.

Here are listed most obvious and important general rules. Please check them carefully before you continue with other chapters.

Always declare local variables at the beginning of the block, before first executable statement

Declare counter variables in for loop

Structures, enumerations, typedefs

When structure is declared, it may use one of 3 different options:

Examples of bad declarations and their suggested corrections

Macros and preprocessor directives

Documented code allows doxygen to parse and general html/pdf/latex output, thus it is very important to do it properly.

Do not include module private declarations in header file

Header file example (no license for sake of an example)

Artistic style configuration

AStyle is a great piece of software that can help with formatting the code based on input configuration.

This repository contains astyle-code-format.cfg file which can be used with AStyle software.

Repository contains eclipse-ext-kr-format.xml file that can be used with eclipse-based toolchains to set formatter options.

About

Recommended C code style and coding rules for standard C99 or later

Источник

Пиши на C как джентльмен

Code style c. Смотреть фото Code style c. Смотреть картинку Code style c. Картинка про Code style c. Фото Code style c

«Code Monkey like Fritos
Code Monkey like Tab and Mountain Dew
Code Monkey very simple man
With big warm fuzzy secret heart:
Code Monkey like you
Code Monkey like you»

— Jonathan Coulton — Code Monkey

Я думаю, многим знакома эта шикарная песня Jonathan Coulton’а, и эта жизненная ситуация, когда «Rob say Code Monkey very diligent», но «his output stink» и «his code not ‘functional’ or ‘elegant’».

Язык Си, подаривший нам столько полезного софта, потихоньку был вытеснен из десктопа и энтерпрайза такими высокоуровневыми гигантами как Java и C# и занял нишу системного программирования. И все бы хорошо, но системщики — очень отбитые своеобразные ребята. Задачи, которые порой возникают перед ними даже своей формулировкой способны вогнать в ужас простых смертных. Собственно говоря, так же, как и некоторые решения.

Сегодня мы поговорим о некоторых полезных практиках, которые я вынес из глубин системного программирования на Си. Поехали.

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

Практика I: Соблюдайте единый Code Style и фундаментальные принципы «хорошего тона»

Функция принимает в качестве аргумента переменную INPUT, парсит её в массив IncomingValues и возвращает result_to_return? Отставить быдлокод!

То, что в первую очередь выдает новичка — несоблюдение единого стиля написания кода в рамках конкретного приложения. Следом идет игнорирование правил «хорошего тона».

Вот несколько самых распространенных рекомендаций к оформлению кода на Си:

Вообще, этот пункт спорный. Мне доводилось видеть проекты, где имена переменных и функций пишутся в camelCase и PascalCase соответственно.

Кстати, рекомендую взять за привычку писать код так, чтобы одна функция делала только одну вещь. Именно это должно отразиться в названии.

То же можно сказать и про переменные — никаких a, b, c — в названии должен быть отражен смысл (итераторы не в счет). Самодокументируемый код — очень хорошая практика.

Как правило, можно выбрать между стилем написания названия: PascalCase и under_score, тут уже зависит от вас.

По возможности инициализируйте переменные при объявлении. Численные с помощью нуля, указатели — NULL:

Ну оставили мы переменные неинициализированными, и что?

А то. Если смотреть их (до инициализации) в отладке (в том же gdb), там будет лежать мусор. Это нередко сбивает с толку (особенно, если мусор «похож на правду»). Про указатели я вообще молчу.

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

Оптимальное решение — писать описания функций, если из аргументов и названия сложно понять весь её функционал. Для переменных — правила те же, в пояснении нуждаются только какие-то нелинейный вещи, где одного названия мало.

На самом деле, в вопросе документации у вас есть полная свобода действий — надо лишь следить, чтобы комментариев было не много, но достаточно, чтобы человек, видящий ваш код в первый раз, не задавал вопросов.

Если вы постоянно работаете с трекерами (вроде RedMine), то при внесении правок в код можно указать номер задачи, в рамках которой эти правки были внесены. Если у кого-то при просмотре кода возникнет вопрос а-ля «Зачем тут этот функционал?», ему не придется далеко ходить. В нашей компании еще пишут фамилию программиста, чтобы если что знать, к кому идти с расспросами.

P.S. Для тех кто устраивается на работу: так же не следует забывать, что в каждой компании, как правило, используется свой Code Style, и ему нужно следовать. В противном сулучае можно получить как минимум укоризненные взгляды товарищей-разрабов или втык от начальства.

Практика II: Оптимизируйте структуру вашего проекта

Если у вас в проекте несколько файлов — имеет смысл хорошо подумать над структурой проекта.
Каждый проект уникален, но, тем не менее, существует ряд рекомендаций, которые помогут удобно структурировать проект:

Не следует называть файлы file1.c, mySUPER_COOL_header.h и т.д.
main.c — для файла с точкой входа, graph_const.h — для заголовочника с графическими константами будет в самый раз.

Если он точно не знает, какой файл ему нужен? Можно сберечь много нервов, если сделать так:

Если у вас игра, в которой есть файлы, отвечающие за движок/звук/графику — будет удобно раскидать их по папкам. Звук, графику и движок — отдельно друг от друга.

Практика III: Используйте враппер-функции для обработки возвращаемых значений

Враппер-функция (функция-обертка) в языке Си используется как функция со встроенной обработкой возвращаемого значения. Как правило, в случае ошибки в работе функции, возвращаемое значение вам об этом скажет, а глобальная переменная errno примет в себя код ошибки.

Если вы пишите в системе (а сейчас большинство программ на си — именно системные программы), то нет ничего хуже, чем «немое» падение программы. По-хорошему, она должна красиво завершиться, напоследок сказав, что именно пошло не по плану.

Но обрабатывать значение от каждой функции в коде — такое себе решение. Тут же упадет читаемость, и объем (+ избыточность) кода увеличится в пару раз.

Тут и помогают врапперы. Рассмотрим первый пример — безопасный код без врапперов:

Ну, такое себе, не правда ли? Теперь попробуем с обертками.

Как видите, код по-прежнему безопасен (не будет «немого» падения), но теперь его функциональная часть гораздо компактнее.

Я называю обертки именем самих функций, но с большой буквы. Каждый сам волен выбрать, как их оформлять.

В использовании оберток есть небольшой минус, который, если захотеть, можно решить костылем. А что это за минус — можете предположить в комментариях 🙂

Практика IV: Используйте keywords как профи

Хорошее знание keywords никогда не будет лишним. Да, и без них ваш код будет работать, не спорю. Но когда речь зайдет об экономии места, быстродействии и оптимизации — это именно то, чего вам будет не хватать.

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

И это только вершина айсберга. Различных модификаторов и ключевых слов — куча.

Практика V: Не доверяйте себе. Доверяйте valgrind.

Если у вас в программе есть работа со строками, динамическое выделение памяти и все, где замешаны указатели, то не будет лишним проверить себя.

Valgrind — программа, которая создана для того, чтобы помочь программисту выявить утечки памяти и ошибки контекста. Не буду вдаваться в подробности, скажу лишь, что даже в небольших программах он нередко находит косяки, которые совсем не очевидны для большинства программистов, но, тем не менее, в эксплуатации могут повлечь за собой большие проблемы. За всем не уследишь.
+ у нее есть и другой полезный функционал.

Более подробно о нем можно узнать тут.

Практика VI: Помогайте тем, кто хочет улучшить ваш софт

Пример будет взят из исходников busybox 1.21. Для тех кто не знает, что такое busybox, можете посмотреть эту вики-статью.

UPD: до этого здесь был пример «плохого» кода из busybox. Спасибо пользователю themiron за то, что показал, что этот код был понят мною неправильно — это были лишь тонкости реализации, причем реализации очень хорошей. В качестве извинения за свою «клевету» на busybox, здесь будет пример хорошего кода.

Причем, все так же из busybox.

Код busybox очень эллегантен, пусть и совсем не прост. Всем, кто хочет взглянуть на язык си под другим углом — рекомендую ознакомиться с исходниками.

Теперь обобщения по этому пункту на примерах из busybox. Все примеры взяты из udhcpc — крохотного DHCP клиента:

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

Листинг выше частично вошел сюда, т.к. подходит для еще одного примера.

Посмотрите: описание упакованных структур идет перед перечислением, отражающим размеры этих структур.

Если в этом же файле мы спустимся чуть пониже, то увидим, что объявления функций работы с опциями так же находятся в одном месте:

В udhcpc есть огромное количество опций, которые по умолчанию не используются. Каждая опция соответствует макросу, который закрывает ее номер.

Если бы они были раскомментированы — то это были бы макросы, которые не всплывают нигде в коде. Человек, который спросил бы «а зачем все эти опции?» искал бы ответ очень долго. И не нашел бы.

Как итог — у нас получилось подобие интерфейса, где комментарием закрыты те опции, методы для которых еще не реализованы.

Довольно сложный для восприятия аспект, требующий пояснения. В двух словах: если у вас есть функция, которая внутри проекта вызывается с n комбинациями различных параметров (где n — небольшое число), причем каждая комбинация вызывается по нескольку раз, имеет смысл сделать для каждой комбинации отдельную функцию, вызывающую внутри себя целевую функцию, но уже с нужными параметрами. Например. У нас есть функция, отправляющая пакеты на определенный порт и адрес:

Но dhcp не всегда нуждается в отправке пакета на один IP адрес. В основном используется широковещательная (BROADCAST) рассылка.

Но широковещательная отправка пакета — всего лишь отправка пакета по адресу, зарезервированному под broadcast. Собственно, для того, чтоб отправить широковещательный запрос, достаточно использовать описанную выше функцию, но в качестве адреса указать тот, что зарезервирован про бродкаст. Отсюда получаем функцию:

Профит этого в том, что везде, где мы будем встречать эту функцию, можно будет по названию понять, что она делает. Если бы мы использовали функцию udhcp_send_raw_packet, то нам бы осталось только гадать по параметрам.

Заключение

Пиши код так, чтобы те, кто будет его сопровождать любили тебя, а не ненавидели. Сложная гибкая реализация гораздо лучше простого костыля.

Описывай интерфейсы доступа, комментируй проблемные моменты. Не делай констант, от изменения которых придется переписывать весь код. Не допускай утечек памяти. Следи за безопасностью и отказоустойчивостью кода.

Источник

C Coding Style#

This document presents the preferred coding style for C programs in GNOME.

While coding style is very much a matter of taste, in GNOME we favor a coding style that promotes consistency, readability, and maintainability.

We present examples of good coding style as well as examples of bad style that is not acceptable in GNOME. Please try to submit patches that conform to GNOME’s coding style; this indicates that you have done your homework to respect the project’s goal of long-term maintainability. Patches with GNOME’s coding style will also be easier to review!

This document is intended for C code. Unlike C, other programming languages have their own official coding style recommendations; we encourage you to follow them wherever applicable.

These guidelines are heavily inspired by the GTK coding style document; the Linux kernel coding style; and the GNU coding standards. These are slight variations of each other, with particular modifications for each project’s particular needs and culture, and GNOME’s version is no different.

The single most important rule#

The single most important rule when writing code is this: check the surrounding code and try to imitate it.

As a maintainer it is dismaying to receive a patch that is obviously in a different coding style to the surrounding code. This is disrespectful, like someone tromping into a spotlessly-clean house with muddy shoes.

So, whatever this document recommends, if there is already written code and you are contributing to it, keep its current style consistent even if it is not your favorite style.

Most importantly, do not make your first contribution to a project a change in the coding style to suit your taste. That is incredibly disrespectful.

Line width#

Try to use lines of code between 80 and 120 characters long. This amount of text is easy to fit in most monitors with a decent font size. Lines longer than that become hard to read, and they mean that you should probably restructure your code. If you have too many levels of indentation, it means that you should fix your code anyway.

Indentation#

In general there are two preferred indentation styles for code in GNOME:

Linux Kernel style. Tabs with a length of 8 characters are used for the indentation, with K&R brace placement:

GNU style. Each new level is indented by 2 spaces, braces go on a line by themselves, and they are indented as well:

Both styles have their pros and cons. The most important things is to be consistent with the surrounding code. For example, the GTK library, which is GNOME’s widget toolkit, is written with the GNU style. Nautilus, GNOME’s file manager, is written in Linux kernel style. Both styles are perfectly readable and consistent when you get used to them.

Your first feeling when having to study or work on a piece of code that doesn’t have your preferred indentation style may be, how shall we put it, gut-wrenching. You should resist your inclination to reindent everything, or to use an inconsistent style for your patch. Remember the first rule: be consistent and respectful of that code’s customs, and your patches will have a much higher chance of being accepted without a lot of arguing about the right indentation style.

Tab characters#

Do not ever change the size of tabs in your editor; leave them as 8 spaces. Changing the size of tabs means that code that you didn’t write yourself will be perpetually misaligned.

Instead, set the indentation size as appropriate for the code you are editing. When writing in something other than Linux kernel style, you may even want to tell your editor to automatically convert all tabs to 8 spaces, so that there is no ambiguity about the intended amount of space.

Braces#

Curly braces should not be used for single statement blocks:

The “no block for single statements” rule has only four exceptions:

In GNU style, if either side of an if … else statement has braces, both sides should, to match up indentation:

If the single statement covers multiple lines, e.g. for functions with many arguments, and it is followed by else or else if :

If the condition is composed of many lines:

Such long conditions are usually hard to understand. A good practice is to set the condition to a boolean variable, with a good name for that variable. Another way is to move the long condition to a function.

In general, new blocks should be placed on a new indentation level, like this:

While curly braces for function definitions should rest on a new line they should not add an indentation level:

Conditions#

Do not check boolean values for equality. By using implicit comparisons, the resulting code can be read more like conversational English. Another rationale is that a ‘true’ value may not be necessarily equal to whatever the TRUE macro uses. For example:

The C language uses the value 0 for many purposes. As a numeric value, the end of a string, a null pointer and the FALSE boolean. To make the code clearer, you should write code that highlights the specific way 0 is used. So when reading a comparison, it is possible to know the variable type. For boolean variables, an implicit comparison is appropriate because it’s already a logical expression. Other variable types are not logical expressions by themselves, so an explicit comparison is better:

Functions#

Functions should be declared by placing the returned value on a separate line from the function name:

The argument list must be broken into a new line for each argument, with the argument names right aligned, taking into account pointers:

If you use Emacs, you can use M-x align to do this kind of alignment automatically. Just put the point and mark around the function’s prototype, and invoke that command.

The alignment also holds when invoking a function without breaking the line length limit:

Whitespace#

Always put a space before an opening parenthesis but never after:

When declaring a structure type use newlines to separate logical sections of the structure:

Do not eliminate whitespace and newlines just because something would fit on a single line:

Do eliminate trailing whitespace on any line, preferably as a separate patch or commit. Never use empty lines at the beginning or at the end of a file.

The switch statement#

A switch should open a block on a new indentation level, and each case should start on the same indentation level as the curly braces, with the case block on a new indentation level:

It is preferable, though not mandatory, to separate the various cases with a newline.

The break statement for the default case is not mandatory.

If switching over an enumerated type, a case statement must exist for every member of the enumerated type. For members you do not want to handle, alias their case statements to default :

If a case block needs to declare new variables, the same rules as the inner blocks apply (see above); the break statement should be placed outside of the inner block:

Header files#

The only major rule for headers is that the function definitions should be vertically aligned in three columns:

The maximum width of each column is given by the longest element in the column:

It is also possible to align the columns to the next tab, to avoid having to reformat headers every time you add a new function:

If you are creating a public library, try to export a single public header file that in turn includes all the smaller header files into it. This is so that public headers are never included directly; rather a single include is used in applications. For example, GTK uses the following in its header files that should not be included directly by applications:

For libraries, all headers should have inclusion guards (for internal usage) and C++ guards. These provide the extern “C” magic that C++ requires to include plain C headers:

GObject classes#

GObject class definitions and implementations require some additional coding style notices, and should always be correctly namespaced.

Type declarations should be placed at the beginning of the file:

This includes enumeration types:

And callback types:

Instance structures should be declared using the G_DECLARE_FINAL_TYPE() or G_DECLARE_DERIVABLE_TYPE() macros:

For final types, private data can be stored in the object struct, which should be defined in the C file:

For derivable types, private data must be stored in a private struct in the C file, configured using G_DEFINE_TYPE_WITH_PRIVATE() and accessed using the generated _get_instance_private() function:

Interfaces should be declared using the G_DECLARE_INTERFACE() macro:

Memory allocation#

Macros#

Try to avoid private macros unless strictly necessary. Remember to #undef them at the end of a block or a series of functions needing them.

Inline functions are usually preferable to private macros.

Public macros should not be used unless they evaluate to a constant.

Public API#

Avoid exporting variables as public API, since this is cumbersome on some platforms. It is always preferable to add getters and setters instead. Also, beware global variables in general.

To avoid exposing private API in the shared library, it is recommended to default to a hidden symbol visibility, and explicitly annotate public symbols in the header file.

Non-exported functions that are only needed in one source file should be declared static to that file.

Источник

Recommended C Style and Coding Standards

L.W. Cannon

R.A. Elliott

L.W. Kirchhoff

J.H. Miller

J.M. Milner

R.W. Mitze

E.P. Schan

N.O. Whittington

Henry Spencer

David Keppel

Mark Brader

This document is an updated version of the Indian Hill C Style and Coding Standards paper, with modifications by the last three authors. It describes a recommended coding standard for C programs. The scope is coding style, not functional organization.

Introduction

This document is a modified version of a document from a committee formed at AT&T’s Indian Hill labs to establish a common set of coding standards and recommendations for the Indian Hill community. The scope of this work is C coding style. Good style should encourage consistent layout, improve portability, and reduce errors. This work does not cover functional organization, or general issues such as the use of goto s. We( The opinions in this document do not reflect the opinions of all authors. This is still an evolving document. Please send comments and suggestions to pardo@cs.washington.edu or !uw-beaver!june!pardo) have tried to combine previous work [1,6,8] on C style into a uniform set of standards that should be appropriate for any project using C, although parts are biased towards particular systems. Of necessity, these standards cannot cover all situations. Experience and informed judgement count for much. Programmers who encounter unusual situations should consult either experienced C programmers or code written by experienced C programmers (preferably following these rules).

The standards in this document are not of themselves required, but individual institutions or groups may adopt part or all of them as a part of program acceptance. It is therefore likely that others at your institution will code in a similar style. Ultimately, the goal of these standards is to increase portability, reduce maintenance, and above all improve clarity.

Many of the style choices here are somewhat arbitrary. Mixed coding style is harder to maintain than bad coding style. When changing existing code it is better to conform to the style (indentation, spacing, commenting, naming conventions) of the existing code than it is to blindly follow this document.

File Organization

A file consists of various sections that should be separated by several blank lines. Although there is no maximum length limit for source files, files with more than about 1000 lines are cumbersome to deal with. The editor may not have enough temp space to edit the file, compilations will go more slowly, etc. Many rows of asterisks, for example, present little information compared to the time it takes to scroll past, and are discouraged. Lines longer than 79 columns are not handled well by all terminals and should be avoided if possible. Excessively long lines which result from deep indenting are often a symptom of poorly-organized code.

File Naming Conventions

File names are made up of a base name, and an optional period and suffix. The first character of the name should be a letter and all characters (except the period) should be lower-case letters and numbers. The base name should be eight or fewer characters and the suffix should be three or fewer characters (four, if you include the period). These rules apply to both program files and default files used and produced by the program (e.g., «rogue.sav»).

Some compilers and tools require certain suffix conventions for names of files [5]. The following suffixes are required:

The following conventions are universally followed:

In addition, it is conventional to use Makefile (not makefile ) for the control file for make (for systems that support it) and «README» for a summary of the contents of the directory or directory tree.

Program Files

The suggested order of sections for a program file is as follows

First in the file is a prologue that tells what is in that file. A description of the purpose of the objects in the files (whether they be functions, external data declarations or definitions, or something else) is more useful than a list of the object names. The prologue may optionally contain author(s), revision control information, references, etc.

Any header file includes should be next. If the include is for a non-obvious reason, the reason should be commented. In most cases, system include files like stdio.h should be included before user include files.

Any defines and typedefs that apply to the file as a whole are next. One normal order is to have «constant» macros first, then «function» macros, then typedefs and enums.

Next come the global (external) data declarations, usually in the order: externs, non-static globals, static globals. If a set of defines applies to a particular piece of global data (such as a flags word), the defines should be immediately after the data declaration or embedded in structure declarations, indented to put the defines one level deeper than the first keyword of the declaration to which they apply.

The functions come last, and should be in some sort of meaningful order. Like functions should appear together. A «breadth-first» approach (functions on a similar level of abstraction together) is preferred over depth-first (functions defined as soon as possible before or after their calls). Considerable judgement is called for here. If defining large numbers of essentially-independent utility functions, consider alphabetical order.

Header Files

Avoid private header filenames that are the same as library header filenames. The statement #include «math.h» will include the standard library math header file if the intended one is not found in the current directory. If this is what you want to happen, comment this fact. Don’t use absolute pathnames for header files. Use the construction for getting them from a standard place, or define them relative to the current directory. The «include-path» option of the C compiler (-I on many systems) is the best way to handle extensive private libraries of header files; it permits reorganizing the directory structure without having to alter source files.

Header files that declare functions or external variables should be included in the file that defines the function or variable. That way, the compiler can do type checking and the external declaration will always agree with the definition.

Defining variables in a header file is often a poor idea. Frequently it is a symptom of poor partitioning of code between files. Also, some objects like typedefs and initialized data definitions cannot be seen twice by the compiler in one compilation. On some systems, repeating uninitialized declarations without the extern keyword also causes problems. Repeated declarations can happen if include files are nested and will cause the compilation to fail.

Header files should not be nested. The prologue for a header file should, therefore, describe what other headers need to be #included for the header to be functional. In extreme cases, where a large number of header files are to be included in several different source files, it is acceptable to put all common #includes in one include file.

This double-inclusion mechanism should not be relied upon, particularly to perform nested includes.

Other Files

It is conventional to have a file called README to document both «the bigger picture» and issues for the program as a whole. For example, it is common to include a list of all conditional compilation flags and what they mean. It is also common to list files that are machine dependent, etc.

Comments

The comments should describe what is happening, how it is being done, what parameters mean, which globals are used and which are modified, and any restrictions or bugs. Avoid, however, comments that are clear from the code, as such information rapidly gets out of date. Comments that disagree with the code are of negative value. Short comments should be what comments, such as «compute mean value», rather than how comments such as «sum of values divided by n». C is not assembler; putting a comment at the top of a 3-10 line section telling what it does overall is often more useful than a comment on each line describing micrologic.

Comments should justify offensive code. The justification should be that something bad will happen if unoffensive code is used. Just making code faster is not enough to rationalize a hack; the performance must be shown to be unacceptable without the hack. The comment should explain the unacceptable behavior and describe why the hack is a «good» fix.

Comments that describe data structures, algorithms, etc., should be in block comment form with the opening /* in columns 1-2, a * in column 2 before each line of comment text, and the closing */ in columns 2-3. An alternative is to have ** in columns 1-2, and put the closing */ also in 1-2.

Note that grep ‘^.\*’ will catch all block comments in the file.

Very short comments may appear on the same line as the code they describe, and should be tabbed over to separate them from the statements. If more than one short comment appears in a block of code they should all be tabbed to the same tab setting.

Declarations

Global declarations should begin in column 1. All external data declaration should be preceded by the extern keyword. If an external variable is an array that is defined with an explicit size, then the array bounds must be repeated in the extern declaration unless the size is always encoded in the array (e.g., a read-only character array that is always null-terminated). Repeated size declarations are particularly beneficial to someone picking up code written by another.

The «pointer» qualifier, ‘ * ‘, should be with the variable name rather than with the type.

instead of which is wrong, since ‘ t ‘ and ‘ u ‘ do not get declared as pointers.

Unrelated declarations, even of the same type, should be on separate lines. A comment describing the role of the object being declared should be included, with the exception that a list of #define d constants do not need comments if the constant names are sufficient documentation. The names, values, and comments are usually tabbed so that they line up underneath each other. Use the tab character rather than blanks (spaces). For structure and union template declarations, each element should be alone on a line with a comment describing it. The opening brace ( < ) should be on the same line as the structure tag, and the closing brace ( >) should be in column 1.

enum s might be better anyway.

Any variable whose initial value is important should be explicitly initialized, or at the very least should be commented to indicate that C’s default initialization to zero is being relied upon. The empty initializer, » <> «, should never be used. Structure initializations should be fully parenthesized with braces. Constants used to initialize longs should be explicitly long. Use capital letters; for example two long » 2l » looks a lot like » 21 «, the number twenty-one.

In any file which is part of a larger whole rather than a self-contained program, maximum use should be made of the static keyword to make functions and variables local to single files. Variables in particular should be accessible from other files only when there is a clear need that cannot be filled in another way. Such usage should be commented to make it clear that another file’s variables are being used; the comment should name the other file. If your debugger hides static objects you need to see during debugging, declare them as STATIC and #define STATIC as needed.

The most important types should be highlighted by typedeffing them, even if they are only integers, as the unique name makes the program easier to read (as long as there are only a few things typedeffed to integers!). Structures may be typedeffed when they are declared. Give the struct and the typedef the same name.

Function Declarations

Each function should be preceded by a block comment prologue that gives a short description of what the function does and (if not clear) how to use it. Discussion of non-trivial design decisions and side-effects is also appropriate. Avoid duplicating information clear from the code.

The function return type should be alone on a line, (optionally) indented one stop4.

#define void or #define void int for compilers without the void keyword. If the value returned requires a long explanation, it should be given in the prologue; otherwise it can be on the same line as the return type, tabbed over. The function name (and the formal parameter list) should be alone on a line, in column 1. Destination (return value) parameters should generally be first (on the left). All formal parameter declarations, local declarations and code within the function body should be tabbed over one stop. The opening brace of the function body should be alone on a line beginning in column 1.

Each parameter should be declared (do not default to int ). In general the role of each variable in the function should be described. This may either be done in the function comment or, if each declaration is on its own line, in a comment on that line. Loop counters called «i», string pointers called «s», and integral types called «c» and used for characters are typically excluded. If a group of functions all have a like parameter or local variable, it helps to call the repeated variable by the same name in all functions. (Conversely, avoid using the same name for different purposes in related functions.) Like parameters should also appear in the same place in the various argument lists.

Comments for parameters and local variables should be tabbed so that they line up underneath each other. Local variable declarations should be separated from the function’s statements by a blank line.

Be careful when you use or declare functions that take a variable number of arguments («varargs»). There is no truly portable way to do varargs in C. Better to design an interface that uses a fixed number of arguments. If you must have varargs, use the library macros for declaring functions with variant argument lists.

If the function uses any external variables (or functions) that are not declared globally in the file, these should have their own declarations in the function body using the extern keyword.

Whitespace

Use vertical and horizontal whitespace generously. Indentation and spacing should reflect the block structure of the code; e.g., there should be at least 2 blank lines between the end of one function and the comments for the next.

A long string of conditional operators should be split onto separate lines.

Might be better as Similarly, elaborate for loops should be split onto different lines. Other complex expressions, particularly those using the ternary ?: operator, are best split on to several lines, too. Keywords that are followed by expressions in parentheses should be separated from the left parenthesis by a blank. (The sizeof operator is an exception.) Blanks should also appear after commas in argument lists to help separate the arguments visually. On the other hand, macro definitions with arguments must not have a blank between the name and the left parenthesis, otherwise the C preprocessor will not recognize the argument list.

Examples

Simple Statements

There should be only one statement per line unless the statements are very closely related.

The null body of a for or while loop should be alone on a line and commented so that it is clear that the null body is intentional and not missing code.

Do not default the test for non-zero, i.e.

The non-zero test is often defaulted for predicates and other functions or expressions which meet the following restrictions:

Evaluates to 0 for false, nothing else.

It is common practice to declare a boolean type » bool » in a global include file. The special names improve readability immensely.

or Even with these declarations, do not check a boolean value for equality with 1 (TRUE, YES, etc.); instead test for inequality with 0 (FALSE, NO, etc.). Most functions are guaranteed to return 0 if false, but only non-zero if true. Thus, must be written It is even better (where possible) to rename the function/variable or rewrite the expression so that the meaning is obvious without a comparison to true or false (e.g., rename to isvalid() ).

There is a time and a place for embedded assignment statements. In some constructs there is no better way to accomplish the results without making the code bulkier and less readable.

When a goto is necessary the accompanying label should be alone on a line and tabbed one stop to the left of the code that follows. The goto should be commented (possibly in the block header) as to its utility and purpose. Continue should be used sparingly and near the top of the loop. Break is less troublesome.

parameters to non-prototyped functions sometimes need to be promoted explicitly. If, for example, a function expects a 32-bit long and gets handed a 16-bit int instead, the stack can get misaligned. Problems occur with pointer, integral, and floating-point values.

Compound Statements

A compound statement is a list of statements enclosed by braces. There are many common ways of formatting the braces. Be consistent with your local standard, if you have one, or pick one and use it consistently. When editing someone else’s code, always use the style used in that code.

The style above is called «K&R style», and is preferred if you haven’t already got a favorite. With K&R style, the else part of an if-else statement and the while part of a do-while statement should appear on the same line as the close brace. With most other styles, the braces are always alone on a line.

When a block of code has several labels (unless there are a lot of them), the labels are placed on separate lines. The fall-through feature of the C switch statement, (that is, when there is no break between a code segment and the next case statement) must be commented for future maintenance. A lint-style comment/directive is best.

Here, the last break is unnecessary, but is required because it prevents a fall-through error if another case is added later after the last one. The default case, if used, should be last and does not require a break if it is last.

Whenever an if-else statement has a compound statement for either the if or else section, the statements of both the if and else sections should both be enclosed in braces (called fully bracketed syntax ).

Braces are also essential in if-if-else sequences with no second else such as the following, which will be parsed incorrectly if the brace after (ex1) and its mate are omitted:

An if-else with else if should be written with the else conditions left-justified.

The format then looks like a generalized switch statement and the tabbing reflects the switch between exactly one of several alternatives rather than a nesting of statements.

Do-while loops should always have braces around the body.

The following code is very dangerous:

Note that on systems where CIRCUIT is not defined the statement » ++i; » will only get executed when expr is false! This example points out both the value of naming macros with CAPS and of making code fully-bracketed.

The «flattened» indentation tells the reader that the boolean test is invariant over the rest of the enclosing block.

Operators

If you think an expression will be hard to read, consider breaking it across lines. Splitting at the lowest-precedence operator near the break is best. Since C has some unexpected precedence rules, expressions involving mixed operators should be parenthesized. Too many parentheses, however, can make a line harder to read because humans aren’t good at parenthesis-matching.

There is a time and place for the binary comma operator, but generally it should be avoided. The comma operator is most useful to provide multiple initializations or operations, as in for statements. Complex expressions, for instance those with nested ternary ?: operators, can be confusing and should be avoided if possible. There are some macros like getchar where both the ternary operator and comma operators are useful. The logical expression operand before the ?: should be parenthesized and both return values must be the same type.

Naming Conventions

Individual projects will no doubt have their own naming conventions. There are some general rules however.

Names with leading and trailing underscores are reserved for system purposes and should not be used for any user-created names. Most systems use them for names that the user should not have to know. If you must have your own private identifiers, begin them with a letter or two identifying the package to which they belong.

#define constants should be in all CAPS.

Enum constants are Capitalized or in all CAPS

Function, typedef, and variable names, as well as struct, union, and enum tag names should be in lower case.

Many macro «functions» are in all CAPS. Some macros (such as getchar and putchar ) are in lower case since they may also exist as functions. Lower-case macro names are only acceptable if the macros behave like a function call, that is, they evaluate their parameters exactly once and do not assign values to named parameters. Sometimes it is impossible to write a macro that behaves like a function even though the arguments are evaluated exactly once.

Similarly, avoid names that look like each other. On many terminals and printers, ‘l’, ‘1’ and ‘I’ look quite similar. A variable named ‘l’ is particularly bad because it looks so much like the constant ‘1’.

In general, global names (including enum s) should have a common prefix identifying the module that they belong with. Globals may alternatively be grouped in a global structure. Typedeffed names often have » _t » appended to their name.

Avoid names that might conflict with various standard library names. Some systems will include more library code than you want. Also, your program may be extended someday.

Constants

Numerical constants should not be coded directly. The #define feature of the C preprocessor should be used to give constants meaningful names. Symbolic constants make the code easier to read. Defining the value in one place also makes it easier to administer large programs since the constant value can be changed uniformly by changing only the define. The enumeration data type is a better way to declare variables that take on only a discrete set of values, since additional type checking is often available. At the very least, any directly-coded numerical constant must have a comment explaining the derivation of the value.

Constants should be defined consistently with their use; e.g. use 540.0 for a float instead of 540 with an implicit float cast. There are some cases where the constants 0 and 1 may appear as themselves instead of as defines. For example if a for loop indexes through an array, then

is reasonable while the code is not. In the last example front_door is a pointer. When a value is a pointer it should be compared to NULL instead of 0. NULL is available either as part of the standard I/O library’s header file stdio.h or in stdlib.h for newer systems. Even simple values like 1 or 0 are often better expressed using defines like TRUE and FALSE (sometimes YES and NO read better).

Simple character constants should be defined as character literals rather than numbers. Non-text characters are discouraged as non-portable. If non-text characters are necessary, particularly if they are used in strings, they should be written using a escape character of three octal digits rather than one (e.g., ‘\007’ ). Even so, such usage should be considered machine-dependent and treated as such.

Macros

Complex expressions can be used as macro parameters, and operator-precedence problems can arise unless all occurrences of parameters have parentheses around them. There is little that can be done about the problems caused by side effects in parameters except to avoid side effects in expressions (a good idea anyway) and, when possible, to write macros that evaluate their parameters exactly once. There are times when it is impossible to write macros that act exactly like functions.

Some macros also exist as functions (e.g., getc and fgetc ). The macro should be used in implementing the function so that changes to the macro will be automatically reflected in the function. Care is needed when interchanging macros and functions since function parameters are passed by value, while macro parameters are passed by name substitution. Carefree use of macros requires that they be declared carefully.

Macros should avoid using globals, since the global name may be hidden by a local declaration. Macros that change named parameters (rather than the storage they point at) or may be used as the left-hand side of an assignment should mention this in their comments. Macros that take no parameters but reference variables, are long, or are aliases for function calls should be given an empty parameter list, e.g.,

Macros save function call/return overhead, but when a macro gets long, the effect of the call/return becomes negligible, so a function should be used instead.

In some cases it is appropriate to make the compiler insure that a macro is terminated with a semicolon.

Conditional Compilation.

Conditional compilation is useful for things like machine-dependencies, debugging, and for setting certain options at compile-time. Beware of conditional compilation. Various controls can easily combine in unforeseen ways. If you #ifdef machine dependencies, make sure that when no machine is specified, the result is an error, not a default machine. (Use » #error » and indent it so it works with older compilers.) If you #ifdef optimizations, the default should be the unoptimized code rather than an uncompilable program. Be sure to test the unoptimized code.

Note that the text inside of an #ifdeffed section may be scanned (processed) by the compiler, even if the #ifdef is false. Thus, even if the #ifdeffed part of the file never gets compiled (e.g., #ifdef COMMENT ), it cannot be arbitrary text.

Put #ifdefs in header files instead of source files when possible. Use the #ifdefs to define macros that can be used uniformly in the code. For instance, a header file for checking memory allocation might look like (omitting definitions for REALLOC and FREE ):

Conditional compilation should generally be on a feature-by-feature basis. Machine or operating system dependencies should be avoided in most cases.

Debugging

If you use enum s, the first enum constant should have a non-zero value, or the first constant should indicate an error.

Uninitialized values will then often «catch themselves».

Check for error return values, even from functions that «can’t» fail. Consider that close() and fclose() can and do fail, even when all prior file operations have succeeded. Write your own functions so that they test for errors and return error values or abort the program in a well-defined way. Include a lot of debugging and error-checking code and leave most of it in the finished product. Check even for «impossible» errors. [8]

Use the assert facility to insist that each function is being passed well-defined values, and that intermediate results are well-formed.

Build in the debug code using as few #ifdefs as possible. For instance, if » mm_malloc » is a debugging memory allocator, then MALLOC will select the appropriate allocator, avoids littering the code with #ifdefs, and makes clear the difference between allocation calls being debugged and extra memory that is allocated only during debugging.

Check bounds even on things that «can’t» overflow. A function that writes on to variable-sized storage should take an argument maxsize that is the size of the destination. If there are times when the size of the destination is unknown, some ‘magic’ value of maxsize should mean «no bounds checks». When bound checks fail, make sure that the function does something useful such as abort or return an error status.

In all, remember that a program that produces wrong answers twice as fast is infinitely slower. The same is true of programs that crash occasionally or clobber valid data.

Portability

The advantages of portable code are well known. This section gives some guidelines for writing portable code. Here, «portable» means that a source file can be compiled and executed on different machines with the only change being the inclusion of possibly different header files and the use of different compiler flags. The header files will contain #defines and typedefs that may vary from machine to machine. In general, a new «machine» is different hardware, a different operating system, a different compiler, or any combination of these. Reference [1] contains useful information on both style and portability. The following is a list of pitfalls to be avoided and recommendations to be considered when designing portable code:

Write portable code first, worry about detail optimizations only on machines where they prove necessary. Optimized code is often obscure. Optimizations for one machine may produce worse code on another. Document performance hacks and localize them as much as possible. Documentation should explain how it works and why it was needed (e.g., «loop executes 6 zillion times»).

Recognize that some things are inherently non-portable. Examples are code to deal with particular hardware registers such as the program status word, and code that is designed to support a particular piece of hardware, such as an assembler or I/O driver. Even in these cases there are many routines and data organizations that can be made machine independent.

Organize source files so that the machine-independent code and the machine-dependent code are in separate files. Then if the program is to be moved to a new machine, it is a much easier task to determine what needs to be changed. Comment the machine dependence in the headers of the appropriate files.

Any behavior that is described as «implementation defined» should be treated as a machine (compiler) dependency. Assume that the compiler or hardware does it some completely screwy way.

Pay attention to word sizes. Objects may be non-intuitive sizes, Pointers are not always the same size as int s, the same size as each other, or freely interconvertible. The following table shows bit sizes for basic types in C for various machines and compilers.

typepdp11VAX/1168000Cray-2UnisysHarris80386
seriesfamily1100H800
char8888988
short16168/1664(32)18248/16
int163216/3264(32)362416/32
long32323264364832
char*16323264722416/32/48
int*16323264(24)722416/32/48
int(*)()163232645762416/32/48
TypeMinimumNo Smaller
# BitsThan
char8
short16char
int16short
long32int
float24
double38float
any *14
char *15any *
void *15any *

The integer constant zero may be cast to any pointer type. The resulting pointer is called a null pointer for that type, and is different from any other pointer of that type. A null pointer always compares equal to the constant zero. A null pointer might not compare equal with a variable that has the value zero. Null pointers are not always stored with all bits zero. Null pointers for two different types are sometimes different. A null pointer of one type cast in to a pointer of another type will be cast in to the null pointer for that second type.

On ANSI compilers, when two pointers of the same type access the same storage, they will compare as equal. When non-zero integer constants are cast to pointer types, they may become identical to other pointers. On non-ANSI compilers, pointers that access the same storage may compare as different. The following two pointers, for instance, may or may not compare equal, and they may or may not access the same storage6.

The code may also fail to compile, fault on pointer creation, fault on pointer comparison, or fault on pointer dereferences.

If you need ‘magic’ pointers other than NULL, either allocate some storage or treat the pointer as a machine dependence.

On some machines the first half of a double may be a float with similar value. Do not depend on this.

Avoid assuming ASCII. If you must assume, document and localize. Remember that characters may hold (much) more than 8 bits.

Code that takes advantage of the two’s complement representation of numbers on most machines should not be used. Optimizations that replace arithmetic operations with equivalent shifting operations are particularly suspect. If absolutely necessary, machine-dependent code should be #ifdeffed or operations should be performed by #ifdeffed macros. You should weigh the time savings with the potential for obscure and difficult bugs when your code is moved.

Data alignment is also important. For instance, on various machines a 4-byte integer may start at any address, start only at an even address, or start only at a multiple-of-four address. Thus, a particular structure may have its elements at different offsets on different machines, even when given elements are the same size on all machines. Indeed, a structure of a 32-bit pointer and an 8-bit character may be 3 sizes on 3 different machines. As a corollary, pointers to objects may not be interchanged freely; saving an integer through a pointer to 4 bytes starting at an odd address will sometimes work, sometimes cause a core dump, and sometimes fail silently (clobbering other data in the process). Pointer-to-character is a particular trouble spot on machines which do not address to the byte. Alignment considerations and loader peculiarities make it very rash to assume that two consecutively-declared variables are together in memory, or that a variable of one type is aligned appropriately to be used as another type.

The bytes of a word are of increasing significance with increasing address on machines such as the VAX (little-endian) and of decreasing significance with increasing address on other machines such as the 68000 (big-endian). The order of bytes in a word and of words in larger objects (say, a double word) might not be the same. Hence any code that depends on the left-right orientation of bits in an object deserves special scrutiny. Bit fields within structure members will only be portable so long as two separate fields are never concatenated and treated as a unit. [1,3] Actually, it is nonportable to concatenate any two variables.

There may be unused holes in structures. Suspect unions used for type cheating. Specifically, a value should not be stored as one type and retrieved as another. An explicit tag field for unions may be useful.

Different compilers use different conventions for returning structures. This causes a problem when libraries return structure values to code compiled with a different compiler. Structure pointers are not a problem.

Do not make assumptions about the parameter passing mechanism. especially pointer sizes and parameter evaluation order, size, etc. The following code, for instance, is very nonportable.

On some machines, the null character pointer ((char *)0) is treated the same way as a pointer to a null string. Do not depend on this.

Do not modify string constants7.

Some libraries attempt to modify and then restore read-only string variables. Programs sometimes won’t port because of these broken libraries. The libraries are getting better. One particularly notorious (bad) example is

The address space may have holes. Simply computing the address of an unallocated element in an array (before or after the actual storage of the array) may crash the program. If the address is used in a comparison, sometimes the program will run but clobber data, give wrong answers, or loop forever. In ANSI C, a pointer into an array of objects may legally point to the first element after the end of the array; this is usually safe in older implementations. This «outside» pointer may not be dereferenced.

Word size also affects shifts and masks. The following code will clear only the three rightmost bits of an int on some 68000s. On other machines it will also clear the upper two bytes.

Use instead which works properly on all machines. Bitfields do not have these problems.

Side effects within expressions can result in code whose semantics are compiler-dependent, since C’s order of evaluation is explicitly undefined in most places. Notorious examples include the following.

In the above example, we know only that the subscript into b has not been incremented. The index into a could be the value of i either before or after the increment. In the second example, the address of » bar->next » may be computed before the value is assigned to » bar «. In the third example, bar can be assigned before bar->next. Although this appears to violate the rule that «assignment proceeds right-to-left», it is a legal interpretation. Consider the following example: The value that » i » is assigned must be a value that is typed as if assignment proceeded right-to-left. However, » i » may be assigned the value » (long)(short)new » before » a[i] » is assigned to. Compilers do differ.

Be suspicious of numeric values appearing in the code («magic numbers»).

Avoid preprocessor tricks. Tricks such as using /**/ for token pasting and macros that rely on argument string expansion will break reliably.

Will only sometimes be expanded to Be aware, however, that tricky preprocessors may cause macros to break accidentally on some machines. Consider the following two versions of a macro. The second version of LOOKUP can be expanded in two different ways and will cause code to break mysteriously.

Become familiar with existing library functions and defines. (But not too familiar. The internal details of library facilities, as opposed to their external interfaces, are subject to change without warning. They are also often quite unportable.) You should not be writing your own string compare routine, terminal control routines, or making your own defines for system structures. «Rolling your own» wastes your time and makes your code less readable, because another reader has to figure out whether you’re doing something special in that reimplemented stuff to justify its existence. It also prevents your program from taking advantage of any microcode assists or other means of improving performance of system routines. Furthermore, it’s a fruitful source of bugs. If possible, be aware of the differences between the common libraries (such as ANSI, POSIX, and so on).

Use lint when it is available. It is a valuable tool for finding machine-dependent constructs as well as other inconsistencies or program bugs that pass the compiler. If your compiler has switches to turn on warnings, use them.

Suspect labels inside blocks with the associated switch or goto outside the block.

Wherever the type is in doubt, parameters should be cast to the appropriate type. Always cast NULL when it appears in non-prototyped function calls. Do not use function calls as a place to do type cheating. C has confusing promotion rules, so be careful. For example, if a function expects a 32-bit long and it is passed a 16-bit int the stack can get misaligned, the value can get promoted wrong, etc.

Use explicit casts when doing arithmetic that mixes signed and unsigned values.

Some linkers convert names to lower-case and some only recognize the first six letters as unique. Programs may break quietly on these systems.

Beware of compiler extensions. If used, document and consider them as machine dependencies.

A program cannot generally execute code in the data segment or write into the code segment. Even when it can, there is no guarantee that it can do so reliably.

ANSI C

Modern C compilers support some or all of the ANSI proposed standard C. Whenever possible, write code to run under standard C, and use features such as function prototypes, constant storage, and volatile storage. Standard C improves program performance by giving better information to optimizers. Standard C improves portability by insuring that all compilers accept the same input language and by providing mechanisms that try to hide machine dependencies or emit warnings about code that may be machine-dependent.

Compatibility

Write code that is easy to port to older compilers. For instance,

conditionally #define new (standard) keywords such as const and volatile in a global .h file. Standard compilers pre-define the preprocessor symbol __STDC__ 8.

Note that under ANSI C, the ‘#’ for a preprocessor directive must be the first non-whitespace character on a line. Under older compilers it must be the first character on the line.

When a static function has a forward declaration, the forward declaration must include the storage class. For older compilers, the class must be » extern «. For ANSI compilers, the class must be » static «. but global functions must still be declared as » extern «. Thus, forward declarations of static functions should use a #define such as FWD_STATIC that is #ifdeffed as appropriate.

An » #ifdef NAME » should end with either » #endif » or » #endif /* NAME */ «, not with » #endif NAME «. The comment should not be used on short #ifdefs, as it is clear from the code.

Formatting

The style for ANSI C is the same as for regular C, with two notable exceptions: storage qualifiers and parameter lists.

Because const and volatile have strange binding rules, each const or volatile object should have a separate declaration.

Prototyped functions merge parameter declaration and definition in to one list. parameters should be commented in the function comment.

Prototypes

Function prototypes should be used to make code more robust and to make it run faster. Unfortunately, the prototyped declaration

is incompatible with the definition The prototype says that c is to be passed as the most natural type for the machine, possibly a byte. The non-prototyped (backwards-compatible) definition implies that c is always passed as an int

If a function has promotable parameters then the caller and callee must be compiled identically. Either both must use function prototypes or neither can use prototypes. The problem can be avoided if parameters are promoted when the program is designed. For example, bork can be defined to take an int parameter.

The above declaration works if the definition is prototyped.

Unfortunately, the prototyped syntax will cause non-ANSI compilers to reject the program.

It is easy to write external declarations that work with both prototyping and with older compilers10.

Note that using PROTO violates the rule «don’t change the syntax via macro substitution.» It is regrettable that there isn’t a better solution.

Note that PROTO must be used with double parentheses.

In the end, it may be best to write in only one style (e.g., with prototypes). When a non-prototyped version is needed, it is generated using an automatic conversion tool.

Pragmas

Pragmas are used to introduce machine-dependent code in a controlled way. Obviously, pragmas should be treated as machine dependencies. Unfortunately, the syntax of ANSI pragmas makes it impossible to isolate them in machine-dependent headers.

Pragmas are of two classes. Optimizations may safely be ignored. Pragmas that change the system behavior («required pragmas») may not. Required pragmas should be #ifdeffed so that compilation will abort if no pragma is selected.

Special Considerations

This section contains some miscellaneous do’s and don’ts.

Don’t change syntax via macro substitution. It makes the program unintelligible to all but the perpetrator.

Compilers have bugs. Common trouble spots include structure assignment and bitfields. You cannot generally predict which bugs a compiler has. You could write a program that avoids all constructs that are known broken on all compilers. You won’t be able to write anything useful, you might still encounter bugs, and the compiler might get fixed in the meanwhile. Thus, you should write «around» compiler bugs only when you are forced to use a particular buggy compiler.

Do not rely on automatic beautifiers. The main person who benefits from good program style is the programmer him/herself, and especially in the early design of handwritten algorithms or pseudo-code. Automatic beautifiers can only be applied to complete, syntactically correct programs and hence are not available when the need for attention to white space and indentation is greatest. Programmers can do a better job of making clear the complete visual layout of a function or file, with the normal attention to detail of a careful programmer. (In other words, some of the visual layout is dictated by intent rather than syntax and beautifiers cannot read minds.) Sloppy programmers should learn to be careful programmers instead of relying on a beautifier to make their code readable.

Accidental omission of the second » = » of the logical compare is a problem. Use explicit tests. Avoid assignment with implicit test.

When embedded assignment is used, make the test explicit so that it doesn’t get «fixed» later.

Explicitly comment variables that are changed out of the normal control flow, or other code that is likely to break during maintenance.

Modern compilers will put variables in registers automatically. Use the register sparingly to indicate the variables that you think are most critical. In extreme cases, mark the 2-4 most critical values as register and mark the rest as REGISTER. The latter can be #defined to register on those machines with many registers.

Lint is a C program checker [2][11] that examines C source files to detect and report type incompatibilities, inconsistencies between function definitions and calls, potential program bugs, etc. The use of lint on all programs is strongly recommended, and it is expected that most projects will require programs to use lint as part of the official acceptance procedure.

It should be noted that the best way to use lint is not as a barrier that must be overcome before official acceptance of a program, but rather as a tool to use during and after changes or additions to the code. Lint can find obscure bugs and insure portability before problems occur. Many messages from lint really do indicate something wrong. One fun story is about is about a program that was missing an argument to ‘ fprintf ‘.

The author never had a problem. But the program dumped core every time an ordinary user made a mistake on the command line. Many versions of lint will catch this.

Источник

90 рекомендаций по стилю написания программ на C++

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

1 Введение

Настоящий документ содержит рекомендации по написанию программ на языке C++.

Рекомендации основаны на установившихся стандартах, собранных из различных источников, личного опыта, частных требований и потребностей определённых проектов, а также почерпнутых из источников (см. ниже).

Но для появления ещё одного списка рекомендаций, помимо указанных источников, есть несколько причин. Основная причина — их излишняя обобщённость, поскольку зачастую требуется задать частные правила (в особенности правила именования). Данный документ содержит комментарии, что делает его более удобным в использовании при проведении ревизий кода, чем другие уже существующие документы. К тому же, рекомендации по программированию обычно вперемешку содержат описания проблем стиля и технических проблем, что не совсем удобно. Этот документ не содержит каких-либо технических рекомендаций по C++, делая упор на вопросах стиля.

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

1.1 Формат документа

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

Рекомендации отображаются следующим образом:

n. Короткое описание рекомендации.
Объяснение, происхождение и дополнительная информация.

Комментарии к рекомендациям особенно важны, поскольку стандарты написания кода и гайдлайны обычно разжигают «холивары», и важным моментом является объяснение рекомендации.

1.2 Важность рекомендаций

Рекомендации разделены по степени важности: обязательные, настоятельно рекомендуемые и общие.

2 Общие рекомендации

1. Допускаются любые нарушения рекомендаций, если это улучшает читаемость.

Основная цель рекомендаций — улучшение читаемости и, следовательно, ясности и лёгкости поддержки, а также общего качества кода. Невозможно дать рекомендации на все случаи жизни, поэтому программист должен мыслить гибко.

2. Правила могут быть нарушены, если против них есть персональные возражения.

Это попытка создать набор общих рекомендаций, не навязывая всем единый стиль. Опытные программисты обычно всё равно подгоняют стиль под себя. Подобный список рекомендаций, имеющийся под рукой (или хотя бы требование ознакомиться с ним), обычно заставляет людей задумываться о стиле программирования и оценке их собственных практик в этой области.

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

3 Соглашения об именовании

3.1 Общие соглашения об именовании

3. Имена, представляющие типы, должны быть обязательно написаны в смешанном регистре, начиная с верхнего.

Общая практика в сообществе разработчиков C++.

4. Имена переменных должны быть записаны в смешанном регистре, начиная с нижнего.

Общая практика в сообществе разработчиков C++. Позволяет легко отличать переменные от типов, предотвращает потенциальные коллизии имён, например: Line line;

5. Именованные константы (включая значения перечислений) должны быть записаны в верхнем регистре с нижним подчёркиванием в качестве разделителя.

Общая практика в сообществе разработчиков C++. Использование таких констант должно быть сведено к минимуму. В большинстве случаев реализация значения в виде метода — лучшее решение:

Эта форма более читаемая и гарантирует единый интерфейс к значениям, хранящимся в классе.

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

Совпадает с правилом для переменных, но отличие между ними состоит в их специфических формах.

7. Названия пространств имён следует записывать в нижнем регистре.

Общая практика в сообществе разработчиков C++.

8. Следует называть имена типов в шаблонах одной заглавной буквой.

Общая практика в сообществе разработчиков C++. Позволяет выделить имена шаблонов среди других используемых имён.

9. Аббревиатуры и сокращения в именах должны записываться в нижнем регистре.

Использование верхнего регистра может привести к конфликту имён, описанному выше. Иначе переменные бы имели имена dVD, hTML и т. д., что не является удобочитаемым. Другая проблема уже описана выше; когда имя связано с другим, читаемость снижается; слово, следующее за аббревиатурой, не выделяется так, как следовало бы.

10. Глобальные переменные всегда следует использовать с оператором разрешения области видимости (::).

Следует избегать использования глобальных переменных. Предпочтительнее использование синглтонов.

11. Членам класса с модификатором private следует присваивать суффикс-подчёркивание.

Не считая имени и типа, область видимости — наиболее важное свойство переменной. Явное указание модификатора доступа в виде подчёркивания избавляет от путаницы между членами класса и локальными переменными. Это важно, поскольку переменные класса имеют большее значение, нежели переменные в методах, и к ним следует относиться более осторожно.

Дополнительным эффектом от суффикса-подчёркивания является разрешение проблемы именования в методах, устанавливающих значения, а также в конструкторах:

Проблема заключается в том, что существует два варианта подчёркивания — в виде суффикса и в виде префикса. Оба варианта широко используются, но рекомендуется именно первый вариант, потому что он обеспечивает лучшую читаемость. Следует отметить, что определение модификатора доступа у переменных — иногда спорный вопрос. Хотя кажется, что рекомендуемая практика набирает сторонников и становится всё более распространённой в среде профессионалов.

12. Настраиваемым переменным следует давать то же имя, что и у их типа.

Сокращайте сложность путём уменьшения числа используемых терминов и имён. Также упрощает распознавание типа просто по имени переменной.

Если по какой-то причине эта рекомендация кажется неподходщей, это означает, что имя типа выбрано неверно.

Не являющиеся настраиваемыми переменные могут быть названы по их назначению и типу:

13. Все имена следует записывать по-английски.

Английский наиболее предпочитетелен для интернациональной разработки.

14. Переменные, имеющие большую область видимости, следует называть длинными именами, имеющие небольшую область видимости — короткими.

Имена временных переменных, использующихся для хранения временных значений или индексов, лучше всего делать короткими. Программист, читающий такие переменные, должен иметь возможность предположить, что их значения не используются за пределами нескольких строк кода. Обычно это переменные i, j, k, l, m, n (для целых), а также c и d (для символов).

15. Имена объектов не указываются явно, следует избегать указания названий объектов в именах методов.

Второй вариант смотрится вполне естественно в объявлении класса, но совершенно избыточен при использовании, как это и показано в примере.

(Пункт № 16 отсутствует.— Примечание переводчика.)

3.2 Особые правила именования

17. Слова get/set должны быть использованы везде, где осуществляется прямой доступ к атрибуту.

Общая практика в сообществе разработчиков C++. В Java это соглашение стало более-менее стандартным.

18. Слово compute может быть использовано в методах, вычисляющих что-либо.

Дайте читающему сразу понять, что это времязатратная операция.

19. Слово find может быть использовано в методах, осуществляющих какой-либо поиск.

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

20. Слово initialize может быть использовано там, где объект или сущность инициализируется.

Следует отдавать предпочтение американскому варианту initialize, нежели британскому initialise. Следует избегать сокращения init.

21. Переменным, представляющим GUI, следует давать суффикс, соответствующий имени типа компонента.

Улучшает читаемость, поскольку имя даёт пользователю прямую подсказку о типе переменной и, следовательно, ресурсах объектов.

22. Множественное число следует использовать для представления наборов (коллекций) объектов.

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

23. Префикс n следует использовать для представления числа объектов.

Обозначение взято из математики, где оно является установившимся соглашением для обозначения числа объектов.

24. Суффикс No следует использовать для обозначения номера сущности.

Обозначение взято из математики, где оно является установившимся соглашением для обозначения номера сущности.

Другой неплохой альтернативой является префикс i: iTable, iEmployee. Он ясно даёт понять, что перед нами именованный итератор.

25. Переменным-итераторам следует давать имена i, j, k и т. д.

Обозначение взято из математики, где оно является установившимся соглашением для обозначения итераторов.

Переменные с именами j, k и т. д. рекомендуется использовать только во вложенных циклах.

26. Префикс is следует использовать только для булевых (логических) переменных и методов.

Общая практика в сообществе разработчиков C++, иногда используемая и в Java.

Использование этого префикса избавляет от таких имён, как status или flag. isStatus или isFlag просто не подходят, и программист вынужден выбирать более осмысленные имена.

В некоторых ситуациях префикс is лучше заменить на другой: has, can или should:

27. Симметричные имена должны использоваться для соответствующих операций.

Уменьшайте сложность за счёт симметрии.

28. Следует избегать сокращений в именах.

Рассмотрим два вида слов. Первые — обычные слова, перечисляемые в словарях, которые нельзя сокращать. Никогда не сокращайте:

Второй вид — слова, специфичные для какой-либо области, которые известны по своему сокращению/аббревиатуре. Их следует записывать сокращённо. Никогда не пишите:

29. Следует избегать дополнительного именования указателей.

Множество переменных в C/C++ являются указателями. Только в том случае, когда тип объекта в языке C++ особенно важен, имя должно отражать его.

30. Нельзя давать булевым (логическим) переменным имена, содержащие отрицание.

Проблема возникает, когда такое имя используется в конъюнкции с оператором логического отрицания, что влечёт двойное отрицание. Результат не обязательно будет отрицанием !isNotFound.

31. Константы в перечислениях могут иметь префикс — общее имя типа.

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

Другим подходом является обращение к константам по их общему типу: Color::RED, Airline::AIR_FRANCE и т. д.

Обратите внимание, что имя перечисления обычно записано в единственном числе, например: enum Color <. >. Имя во множественном числе хорошо выглядит при объявлении, но не очень хорошо подходит для практического использования.

32. Классам исключений следует присваивать суффикс Exception.

Классы исключений в действительности не являются частью архитектуры программ, и такое именование отделяет их от других классов.

33. Функциям (методам, возвращающим какие-либо значения) следует давать имена в зависимости от того, что они возвращают, а процедурам — в зависимости от того, что они выполняют (методы void).

Улучшайте читаемость. Такое именование даёт понять, что метод делает, а что нет, а также избавляет код от потенциальных побочных эффектов.

4 Файлы

4.1 Файлы исходных кодов

Это расширения, одобряемые стандартом C++.

35. Класс следует объявлять в заголовочном файле и определять (реализовывать) в файле исходного кода, имена файлов совпадают с именем класса.

Облегчает поиск связанных с классом файлов. Очевидное исключение — шаблонные классы, которые должны быть объявлены и определены в заголовочном файле.

36. Все определения должны находиться в файлах исходного кода.

Заголовочные файлы объявляют интерфейс, файлы исходного кода его реализовывают. Если программисту необходимо найти реализацию, он должен быть уверен, что найдёт её именно в файле исходного кода.

37. Содержимое файлов не должно превышать 80 колонок.

80 колонок — широко распространённое разрешение для редакторов, эмуляторов терминалов, принтеров и отладчиков; файлы передаются между различными людьми, поэтому нужно придерживаться этих ограничений. Уместная разбивка строк улучшает читаемость при совместной работе над исходным кодом.

38. Нельзя использовать специальные символы (например, TAB) и разрывы страниц.

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

39. Незавершённость разбитых строк должна быть очевидна.

Разбивка строк появляется, когда ограничение на 80 колонок, описанное выше, нарушается. Сложно дать жёсткие правила по разбивке, но примеры выше показывают общие принципы.

4.2 Включения файлов

40. Заголовочные файлы должны содержать защиту от вложенного включения.

Конструкция позволяет избегать ошибок компиляции. Это соглашение позволяет увидеть положение файла в структуре проекта и предотвращает конфликты имён.

41. Директивы включения следует сортировать (по месту в иерархии системы, ниже уровень — выше позиция) и группировать. Оставляйте пустую строку между группами.

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

42. Директивы включения должны располагаться только в начале файла.

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

5 Выражения

5.1 Типы

43. Локальные типы, используемые в одном файле, должны быть объявлены только в нём.

Улучшает сокрытие информации.

44. Разделы класса public, protected и private должны быть отсортированы. Все разделы должны быть явно указаны.

Сперва должен идти раздел public, что избавит желающих ознакомиться с классом от чтения разделов protected/private.

45. Приведение типов должно быть явным. Никогда не полагайтесь на неявное приведение типов.

Этим программист показывает, что ему известно о различии типов, что смешение сделано намеренно.

5.2 Переменные

46. Следует инициализировать переменные в месте их объявления.

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

В этих случаях лучше оставить переменные неинициализированными, чем присваивать им какие-либо значения.

47. Переменные никогда не должны иметь двойной смысл.

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

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

Не существует причины использовать глобальные переменные в C++ (на самом деле существует.— Примечание переводчика). То же касается глобальных функций и (статических) переменных, область видимости которых — весь файл.

49. Не следует объявлять переменные класса как public.

Эти переменные нарушают принципы сокрытия информации и инкапсуляции. Вместо этого используйте переменные с модификатором private и соответствующие функции доступа. Исключение — класс без поведения, практически структура данных (эквивалент структур языка C). В этом случае нет смысла скрывать эти переменные.

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

(Пункт № 50 отсутствует.— Примечание переводчика.)

51. Символ указателя или ссылки в языке C++ следует ставить сразу после имени типа, а не с именем переменной.

То, что переменная — указатель или ссылка, относится скорее к её типу, а не к имени. Программисты на C часто используют другой подход, но в C++ лучше придерживаться этой рекомендации.

(Пункт № 52 отсутствует.— Примечание переводчика.)

53. Следует избегать неявного сравнения булевых (логических) переменных и указателей с нулём.

Стандарт C++ не гарантирует, что значения переменных int и float, равные нулю, будут представлены как бинарный 0. Также при явном сравнении видно сравниваемый тип.

Логично было бы предположить, что также и указатели не следует неявно сравнивать с нулём, например, if (line == 0) вместо if (line). Последнее является очень распространённой практикой в C/C++, поэтому также может быть использовано.

54. Переменные следует объявлять в как можно меньшей области видимости.

Это упрощает контроль над действием переменной и сторонними эффектами.

5.3 Циклы

55. Нельзя включать в конструкцию for() выражения, не относящиеся к управлению циклом.

Улучшайте поддержку и читаемость. Строго разделяйте контроль над циклом и то, что в нём содержится.

56. Переменные, относящиеся к циклу, следует инициализировать непосредственно перед ним.

57. Можно избегать циклов do-while.

Такие циклы хуже читаемы, поскольку условие описано после тела. Читающему придётся просмотреть весь цикл, чтобы понять его работу.

Циклы do-while вообще не являются острой необходимостью. Любой такой цикл может быть заменён на цикл while или for.

Меньшее число используемых конструкций улучшает читаемость.

58. Следует избегать использования break и continue в циклах.

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

(Пункт № 59 отсутствует.— Примечание переводчика.)

60. Для бесконечных циклов следует использовать форму while (true) .

Проверка на единицу не является необходимой и бессмысленна. Форма for (;;) не очень читаема; также не является очевидным, что цикл бесконечный.

5.4 Условные выражения

61. Строго избегайте сложных уловных выражений. Вместо этого вводите булевы переменные.

Задание булевых переменных для выражений приведёт к самодокументированию программы. Конструкцию будет легче читать, отлаживать и поддерживать.

62. Ожидаемую часть следует располагать в части if, исключение — в части else.

Это позволяет убедиться, что исключения не вносят неясности в нормальный ход выполнения. Важно для читаемости и производительности.

63. Условие следует размещать в отдельной строке.

Применяется для отладки.

64. Следует строго избегать исполнимых выражений в условиях.

Исполняемые выражения в условиях усложняют читаемость. Особенно это касается новичков в С/С++.

5.5 Разное

65. Следует избегать «магических» чисел в коде. Числа, отличные от 0 или 1, следует объявлять как именованные константы.

Если число само по себе не имеет очевидного значения, читаемость улучшается путём введения именованной константы. Другой подход — создание метода, с помощью которого можно было бы осуществлять доступ к константе.

66. Константы с плавающей точкой следует записывать с десятичной точкой и с указанием по крайней мере одной цифры после запятой.

Это подчёркивает различные подходы при работе с целыми числами и числами с плавающей точкой. С точки зрения математики, эти две модели совершенно различны и не совместимы.

А также (как это показано в последнем примере выше) делается акцент на типе переменной (sum) в том месте, где это не является очевидным.

67. Константы с плавающей точкой следует всегда записывать, по крайней мере, с одной цифрой до десятичной точки.

68. У функций нужно обязательно указывать тип возвращаемого значения.

Если это не указано явно, C++ считает, что возвращаемое значение имеет тип int. Никогда нельзя полагаться на это, поскольку такой способ может смутить программистов, не знакомых с ним.

69. Не следует использовать goto.

Этот оператор нарушает принципы структурного программирования. Следует использовать только в очень редких случаях (например, для выхода из глубоко вложенного цикла), когда иные варианты однозначно ухудшат читаемость.

70. Следует использовать «0» вместо «NULL».

NULL является частью стандартной библиотеки C и устарело в C++.

6 Оформление и комментарии

6.1 Оформление

71. Основной отступ следует делать в два пробела.

Отступ в один пробел достаточно мал, чтобы отражать логическую структуру кода. Отступ более 4 пробелов делает глубоко вложенный код нечитаемым и увеличивает вероятность того, что строки придётся разбивать. Широко распространены варианты в 2, 3 или 4 пробела; причём 2 и 4 — более широко.

72. Блоки кода следует оформлять так, как показано в примере 1 (рекомендуется) или в примере 2, но ни в коем случае не так, как показано в примере 3. Оформление функций и классов должно следовать примеру 2.

Пример 3 использует лишние отступы, что мешает ясному отображению логической структуры кода.

73. Объявления классов следует оформлять следующим образом:

Частное следствие из правила, указанного выше.

74. Определения методов следует оформлять следующим образом:

Следствие из правила, указанного выше.

75. Конструкцию if-else следует оформлять следующим образом:

Следствие из правила, указанного выше. Причём написание else на той же строке, где стоит закрывающая фигурная скобка первого блока, не является запрещённым:

Лучше каждую часть if-else помещать на отдельной строке. Это упрощает действия с кодом, например, перемещение блока else.

76. Цикл for следует оформлять следующим образом:

Следствие из правила, указанного выше.

77. Цикл for с пустым телом следует оформлять следующим образом:

Делает акцент для читающего на том, что тело пусто. Однако циклов, не имеющих тела, следует избегать.

78. Цикл while следует оформлять следующим образом:

Следствие из правила, указанного выше.

79. Цикл do-while следует оформлять следующим образом:

Следствие из правила, указанного выше.

80. Конструкцию switch следует оформлять следующим образом:

Обратите внимание, что каждое слово case имеет отступ относительно всей конструкции, что помогает её выделить. Также обратите внимание на пробелы перед двоеточиями. Если где-то отсутствует ключевое слово break, то предупреждением об этом должен служить комментарий. Программисты часто забывают ставить это слово, поэтому случай нарочного его пропуска должен описываться специально.

81. Конструкцию try-catch следует оформлять следующим образом:

Следствие из правила, указанного выше. Вопросы, касающиеся закрывающих фигурных скобок у конструкции if-else, применимы и здесь.

82. Если конструкция if-else содержит только одно выражение в теле, фигурные скобки можно опускать.

Рекомендуется всё же не опускать фигурные скобки.

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

Так функции выровнены в одну колонку.

6.2 Пробелы

— Операторы следует отбивать пробелами.
— После зарезервированных ключевых слов языка C++ следует ставить пробел.
— После запятых следует ставить пробелы.
— Двоеточия следует отбивать пробелами.
— После точек с запятой в цикле for следует ставить пробелы.

Выделяет отдельные части выражений. Улучшает читаемость. Сложно дать всеобъемлющий набор рекомендаций относительно пробелов в языке C++. Рекомендации выше должны показать общие принципы.

85. После имён методов может идти пробел, если далее следует другое имя.

Выделяет отдельные имена. Улучшает читаемость. Если далее нет никакого имени, пробел можно опускать (doSomething()).

Другим подходом является указание пробела сразу после открывающей скобки. Использующие его также обычно ставят пробел и перед закрывающей скобкой: doSomething( currentFile );. Это позволяет выделять отдельные имена; пробел перед закрывающей скобкой выглядит неестественно, но без него выражение выглядит несимметрично (doSomething( currentFile);).

86. Логические блоки в коде следует отделять пустой строкой.

87. Методы рекомендуется отделять тремя пустыми строками.

Это позволяет лучше их выделять.

88. Переменные в объявлениях можно выравнивать.

Улучшает читаемость. Чётче видны пары тип — переменная.

89. Используйте выравнивание везде, где это улучшает читаемость.

Есть множество случаев, когда код можно дополнительно выравнивать, даже если это нарушает установленные ранее правила.

6.3 Комментарии

90. Сложный код, написанный с использованием хитрых ходов, следует не комментировать, а переписывать!

Следует делать как можно меньше комментариев, делая код самодокументируемым путём выбора правильных имён и создания ясной логической структуры.

91. Все комментарии следует писать на английском.

В интернациональной среде английский — предпочтительный язык.

92. Используйте // для всех комментариев, включая многострочные.

Если следовать этой рекомендации, многострочные комментарии /* */ можно использовать для отладки и иных целей.

После // следует ставить пробел, а сам комментарий следует начинать писать с большой буквы завершать точкой.

93. Комментарии следует располагать так, чтобы они относились к тому, что они описывают.

Это делается с тем, чтобы избежать ситуацию, когда комментарии нарушают логическую структуру программы.

94. Комментарии к классам и заголовкам методов следует делать в соответствии с соглашениями JavaDoc.

Программисты на языке Java используют более развитый подход к документированию благодаря стандартному автоматическому средству Javadoc, которое является частью пакета разработки и позволяет автоматически создавать документацию в формате HTML из комментариев в коде.

Подобные средства есть и в C++. Они следуют тем же соглашениям о синтаксисе тегов, что и JavaDoc (см., например, Doc++ или Doxygen).

Источник

Google C++ Style Guide

Background

C++ is one of the main development languages used by many of Google’s open-source projects. As every C++ programmer knows, the language has many powerful features, but this power brings with it complexity, which in turn can make code more bug-prone and harder to read and maintain.

Style, also known as readability, is what we call the conventions that govern our C++ code. The term Style is a bit of a misnomer, since these conventions cover far more than just source file formatting.

Most open-source projects developed by Google conform to the requirements in this guide.

Note that this guide is not a C++ tutorial: we assume that the reader is familiar with the language.

Goals of the Style Guide

Why do we have this document?

There are a few core goals that we believe this guide should serve. These are the fundamental whys that underlie all of the individual rules. By bringing these ideas to the fore, we hope to ground discussions and make it clearer to our broader community why the rules are in place and why particular decisions have been made. If you understand what goals each rule is serving, it should be clearer to everyone when a rule may be waived (some can be), and what sort of argument or alternative would be necessary to change a rule in the guide.

The goals of the style guide as we currently see them are as follows:

Style rules should pull their weight The benefit of a style rule must be large enough to justify asking all of our engineers to remember it. The benefit is measured relative to the codebase we would get without the rule, so a rule against a very harmful practice may still have a small benefit if people are unlikely to do it anyway. This principle mostly explains the rules we don’t have, rather than the rules we do: for example, goto contravenes many of the following principles, but is already vanishingly rare, so the Style Guide doesn’t discuss it. Optimize for the reader, not the writer Our codebase (and most individual components submitted to it) is expected to continue for quite some time. As a result, more time will be spent reading most of our code than writing it. We explicitly choose to optimize for the experience of our average software engineer reading, maintaining, and debugging code in our codebase rather than ease when writing said code. «Leave a trace for the reader» is a particularly common sub-point of this principle: When something surprising or unusual is happening in a snippet of code (for example, transfer of pointer ownership), leaving textual hints for the reader at the point of use is valuable ( std::unique_ptr demonstrates the ownership transfer unambiguously at the call site). Be consistent with existing code Using one style consistently through our codebase lets us focus on other (more important) issues. Consistency also allows for automation: tools that format your code or adjust your #include s only work properly when your code is consistent with the expectations of the tooling. In many cases, rules that are attributed to «Be Consistent» boil down to «Just pick one and stop worrying about it»; the potential value of allowing flexibility on these points is outweighed by the cost of having people argue over them. However, there are limits to consistency; it is a good tie breaker when there is no clear technical argument, nor a long-term direction. It applies more heavily locally (per file, or for a tightly-related set of interfaces). Consistency should not generally be used as a justification to do things in an old style without considering the benefits of the new style, or the tendency of the codebase to converge on newer styles over time. Be consistent with the broader C++ community when appropriate Consistency with the way other organizations use C++ has value for the same reasons as consistency within our code base. If a feature in the C++ standard solves a problem, or if some idiom is widely known and accepted, that’s an argument for using it. However, sometimes standard features and idioms are flawed, or were just designed without our codebase’s needs in mind. In those cases (as described below) it’s appropriate to constrain or ban standard features. In some cases we prefer a homegrown or third-party library over a library defined in the C++ Standard, either out of perceived superiority or insufficient value to transition the codebase to the standard interface. Avoid surprising or dangerous constructs C++ has features that are more surprising or dangerous than one might think at a glance. Some style guide restrictions are in place to prevent falling into these pitfalls. There is a high bar for style guide waivers on such restrictions, because waiving such rules often directly risks compromising program correctness. Avoid constructs that our average C++ programmer would find tricky or hard to maintain C++ has features that may not be generally appropriate because of the complexity they introduce to the code. In widely used code, it may be more acceptable to use trickier language constructs, because any benefits of more complex implementation are multiplied widely by usage, and the cost in understanding the complexity does not need to be paid again when working with new portions of the codebase. When in doubt, waivers to rules of this type can be sought by asking your project leads. This is specifically important for our codebase because code ownership and team membership changes over time: even if everyone that works with some piece of code currently understands it, such understanding is not guaranteed to hold a few years from now. Be mindful of our scale With a codebase of 100+ million lines and thousands of engineers, some mistakes and simplifications for one engineer can become costly for many. For instance it’s particularly important to avoid polluting the global namespace: name collisions across a codebase of hundreds of millions of lines are difficult to work with and hard to avoid if everyone puts things into the global namespace. Concede to optimization when necessary Performance optimizations can sometimes be necessary and appropriate, even when they conflict with the other principles of this document.

The intent of this document is to provide maximal guidance with reasonable restriction. As always, common sense and good taste should prevail. By this we specifically refer to the established conventions of the entire Google C++ community, not just your personal preferences or those of your team. Be skeptical about and reluctant to use clever or unusual constructs: the absence of a prohibition is not the same as a license to proceed. Use your judgment, and if you are unsure, please don’t hesitate to ask your project leads to get additional input.

C++ Version

Currently, code should target C++17, i.e., should not use C++2x features, with the exception of designated initializers. The C++ version targeted by this guide will advance (aggressively) over time.

Header Files

Correct use of header files can make a huge difference to the readability, size and performance of your code.

The following rules will guide you through the various pitfalls of using header files.

Self-contained Headers

All header files should be self-contained. Users and refactoring tools should not have to adhere to special conditions to include the header. Specifically, a header should have header guards and include all other headers it needs.

The #define Guard

All header files should have #define guards to prevent multiple inclusion. The format of the symbol name should be

To guarantee uniqueness, they should be based on the full path in a project’s source tree. For example, the file foo/src/bar/baz.h in project foo should have the following guard:

Include What You Use

If a source or header file refers to a symbol defined elsewhere, the file should directly include a header file which properly intends to provide a declaration or definition of that symbol. It should not include header files for any other reason.

Forward Declarations

Avoid using forward declarations where possible. Instead, include the headers you need.

A «forward declaration» is a declaration of an entity without an associated definition.

Try to avoid forward declarations of entities defined in another project.

Inline Functions

Define functions inline only when they are small, say, 10 lines or fewer.

You can declare functions in a way that allows the compiler to expand them inline rather than calling them through the usual function call mechanism.

Inlining a function can generate more efficient object code, as long as the inlined function is small. Feel free to inline accessors and mutators, and other short, performance-critical functions.

Overuse of inlining can actually make programs slower. Depending on a function’s size, inlining it can cause the code size to increase or decrease. Inlining a very small accessor function will usually decrease code size while inlining a very large function can dramatically increase code size. On modern processors smaller code usually runs faster due to better use of the instruction cache.

A decent rule of thumb is to not inline a function if it is more than 10 lines long. Beware of destructors, which are often longer than they appear because of implicit member- and base-destructor calls!

Another useful rule of thumb: it’s typically not cost effective to inline functions with loops or switch statements (unless, in the common case, the loop or switch statement is never executed).

It is important to know that functions are not always inlined even if they are declared as such; for example, virtual and recursive functions are not normally inlined. Usually recursive functions should not be inline. The main reason for making a virtual function inline is to place its definition in the class, either for convenience or to document its behavior, e.g., for accessors and mutators.

Names and Order of Includes

Include headers in the following order: Related header, C system headers, C++ standard library headers, other libraries’ headers, your project’s headers.

Separate each non-empty group with one blank line.

Note that the C headers such as stddef.h are essentially interchangeable with their C++ counterparts ( cstddef ). Either style is acceptable, but prefer consistency with existing code.

Within each section the includes should be ordered alphabetically. Note that older code might not conform to this rule and should be fixed when convenient.

For example, the includes in google-awesome-project/src/foo/internal/fooserver.cc might look like this:

Sometimes, system-specific code needs conditional includes. Such code can put conditional includes after other includes. Of course, keep your system-specific code small and localized. Example:

Scoping

Namespaces

With few exceptions, place code in a namespace. Namespaces should have unique names based on the project name, and possibly its path. Do not use using-directives (e.g., using namespace foo ). Do not use inline namespaces. For unnamed namespaces, see Internal Linkage.

Namespaces subdivide the global scope into distinct, named scopes, and so are useful for preventing name collisions in the global scope.

Namespaces provide a method for preventing name conflicts in large programs while allowing most code to use reasonably short names.

For example, if two different projects have a class Foo in the global scope, these symbols may collide at compile time or at runtime. If each project places their code in a namespace, project1::Foo and project2::Foo are now distinct symbols that do not collide, and code within each project’s namespace can continue to refer to Foo without the prefix.

Inline namespaces automatically place their names in the enclosing scope. Consider the following snippet, for example:

The expressions outer::inner::foo() and outer::foo() are interchangeable. Inline namespaces are primarily intended for ABI compatibility across versions.

Namespaces can be confusing, because they complicate the mechanics of figuring out what definition a name refers to.

Inline namespaces, in particular, can be confusing because names aren’t actually restricted to the namespace where they are declared. They are only useful as part of some larger versioning policy.

In some contexts, it’s necessary to repeatedly refer to symbols by their fully-qualified names. For deeply-nested namespaces, this can add a lot of clutter.

Namespaces should be used as follows:

Namespaces wrap the entire source file after includes, gflags definitions/declarations and forward declarations of classes from other namespaces.

You may not use a using-directive to make all names from a namespace available.

Do not use Namespace aliases at namespace scope in header files except in explicitly marked internal-only namespaces, because anything imported into a namespace in a header file becomes part of the public API exported by that file.

Internal Linkage

Format unnamed namespaces like named namespaces. In the terminating comment, leave the namespace name empty:

Nonmember, Static Member, and Global Functions

Prefer placing nonmember functions in a namespace; use completely global functions rarely. Do not use a class simply to group static members. Static methods of a class should generally be closely related to instances of the class or the class’s static data.

Nonmember and static member functions can be useful in some situations. Putting nonmember functions in a namespace avoids polluting the global namespace.

Nonmember and static member functions may make more sense as members of a new class, especially if they access external resources or have significant dependencies.

Sometimes it is useful to define a function not bound to a class instance. Such a function can be either a static member or a nonmember function. Nonmember functions should not depend on external variables, and should nearly always exist in a namespace. Do not create classes only to group static members; this is no different than just giving the names a common prefix, and such grouping is usually unnecessary anyway.

Local Variables

Place a function’s variables in the narrowest scope possible, and initialize variables in the declaration.

C++ allows you to declare variables anywhere in a function. We encourage you to declare them in as local a scope as possible, and as close to the first use as possible. This makes it easier for the reader to find the declaration and see what type the variable is and what it was initialized to. In particular, initialization should be used instead of declaration and assignment, e.g.

There is one caveat: if the variable is an object, its constructor is invoked every time it enters scope and is created, and its destructor is invoked every time it goes out of scope.

It may be more efficient to declare such a variable used in a loop outside that loop:

Static and Global Variables

Objects with static storage duration are forbidden unless they are trivially destructible. Informally this means that the destructor does not do anything, even taking member and base destructors into account. More formally it means that the type has no user-defined or virtual destructor and that all bases and non-static members are trivially destructible. Static function-local variables may use dynamic initialization. Use of dynamic initialization for static class member variables or variables at namespace scope is discouraged, but allowed in limited circumstances; see below for details.

Global and static variables are very useful for a large number of applications: named constants, auxiliary data structures internal to some translation unit, command-line flags, logging, registration mechanisms, background infrastructure, etc.

Global and static variables that use dynamic initialization or have non-trivial destructors create complexity that can easily lead to hard-to-find bugs. Dynamic initialization is not ordered across translation units, and neither is destruction (except that destruction happens in reverse order of initialization). When one initialization refers to another variable with static storage duration, it is possible that this causes an object to be accessed before its lifetime has begun (or after its lifetime has ended). Moreover, when a program starts threads that are not joined at exit, those threads may attempt to access objects after their lifetime has ended if their destructor has already run.

Decision on destruction

When destructors are trivial, their execution is not subject to ordering at all (they are effectively not «run»); otherwise we are exposed to the risk of accessing objects after the end of their lifetime. Therefore, we only allow objects with static storage duration if they are trivially destructible. Fundamental types (like pointers and int ) are trivially destructible, as are arrays of trivially destructible types. Note that variables marked with constexpr are trivially destructible.

Note that references are not objects, and thus they are not subject to the constraints on destructibility. The constraint on dynamic initialization still applies, though. In particular, a function-local static reference of the form static T& t = *new T; is allowed.

Decision on initialization

Initialization is a more complex topic. This is because we must not only consider whether class constructors execute, but we must also consider the evaluation of the initializer:

All but the first statement expose us to indeterminate initialization ordering.

Constant initialization is always allowed. Constant initialization of static storage duration variables should be marked with constexpr or where possible the ABSL_CONST_INIT attribute. Any non-local static storage duration variable that is not so marked should be presumed to have dynamic initialization, and reviewed very carefully.

By contrast, the following initializations are problematic:

Dynamic initialization of nonlocal variables is discouraged, and in general it is forbidden. However, we do permit it if no aspect of the program depends on the sequencing of this initialization with respect to all other initializations. Under those restrictions, the ordering of the initialization does not make an observable difference. For example:

Dynamic initialization of static local variables is allowed (and common).

Common patterns

thread_local Variables

thread_local variables that aren’t declared inside a function must be initialized with a true compile-time constant, and this must be enforced by using the ABSL_CONST_INIT attribute. Prefer thread_local over other ways of defining thread-local data.

Variables can be declared with the thread_local specifier:

Such a variable is actually a collection of objects, so that when different threads access it, they are actually accessing different objects. thread_local variables are much like static storage duration variables in many respects. For instance, they can be declared at namespace scope, inside functions, or as static class members, but not as ordinary class members.

thread_local variable instances are initialized much like static variables, except that they must be initialized separately for each thread, rather than once at program startup. This means that thread_local variables declared within a function are safe, but other thread_local variables are subject to the same initialization-order issues as static variables (and more besides).

thread_local variable instances are not destroyed before their thread terminates, so they do not have the destruction-order issues of static variables.

thread_local variables inside a function have no safety concerns, so they can be used without restriction. Note that you can use a function-scope thread_local to simulate a class- or namespace-scope thread_local by defining a function or static method that exposes it:

thread_local should be preferred over other mechanisms for defining thread-local data.

Classes

Classes are the fundamental unit of code in C++. Naturally, we use them extensively. This section lists the main dos and don’ts you should follow when writing a class.

Doing Work in Constructors

Avoid virtual method calls in constructors, and avoid initialization that can fail if you can’t signal an error.

It is possible to perform arbitrary initialization in the body of the constructor.

Implicit Conversions

Do not define implicit conversions. Use the explicit keyword for conversion operators and single-argument constructors.

Implicit conversions allow an object of one type (called the ) to be used where a different type (called the ) is expected, such as when passing an int argument to a function that takes a double parameter.

In addition to the implicit conversions defined by the language, users can define their own, by adding appropriate members to the class definition of the source or destination type. An implicit conversion in the source type is defined by a type conversion operator named after the destination type (e.g., operator bool() ). An implicit conversion in the destination type is defined by a constructor that can take the source type as its only argument (or only argument with no default value).

The explicit keyword can be applied to a constructor or a conversion operator, to ensure that it can only be used when the destination type is explicit at the point of use, e.g., with a cast. This applies not only to implicit conversions, but to list initialization syntax:

This kind of code isn’t technically an implicit conversion, but the language treats it as one as far as explicit is concerned.

Implicit conversions can sometimes be necessary and appropriate for types that are designed to be interchangeable, for example when objects of two types are just different representations of the same underlying value. In that case, contact your project leads to request a waiver of this rule.

Copyable and Movable Types

A class’s public API must make clear whether the class is copyable, move-only, or neither copyable nor movable. Support copying and/or moving if these operations are clear and meaningful for your type.

A movable type is one that can be initialized and assigned from temporaries.

For user-defined types, the copy behavior is defined by the copy constructor and the copy-assignment operator. Move behavior is defined by the move constructor and the move-assignment operator, if they exist, or by the copy constructor and the copy-assignment operator otherwise.

The copy/move constructors can be implicitly invoked by the compiler in some situations, e.g., when passing objects by value.

Objects of copyable and movable types can be passed and returned by value, which makes APIs simpler, safer, and more general. Unlike when passing objects by pointer or reference, there’s no risk of confusion over ownership, lifetime, mutability, and similar issues, and no need to specify them in the contract. It also prevents non-local interactions between the client and the implementation, which makes them easier to understand, maintain, and optimize by the compiler. Further, such objects can be used with generic APIs that require pass-by-value, such as most containers, and they allow for additional flexibility in e.g., type composition.

Move operations allow the implicit and efficient transfer of resources out of rvalue objects. This allows a plainer coding style in some cases.

Some types do not need to be copyable, and providing copy operations for such types can be confusing, nonsensical, or outright incorrect. Types representing singleton objects ( Registerer ), objects tied to a specific scope ( Cleanup ), or closely coupled to object identity ( Mutex ) cannot be copied meaningfully. Copy operations for base class types that are to be used polymorphically are hazardous, because use of them can lead to object slicing. Defaulted or carelessly-implemented copy operations can be incorrect, and the resulting bugs can be confusing and difficult to diagnose.

Copy constructors are invoked implicitly, which makes the invocation easy to miss. This may cause confusion for programmers used to languages where pass-by-reference is conventional or mandatory. It may also encourage excessive copying, which can cause performance problems.

Every class’s public interface must make clear which copy and move operations the class supports. This should usually take the form of explicitly declaring and/or deleting the appropriate operations in the public section of the declaration.

Specifically, a copyable class should explicitly declare the copy operations, a move-only class should explicitly declare the move operations, and a non-copyable/movable class should explicitly delete the copy operations. A copyable class may also declare move operations in order to support efficient moves. Explicitly declaring or deleting all four copy/move operations is permitted, but not required. If you provide a copy or move assignment operator, you must also provide the corresponding constructor.

These declarations/deletions can be omitted only if they are obvious:

A type should not be copyable/movable if the meaning of copying/moving is unclear to a casual user, or if it incurs unexpected costs. Move operations for copyable types are strictly a performance optimization and are a potential source of bugs and complexity, so avoid defining them unless they are significantly more efficient than the corresponding copy operations. If your type provides copy operations, it is recommended that you design your class so that the default implementation of those operations is correct. Remember to review the correctness of any defaulted operations as you would any other code.

To eliminate the risk of slicing, prefer to make base classes abstract, by making their constructors protected, by declaring their destructors protected, or by giving them one or more pure virtual member functions. Prefer to avoid deriving from concrete classes.

Structs vs. Classes

The struct and class keywords behave almost identically in C++. We add our own semantic meanings to each keyword, so you should use the appropriate keyword for the data-type you’re defining.

structs should be used for passive objects that carry data, and may have associated constants. All fields must be public. The struct must not have invariants that imply relationships between different fields, since direct user access to those fields may break those invariants. Constructors, destructors, and helper methods may be present; however, these methods must not require or enforce any invariants.

For consistency with STL, you can use struct instead of class for stateless types, such as traits, template metafunctions, and some functors.

Note that member variables in structs and classes have different naming rules.

Structs vs. Pairs and Tuples

Prefer to use a struct instead of a pair or a tuple whenever the elements can have meaningful names.

Pairs and tuples may be appropriate in generic code where there are not specific meanings for the elements of the pair or tuple. Their use may also be required in order to interoperate with existing code or APIs.

Inheritance

When a sub-class inherits from a base class, it includes the definitions of all the data and operations that the base class defines. «Interface inheritance» is inheritance from a pure abstract base class (one with no state or defined methods); all other inheritance is «implementation inheritance».

Implementation inheritance reduces code size by re-using the base class code as it specializes an existing type. Because inheritance is a compile-time declaration, you and the compiler can understand the operation and detect errors. Interface inheritance can be used to programmatically enforce that a class expose a particular API. Again, the compiler can detect errors, in this case, when a class does not define a necessary method of the API.

For implementation inheritance, because the code implementing a sub-class is spread between the base and the sub-class, it can be more difficult to understand an implementation. The sub-class cannot override functions that are not virtual, so the sub-class cannot change implementation.

Multiple inheritance is especially problematic, because it often imposes a higher performance overhead (in fact, the performance drop from single inheritance to multiple inheritance can often be greater than the performance drop from ordinary to virtual dispatch), and because it risks leading to «diamond» inheritance patterns, which are prone to ambiguity, confusion, and outright bugs.

Limit the use of protected to those member functions that might need to be accessed from subclasses. Note that data members should be private.

Explicitly annotate overrides of virtual functions or virtual destructors with exactly one of an override or (less frequently) final specifier. Do not use virtual when declaring an override. Rationale: A function or destructor marked override or final that is not an override of a base class virtual function will not compile, and this helps catch common errors. The specifiers serve as documentation; if no specifier is present, the reader has to check all ancestors of the class in question to determine if the function or destructor is virtual or not.

Multiple inheritance is permitted, but multiple implementation inheritance is strongly discouraged.

Operator Overloading

Overload operators judiciously. Do not use user-defined literals.

User-defined literals are a very concise notation for creating objects of user-defined types.

Define overloaded operators only if their meaning is obvious, unsurprising, and consistent with the corresponding built-in operators. For example, use | as a bitwise- or logical-or, not as a shell-style pipe.

Prefer to define non-modifying binary operators as non-member functions. If a binary operator is defined as a class member, implicit conversions will apply to the right-hand argument, but not the left-hand one. It will confuse your users if a compiles but b doesn’t.

Type conversion operators are covered in the section on implicit conversions. The = operator is covered in the section on copy constructors. Overloading for use with streams is covered in the section on streams. See also the rules on function overloading, which apply to operator overloading as well.

Access Control

Declaration Order

Group similar declarations together, placing public parts earlier.

Within each section, prefer grouping similar kinds of declarations together, and prefer the following order:

Do not put large method definitions inline in the class definition. Usually, only trivial or performance-critical, and very short, methods may be defined inline. See Inline Functions for more details.

Functions

Inputs and Outputs

The output of a C++ function is naturally provided via a return value and sometimes via output parameters (or in/out parameters).

Prefer using return values over output parameters: they improve readability, and often provide the same or better performance.

Prefer to return by value or, failing that, return by reference. Avoid returning a pointer unless it can be null.

Parameters are either inputs to the function, outputs from the function, or both. Non-optional input parameters should usually be values or const references, while non-optional output and input/output parameters should usually be references (which cannot be null). Generally, use std::optional to represent optional by-value inputs, and use a const pointer when the non-optional form would have used a reference. Use non- const pointers to represent optional outputs and optional input/output parameters.

Avoid defining functions that require a const reference parameter to outlive the call, because const reference parameters bind to temporaries. Instead, find a way to eliminate the lifetime requirement (for example, by copying the parameter), or pass it by const pointer and document the lifetime and non-null requirements.

When ordering function parameters, put all input-only parameters before any output parameters. In particular, do not add new parameters to the end of the function just because they are new; place new input-only parameters before the output parameters. This is not a hard-and-fast rule. Parameters that are both input and output muddy the waters, and, as always, consistency with related functions may require you to bend the rule. Variadic functions may also require unusual parameter ordering.

Write Short Functions

Prefer small and focused functions.

We recognize that long functions are sometimes appropriate, so no hard limit is placed on functions length. If a function exceeds about 40 lines, think about whether it can be broken up without harming the structure of the program.

Even if your long function works perfectly now, someone modifying it in a few months may add new behavior. This could result in bugs that are hard to find. Keeping your functions short and simple makes it easier for other people to read and modify your code. Small functions are also easier to test.

You could find long and complicated functions when working with some code. Do not be intimidated by modifying existing code: if working with such a function proves to be difficult, you find that errors are hard to debug, or you want to use a piece of it in several different contexts, consider breaking up the function into smaller and more manageable pieces.

Function Overloading

Use overloaded functions (including constructors) only if a reader looking at a call site can get a good idea of what is happening without having to first figure out exactly which overload is being called.

Overloading can make code more intuitive by allowing an identically-named function to take different arguments. It may be necessary for templatized code, and it can be convenient for Visitors.

Overloading based on const or ref qualification may make utility code more usable, more efficient, or both. (See TotW 148 for more.)

If a function is overloaded by the argument types alone, a reader may have to understand C++’s complex matching rules in order to tell what’s going on. Also many people are confused by the semantics of inheritance if a derived class overrides only some of the variants of a function.

You may overload a function when there are no semantic differences between variants. These overloads may vary in types, qualifiers, or argument count. However, a reader of such a call must not need to know which member of the overload set is chosen, only that something from the set is being called. If you can document all entries in the overload set with a single comment in the header, that is a good sign that it is a well-designed overload set.

Default Arguments

Default arguments are allowed on non-virtual functions when the default is guaranteed to always have the same value. Follow the same restrictions as for function overloading, and prefer overloaded functions if the readability gained with default arguments doesn’t outweigh the downsides below.

Often you have a function that uses default values, but occasionally you want to override the defaults. Default parameters allow an easy way to do this without having to define many functions for the rare exceptions. Compared to overloading the function, default arguments have a cleaner syntax, with less boilerplate and a clearer distinction between ‘required’ and ‘optional’ arguments.

Defaulted arguments are another way to achieve the semantics of overloaded functions, so all the reasons not to overload functions apply.

The defaults for arguments in a virtual function call are determined by the static type of the target object, and there’s no guarantee that all overrides of a given function declare the same defaults.

Default parameters are re-evaluated at each call site, which can bloat the generated code. Readers may also expect the default’s value to be fixed at the declaration instead of varying at each call.

Function pointers are confusing in the presence of default arguments, since the function signature often doesn’t match the call signature. Adding function overloads avoids these problems.

In some other cases, default arguments can improve the readability of their function declarations enough to overcome the downsides above, so they are allowed. When in doubt, use overloads.

Trailing Return Type Syntax

Use trailing return types only where using the ordinary syntax (leading return types) is impractical or much less readable.

C++ allows two different forms of function declarations. In the older form, the return type appears before the function name. For example:

The newer form uses the auto keyword before the function name and a trailing return type after the argument list. For example, the declaration above could equivalently be written:

The trailing return type is in the function’s scope. This doesn’t make a difference for a simple case like int but it matters for more complicated cases, like types declared in class scope or types written in terms of the function parameters.

Trailing return types are the only way to explicitly specify the return type of a lambda expression. In some cases the compiler is able to deduce a lambda’s return type, but not in all cases. Even when the compiler can deduce it automatically, sometimes specifying it explicitly would be clearer for readers.

Sometimes it’s easier and more readable to specify a return type after the function’s parameter list has already appeared. This is particularly true when the return type depends on template parameters. For example:

Trailing return type syntax is relatively new and it has no analogue in C++-like languages such as C and Java, so some readers may find it unfamiliar.

Existing code bases have an enormous number of function declarations that aren’t going to get changed to use the new syntax, so the realistic choices are using the old syntax only or using a mixture of the two. Using a single version is better for uniformity of style.

In most cases, continue to use the older style of function declaration where the return type goes before the function name. Use the new trailing-return-type form only in cases where it’s required (such as lambdas) or where, by putting the type after the function’s parameter list, it allows you to write the type in a much more readable way. The latter case should be rare; it’s mostly an issue in fairly complicated template code, which is discouraged in most cases.

Google-Specific Magic

There are various tricks and utilities that we use to make C++ code more robust, and various ways we use C++ that may differ from what you see elsewhere.

Ownership and Smart Pointers

Prefer to have single, fixed owners for dynamically allocated objects. Prefer to transfer ownership with smart pointers.

«Ownership» is a bookkeeping technique for managing dynamically allocated memory (and other resources). The owner of a dynamically allocated object is an object or function that is responsible for ensuring that it is deleted when no longer needed. Ownership can sometimes be shared, in which case the last owner is typically responsible for deleting it. Even when ownership is not shared, it can be transferred from one piece of code to another.

If dynamic allocation is necessary, prefer to keep ownership with the code that allocated it. If other code needs access to the object, consider passing it a copy, or passing a pointer or reference without transferring ownership. Prefer to use std::unique_ptr to make ownership transfer explicit. For example:

cpplint

Use cpplint.py to detect style errors.

cpplint.py is a tool that reads a source file and identifies many style errors. It is not perfect, and has both false positives and false negatives, but it is still a valuable tool.

Some projects have instructions on how to run cpplint.py from their project tools. If the project you are contributing to does not, you can download cpplint.py separately.

Other C++ Features

Rvalue References

Use rvalue references only in certain special cases listed below.

Rvalue references are a type of reference that can only bind to temporary objects. The syntax is similar to traditional reference syntax. For example, void f(std::string&& s); declares a function whose argument is an rvalue reference to a std::string.

When the token ‘&&’ is applied to an unqualified template argument in a function parameter, special template argument deduction rules apply. Such a reference is called forwarding reference.

Do not use rvalue references (or apply the && qualifier to methods), except as follows:

Friends

We allow use of friend classes and functions, within reason.

Friends should usually be defined in the same file so that the reader does not have to look in another file to find uses of the private members of a class. A common use of friend is to have a FooBuilder class be a friend of Foo so that it can construct the inner state of Foo correctly, without exposing this state to the world. In some cases it may be useful to make a unittest class a friend of the class it tests.

Friends extend, but do not break, the encapsulation boundary of a class. In some cases this is better than making a member public when you want to give only one other class access to it. However, most classes should interact with other classes solely through their public members.

Exceptions

We do not use C++ exceptions.

On their face, the benefits of using exceptions outweigh the costs, especially in new projects. However, for existing code, the introduction of exceptions has implications on all dependent code. If exceptions can be propagated beyond a new project, it also becomes problematic to integrate the new project into existing exception-free code. Because most existing C++ code at Google is not prepared to deal with exceptions, it is comparatively difficult to adopt new code that generates exceptions.

Given that Google’s existing code is not exception-tolerant, the costs of using exceptions are somewhat greater than the costs in a new project. The conversion process would be slow and error-prone. We don’t believe that the available alternatives to exceptions, such as error codes and assertions, introduce a significant burden.

Our advice against using exceptions is not predicated on philosophical or moral grounds, but practical ones. Because we’d like to use our open-source projects at Google and it’s difficult to do so if those projects use exceptions, we need to advise against exceptions in Google open-source projects as well. Things would probably be different if we had to do it all over again from scratch.

There is an exception to this rule (no pun intended) for Windows code.

noexcept

Specify noexcept when it is useful and correct.

The noexcept operator performs a compile-time check that returns true if an expression is declared to not throw any exceptions.

You may use noexcept when it is useful for performance if it accurately reflects the intended semantics of your function, i.e., that if an exception is somehow thrown from within the function body then it represents a fatal error. You can assume that noexcept on move constructors has a meaningful performance benefit. If you think there is significant performance benefit from specifying noexcept on some other function, please discuss it with your project leads.

Run-Time Type Information (RTTI)

Avoid using run-time type information (RTTI).

The standard alternatives to RTTI (described below) require modification or redesign of the class hierarchy in question. Sometimes such modifications are infeasible or undesirable, particularly in widely-used or mature code.

RTTI can be useful in some unit tests. For example, it is useful in tests of factory classes where the test has to verify that a newly created object has the expected dynamic type. It is also useful in managing the relationship between objects and their mocks.

RTTI is useful when considering multiple abstract objects. Consider

Querying the type of an object at run-time frequently means a design problem. Needing to know the type of an object at runtime is often an indication that the design of your class hierarchy is flawed.

Undisciplined use of RTTI makes code hard to maintain. It can lead to type-based decision trees or switch statements scattered throughout the code, all of which must be examined when making further changes.

RTTI has legitimate uses but is prone to abuse, so you must be careful when using it. You may use it freely in unittests, but avoid it when possible in other code. In particular, think twice before using RTTI in new code. If you find yourself needing to write code that behaves differently based on the class of an object, consider one of the following alternatives to querying the type:

When the logic of a program guarantees that a given instance of a base class is in fact an instance of a particular derived class, then a dynamic_cast may be used freely on the object. Usually one can use a static_cast as an alternative in such situations.

Decision trees based on type are a strong indication that your code is on the wrong track.

Code such as this usually breaks when additional subclasses are added to the class hierarchy. Moreover, when properties of a subclass change, it is difficult to find and modify all the affected code segments.

Do not hand-implement an RTTI-like workaround. The arguments against RTTI apply just as much to workarounds like class hierarchies with type tags. Moreover, workarounds disguise your true intent.

Casting

C++ introduced a different cast system from C that distinguishes the types of cast operations.

The problem with C casts is the ambiguity of the operation; sometimes you are doing a conversion (e.g., (int)3.5 ) and sometimes you are doing a cast (e.g., (int)»hello» ). Brace initialization and C++ casts can often help avoid this ambiguity. Additionally, C++ casts are more visible when searching for them.

The C++-style cast syntax is verbose and cumbersome.

In general, do not use C-style casts. Instead, use these C++-style casts when explicit type conversion is necessary.

Streams

Use streams where appropriate, and stick to «simple» usages. Overload for streaming only for types representing values, and write only the user-visible value, not any implementation details.

Avoid using streams for I/O that faces external users or handles untrusted data. Instead, find and use the appropriate templating libraries to handle issues like internationalization, localization, and security hardening.

Overload as a streaming operator for your type only if your type represents a value, and writes out a human-readable string representation of that value. Avoid exposing implementation details in the output of ; if you need to print object internals for debugging, use named functions instead (a method named DebugString() is the most common convention).

Preincrement and Predecrement

Use the prefix form ( ++i ) of the increment and decrement operators unless you need postfix semantics.

A postfix increment/decrement expression evaluates to the value as it was before it was modified. This can result in code that is more compact but harder to read. The prefix form is generally more readable, is never less efficient, and can be more efficient because it doesn’t need to make a copy of the value as it was before the operation.

The tradition developed, in C, of using post-increment, even when the expression value is not used, especially in for loops.

Use prefix increment/decrement, unless the code explicitly needs the result of the postfix increment/decrement expression.

Use of const

In APIs, use const whenever it makes sense. constexpr is a better choice for some uses of const.

Declared variables and parameters can be preceded by the keyword const to indicate the variables are not changed (e.g., const int foo ). Class functions can have the const qualifier to indicate the function does not change the state of the class member variables (e.g., class Foo < int Bar(char c) const; >; ).

Easier for people to understand how variables are being used. Allows the compiler to do better type checking, and, conceivably, generate better code. Helps people convince themselves of program correctness because they know the functions they call are limited in how they can modify your variables. Helps people know what functions are safe to use without locks in multi-threaded programs.

const is viral: if you pass a const variable to a function, that function must have const in its prototype (or the variable will need a const_cast ). This can be a particular problem when calling library functions.

We strongly recommend using const in APIs (i.e., on function parameters, methods, and non-local variables) wherever it is meaningful and accurate. This provides consistent, mostly compiler-verified documentation of what objects an operation can mutate. Having a consistent and reliable way to distinguish reads from writes is critical to writing thread-safe code, and is useful in many other contexts as well. In particular:

Using const on local variables is neither encouraged nor discouraged.

All of a class’s const operations should be safe to invoke concurrently with each other. If that’s not feasible, the class must be clearly documented as «thread-unsafe».

Where to put the const

That said, while we encourage putting const first, we do not require it. But be consistent with the code around you!

Use of constexpr

Use constexpr to define true constants or to ensure constant initialization.

Some variables can be declared constexpr to indicate the variables are true constants, i.e., fixed at compilation/link time. Some functions and constructors can be declared constexpr which enables them to be used in defining a constexpr variable.

Use of constexpr enables definition of constants with floating-point expressions rather than just literals; definition of constants of user-defined types; and definition of constants with function calls.

Prematurely marking something as constexpr may cause migration problems if later on it has to be downgraded. Current restrictions on what is allowed in constexpr functions and constructors may invite obscure workarounds in these definitions.

Integer Types

Uniformity of declaration.

The sizes of integral types in C++ can vary based on compiler and architecture.

If your code is a container that returns a size, be sure to use a type that will accommodate any possible usage of your container. When in doubt, use a larger type rather than a smaller type.

Use care when converting integer types. Integer conversions and promotions can cause undefined behavior, leading to security bugs and other problems.

On Unsigned Integers

That said, mixing signedness of integer types is responsible for an equally large class of problems. The best advice we can provide: try to use iterators and containers rather than pointers and sizes, try not to mix signedness, and try to avoid unsigned types (except for representing bitfields or modular arithmetic). Do not use an unsigned type merely to assert that a variable is non-negative.

64-bit Portability

Code should be 64-bit and 32-bit friendly. Bear in mind problems of printing, comparisons, and structure alignment.

Use braced-initialization as needed to create 64-bit constants. For example:

Preprocessor Macros

Avoid defining macros, especially in headers; prefer inline functions, enums, and const variables. Name macros with a project-specific prefix. Do not use macros to define pieces of a C++ API.

Macros mean that the code you see is not the same as the code the compiler sees. This can introduce unexpected behavior, especially since macros have global scope.

The problems introduced by macros are especially severe when they are used to define pieces of a C++ API, and still more so for public APIs. Every error message from the compiler when developers incorrectly use that interface now must explain how the macros formed the interface. Refactoring and analysis tools have a dramatically harder time updating the interface. As a consequence, we specifically disallow using macros in this way. For example, avoid patterns like:

Macros can do things these other techniques cannot, and you do see them in the codebase, especially in the lower-level libraries. And some of their special features (like stringifying, concatenation, and so forth) are not available through the language proper. But before using a macro, consider carefully whether there’s a non-macro way to achieve the same result. If you need to use a macro to define an interface, contact your project leads to request a waiver of this rule.

The following usage pattern will avoid many problems with macros; if you use macros, follow it whenever possible:

Exporting macros from headers (i.e., defining them in a header without #undef ing them before the end of the header) is extremely strongly discouraged. If you do export a macro from a header, it must have a globally unique name. To achieve this, it must be named with a prefix consisting of your project’s namespace name (but upper case).

0 and nullptr/NULL

Use nullptr for pointers, and ‘\0’ for chars (and not the 0 literal).

Use ‘\0’ for the null character. Using the correct type makes the code more readable.

sizeof

Use sizeof( varname ) when you take the size of a particular variable. sizeof( varname ) will update appropriately if someone changes the variable type either now or later. You may use sizeof( type ) for code unrelated to any particular variable, such as code that manages an external or internal data format where a variable of an appropriate C++ type is not convenient.

Type Deduction (including auto)

Use type deduction only if it makes the code clearer to readers who aren’t familiar with the project, or if it makes the code safer. Do not use it merely to avoid the inconvenience of writing an explicit type.

There are several contexts in which C++ allows (or even requires) types to be deduced by the compiler, rather than spelled out explicitly in the code:

(These summaries omit many details and caveats; see the links for further information.)

C++ code is usually clearer when types are explicit, especially when type deduction would depend on information from distant parts of the code. In expressions like:

it may not be obvious what the resulting types are if the type of y isn’t very well known, or if y was declared many lines earlier.

Programmers have to understand when type deduction will or won’t produce a reference type, or they’ll get copies when they didn’t mean to.

If a deduced type is used as part of an interface, then a programmer might change its type while only intending to change its value, leading to a more radical API change than intended.

The fundamental rule is: use type deduction only to make the code clearer or safer, and do not use it merely to avoid the inconvenience of writing an explicit type. When judging whether the code is clearer, keep in mind that your readers are not necessarily on your team, or familiar with your project, so types that you and your reviewer experience as unnecessary clutter will very often provide useful information to others. For example, you can assume that the return type of make_unique () is obvious, but the return type of MyWidgetFactory() probably isn’t.

These principles apply to all forms of type deduction, but the details vary, as described in the following sections.

Function template argument deduction

Function template argument deduction is almost always OK. Type deduction is the expected default way of interacting with function templates, because it allows function templates to act like infinite sets of ordinary function overloads. Consequently, function templates are almost always designed so that template argument deduction is clear and safe, or doesn’t compile.

Local variable type deduction

For local variables, you can use type deduction to make the code clearer by eliminating type information that is obvious or irrelevant, so that the reader can focus on the meaningful parts of the code:

Types sometimes contain a mixture of useful information and boilerplate, such as it in the example above: it’s obvious that the type is an iterator, and in many contexts the container type and even the key type aren’t relevant, but the type of the values is probably useful. In such situations, it’s often possible to define local variables with explicit types that convey the relevant information:

If the type is a template instance, and the parameters are boilerplate but the template itself is informative, you can use class template argument deduction to suppress the boilerplate. However, cases where this actually provides a meaningful benefit are quite rare. Note that class template argument deduction is also subject to a separate style rule.

Do not use decltype(auto) if a simpler option will work, because it’s a fairly obscure feature, so it has a high cost in code clarity.

Return type deduction

Use return type deduction (for both functions and lambdas) only if the function body has a very small number of return statements, and very little other code, because otherwise the reader may not be able to tell at a glance what the return type is. Furthermore, use it only if the function or lambda has a very narrow scope, because functions with deduced return types don’t define abstraction boundaries: the implementation is the interface. In particular, public functions in header files should almost never have deduced return types.

Parameter type deduction

auto parameter types for lambdas should be used with caution, because the actual type is determined by the code that calls the lambda, rather than by the definition of the lambda. Consequently, an explicit type will almost always be clearer unless the lambda is explicitly called very close to where it’s defined (so that the reader can easily see both), or the lambda is passed to an interface so well-known that it’s obvious what arguments it will eventually be called with (e.g., the std::sort example above).

Lambda init captures

Init captures are covered by a more specific style rule, which largely supersedes the general rules for type deduction.

Structured bindings

Unlike other forms of type deduction, structured bindings can actually give the reader additional information, by giving meaningful names to the elements of a larger object. This means that a structured binding declaration may provide a net readability improvement over an explicit type, even in cases where auto would not. Structured bindings are especially beneficial when the object is a pair or tuple (as in the insert example above), because they don’t have meaningful field names to begin with, but note that you generally shouldn’t use pairs or tuples unless a pre-existing API like insert forces you to.

If the object being bound is a struct, it may sometimes be helpful to provide names that are more specific to your usage, but keep in mind that this may also mean the names are less recognizable to your reader than the field names. We recommend using a comment to indicate the name of the underlying field, if it doesn’t match the name of the binding, using the same syntax as for function parameter comments:

As with function parameter comments, this can enable tools to detect if you get the order of the fields wrong.

Class Template Argument Deduction

Use class template argument deduction only with templates that have explicitly opted into supporting it.

Class template argument deduction (often abbreviated «CTAD») occurs when a variable is declared with a type that names a template, and the template argument list is not provided (not even empty angle brackets):

The compiler deduces the arguments from the initializer using the template’s «deduction guides», which can be explicit or implicit.

Constructors in a primary template (as opposed to a template specialization) also implicitly define deduction guides.

When you declare a variable that relies on CTAD, the compiler selects a deduction guide using the rules of constructor overload resolution, and that guide’s return type becomes the type of the variable.

CTAD can sometimes allow you to omit boilerplate from your code.

The implicit deduction guides that are generated from constructors may have undesirable behavior, or be outright incorrect. This is particularly problematic for constructors written before CTAD was introduced in C++17, because the authors of those constructors had no way of knowing about (much less fixing) any problems that their constructors would cause for CTAD. Furthermore, adding explicit deduction guides to fix those problems might break any existing code that relies on the implicit deduction guides.

Do not use CTAD with a given template unless the template’s maintainers have opted into supporting use of CTAD by providing at least one explicit deduction guide (all templates in the std namespace are also presumed to have opted in). This should be enforced with a compiler warning if available.

Uses of CTAD must also follow the general rules on Type deduction.

Designated Initializers

Use designated initializers only in their C++20-compliant form.

Designated initializers are a syntax that allows for initializing an aggregate («plain old struct») by naming its fields explicitly:

The explicitly listed fields will be initialized as specified, and others will be initialized in the same way they would be in a traditional aggregate initialization expression like Point <1.0, 2.0>.

Designated initializers can make for convenient and highly readable aggregate expressions, especially for structs with less straightforward ordering of fields than the Point example above.

While designated initializers have long been part of the C standard and supported by C++ compilers as an extension, only recently have they made it into the C++ standard, being added as part of C++20.

Use designated initializers only in the form that is compatible with the C++20 standard: with initializers in the same order as the corresponding fields appear in the struct definition.

Lambda Expressions

Use lambda expressions where appropriate. Prefer explicit captures when the lambda will escape the current scope.

Lambda expressions are a concise way of creating anonymous function objects. They’re often useful when passing functions as arguments. For example:

They further allow capturing variables from the enclosing scope either explicitly by name, or implicitly using a default capture. Explicit captures require each variable to be listed, as either a value or reference capture:

Default captures implicitly capture any variable referenced in the lambda body, including this if any members are used:

A variable capture can also have an explicit initializer, which can be used for capturing move-only variables by value, or for other situations not handled by ordinary reference or value captures:

Such captures (often called «init captures» or «generalized lambda captures») need not actually «capture» anything from the enclosing scope, or even have a name from the enclosing scope; this syntax is a fully general way to define members of a lambda object:

Template Metaprogramming

Avoid complicated template programming.

Template metaprogramming refers to a family of techniques that exploit the fact that the C++ template instantiation mechanism is Turing complete and can be used to perform arbitrary compile-time computation in the type domain.

The techniques used in template metaprogramming are often obscure to anyone but language experts. Code that uses templates in complicated ways is often unreadable, and is hard to debug or maintain.

Template metaprogramming often leads to extremely poor compile time error messages: even if an interface is simple, the complicated implementation details become visible when the user does something wrong.

Template metaprogramming interferes with large scale refactoring by making the job of refactoring tools harder. First, the template code is expanded in multiple contexts, and it’s hard to verify that the transformation makes sense in all of them. Second, some refactoring tools work with an AST that only represents the structure of the code after template expansion. It can be difficult to automatically work back to the original source construct that needs to be rewritten.

Template metaprogramming sometimes allows cleaner and easier-to-use interfaces than would be possible without it, but it’s also often a temptation to be overly clever. It’s best used in a small number of low level components where the extra maintenance burden is spread out over a large number of uses.

Think twice before using template metaprogramming or other complicated template techniques; think about whether the average member of your team will be able to understand your code well enough to maintain it after you switch to another project, or whether a non-C++ programmer or someone casually browsing the code base will be able to understand the error messages or trace the flow of a function they want to call. If you’re using recursive template instantiations or type lists or metafunctions or expression templates, or relying on SFINAE or on the sizeof trick for detecting function overload resolution, then there’s a good chance you’ve gone too far.

If you use template metaprogramming, you should expect to put considerable effort into minimizing and isolating the complexity. You should hide metaprogramming as an implementation detail whenever possible, so that user-facing headers are readable, and you should make sure that tricky code is especially well commented. You should carefully document how the code is used, and you should say something about what the «generated» code looks like. Pay extra attention to the error messages that the compiler emits when users make mistakes. The error messages are part of your user interface, and your code should be tweaked as necessary so that the error messages are understandable and actionable from a user point of view.

Boost

Use only approved libraries from the Boost library collection.

The Boost library collection is a popular collection of peer-reviewed, free, open-source C++ libraries.

Boost code is generally very high-quality, is widely portable, and fills many important gaps in the C++ standard library, such as type traits and better binders.

Some Boost libraries encourage coding practices which can hamper readability, such as metaprogramming and other advanced template techniques, and an excessively «functional» style of programming.

In order to maintain a high level of readability for all contributors who might read and maintain code, we only allow an approved subset of Boost features. Currently, the following libraries are permitted:

We are actively considering adding other Boost features to the list, so this list may be expanded in the future.

Other C++ Features

As with Boost, some modern C++ extensions encourage coding practices that hamper readability—for example by removing checked redundancy (such as type names) that may be helpful to readers, or by encouraging template metaprogramming. Other extensions duplicate functionality available through existing mechanisms, which may lead to confusion and conversion costs.

In addition to what’s described in the rest of the style guide, the following C++ features may not be used:

Nonstandard Extensions

Nonstandard extensions to C++ may not be used unless otherwise specified.

Do not use nonstandard extensions. You may use portability wrappers that are implemented using nonstandard extensions, so long as those wrappers are provided by a designated project-wide portability header.

Aliases

Public aliases are for the benefit of an API’s user, and should be clearly documented.

There are several ways to create names that are aliases of other entities:

Don’t put an alias in your public API just to save typing in the implementation; do so only if you intend it to be used by your clients.

When defining a public alias, document the intent of the new name, including whether it is guaranteed to always be the same as the type it’s currently aliased to, or whether a more limited compatibility is intended. This lets the user know whether they can treat the types as substitutable or whether more specific rules must be followed, and can help the implementation retain some degree of freedom to change the alias.

Don’t put namespace aliases in your public API. (See also Namespaces).

For example, these aliases document how they are intended to be used in client code:

These aliases don’t document intended use, and half of them aren’t meant for client use:

Inclusive Language

In all code, including naming and comments, use inclusive language and avoid terms that other programmers might find disrespectful or offensive (such as «master» and «slave», «blacklist» and «whitelist», or «redline»), even if the terms also have an ostensibly neutral meaning. Similarly, use gender-neutral language unless you’re referring to a specific person (and using their pronouns). For example, use «they»/»them»/»their» for people of unspecified gender (even when singular), and «it»/»its» for software, computers, and other things that aren’t people.

Naming

The most important consistency rules are those that govern naming. The style of a name immediately informs us what sort of thing the named entity is: a type, a variable, a function, a constant, a macro, etc., without requiring us to search for the declaration of that entity. The pattern-matching engine in our brains relies a great deal on these naming rules.

Naming rules are pretty arbitrary, but we feel that consistency is more important than individual preferences in this area, so regardless of whether you find them sensible or not, the rules are the rules.

General Naming Rules

Optimize for readability using names that would be clear even to people on a different team.

Use names that describe the purpose or intent of the object. Do not worry about saving horizontal space as it is far more important to make your code immediately understandable by a new reader. Minimize the use of abbreviations that would likely be unknown to someone outside your project (especially acronyms and initialisms). Do not abbreviate by deleting letters within a word. As a rule of thumb, an abbreviation is probably OK if it’s listed in Wikipedia. Generally speaking, descriptiveness should be proportional to the name’s scope of visibility. For example, n may be a fine name within a 5-line function, but within the scope of a class, it’s likely too vague.

Note that certain universally-known abbreviations are OK, such as i for an iteration variable and T for a template parameter.

Template parameters should follow the naming style for their category: type template parameters should follow the rules for type names, and non-type template parameters should follow the rules for variable names.

File Names

Examples of acceptable file names:

Type Names

The names of all types — classes, structs, type aliases, enums, and type template parameters — have the same naming convention. Type names should start with a capital letter and have a capital letter for each new word. No underscores. For example:

Variable Names

Common Variable names

Class Data Members

Data members of classes, both static and non-static, are named like ordinary nonmember variables, but with a trailing underscore.

Struct Data Members

Data members of structs, both static and non-static, are named like ordinary nonmember variables. They do not have the trailing underscores that data members in classes have.

See Structs vs. Classes for a discussion of when to use a struct versus a class.

Constant Names

Variables declared constexpr or const, and whose value is fixed for the duration of the program, are named with a leading «k» followed by mixed case. Underscores can be used as separators in the rare cases where capitalization cannot be used for separation. For example:

All such variables with static storage duration (i.e., statics and globals, see Storage Duration for details) should be named this way. This convention is optional for variables of other storage classes, e.g., automatic variables, otherwise the usual variable naming rules apply.

Function Names

Regular functions have mixed case; accessors and mutators may be named like variables.

Ordinarily, functions should start with a capital letter and have a capital letter for each new word.

(The same naming rule applies to class- and namespace-scope constants that are exposed as part of an API and that are intended to look like functions, because the fact that they’re objects rather than functions is an unimportant implementation detail.)

Namespace Names

The name of a top-level namespace should usually be the name of the project or team whose code is contained in that namespace. The code in that namespace should usually be in a directory whose basename matches the namespace name (or in subdirectories thereof).

Keep in mind that the rule against abbreviated names applies to namespaces just as much as variable names. Code inside the namespace seldom needs to mention the namespace name, so there’s usually no particular need for abbreviation anyway.

For internal namespaces, be wary of other code being added to the same internal namespace causing a collision (internal helpers within a team tend to be related and may lead to collisions). In such a situation, using the filename to make a unique internal name is helpful ( websearch::index::frobber_internal for use in frobber.h ).

Enumerator Names

Until January 2009, the style was to name enum values like macros. This caused problems with name collisions between enum values and macros. Hence, the change to prefer constant-style naming was put in place. New code should use constant-style naming.

Macro Names

Please see the description of macros; in general macros should not be used. However, if they are absolutely needed, then they should be named with all capitals and underscores.

Exceptions to Naming Rules

If you are naming something that is analogous to an existing C or C++ entity then you can follow the existing naming convention scheme.

Comments

Comments are absolutely vital to keeping our code readable. The following rules describe what you should comment and where. But remember: while comments are very important, the best code is self-documenting. Giving sensible names to types and variables is much better than using obscure names that you must then explain through comments.

When writing your comments, write for your audience: the next contributor who will need to understand your code. Be generous — the next one may be you!

Comment Style

Use either the // or /* */ syntax, as long as you are consistent.

You can use either the // or the /* */ syntax; however, // is much more common. Be consistent with how you comment and what style you use where.

File Comments

Start each file with license boilerplate.

File comments describe the contents of a file. If a file declares, implements, or tests exactly one abstraction that is documented by a comment at the point of declaration, file comments are not required. All other files must have file comments.

Legal Notice and Author Line

Every file should contain license boilerplate. Choose the appropriate boilerplate for the license used by the project (for example, Apache 2.0, BSD, LGPL, GPL).

If you make significant changes to a file with an author line, consider deleting the author line. New files should usually not contain copyright notice or author line.

File Contents

Class Comments

Every non-obvious class or struct declaration should have an accompanying comment that describes what it is for and how it should be used.

The class comment should provide the reader with enough information to know how and when to use the class, as well as any additional considerations necessary to correctly use the class. Document the synchronization assumptions the class makes, if any. If an instance of the class can be accessed by multiple threads, take extra care to document the rules and invariants surrounding multithreaded use.

The class comment is often a good place for a small example code snippet demonstrating a simple and focused usage of the class.

Function Comments

Declaration comments describe use of the function (when it is non-obvious); comments at the definition of a function describe operation.

Function Declarations

Almost every function declaration should have comments immediately preceding it that describe what the function does and how to use it. These comments may be omitted only if the function is simple and obvious (e.g., simple accessors for obvious properties of the class). Private methods and functions declared in `.cc` files are not exempt. Function comments should be written with an implied subject of This function and should start with the verb phrase; for example, «Opens the file», rather than «Open the file». In general, these comments do not describe how the function performs its task. Instead, that should be left to comments in the function definition.

Types of things to mention in comments at the function declaration:

Here is an example:

However, do not be unnecessarily verbose or state the completely obvious.

When documenting function overrides, focus on the specifics of the override itself, rather than repeating the comment from the overridden function. In many of these cases, the override needs no additional documentation and thus no comment is required.

When commenting constructors and destructors, remember that the person reading your code knows what constructors and destructors are for, so comments that just say something like «destroys this object» are not useful. Document what constructors do with their arguments (for example, if they take ownership of pointers), and what cleanup the destructor does. If this is trivial, just skip the comment. It is quite common for destructors not to have a header comment.

Function Definitions

If there is anything tricky about how a function does its job, the function definition should have an explanatory comment. For example, in the definition comment you might describe any coding tricks you use, give an overview of the steps you go through, or explain why you chose to implement the function in the way you did rather than using a viable alternative. For instance, you might mention why it must acquire a lock for the first half of the function but why it is not needed for the second half.

Variable Comments

In general the actual name of the variable should be descriptive enough to give a good idea of what the variable is used for. In certain cases, more comments are required.

Class Data Members

The purpose of each class data member (also called an instance variable or member variable) must be clear. If there are any invariants (special values, relationships between members, lifetime requirements) not clearly expressed by the type and name, they must be commented. However, if the type and name suffice ( int num_events_; ), no comment is needed.

Global Variables

All global variables should have a comment describing what they are, what they are used for, and (if unclear) why they need to be global. For example:

Implementation Comments

In your implementation you should have comments in tricky, non-obvious, interesting, or important parts of your code.

Explanatory Comments

Tricky or complicated code blocks should have comments before them.

Function Argument Comments

When the meaning of a function argument is nonobvious, consider one of the following remedies:

Don’ts

Do not state the obvious. In particular, don’t literally describe what code does, unless the behavior is nonobvious to a reader who understands C++ well. Instead, provide higher level comments that describe why the code does what it does, or make the code self describing.

Compare this: To this: Self-describing code doesn’t need a comment. The comment from the example above would be obvious:

Punctuation, Spelling, and Grammar

Pay attention to punctuation, spelling, and grammar; it is easier to read well-written comments than badly written ones.

Comments should be as readable as narrative text, with proper capitalization and punctuation. In many cases, complete sentences are more readable than sentence fragments. Shorter comments, such as comments at the end of a line of code, can sometimes be less formal, but you should be consistent with your style.

Although it can be frustrating to have a code reviewer point out that you are using a comma when you should be using a semicolon, it is very important that source code maintain a high level of clarity and readability. Proper punctuation, spelling, and grammar help with that goal.

TODO Comments

Use TODO comments for code that is temporary, a short-term solution, or good-enough but not perfect.

If your TODO is of the form «At a future date do something» make sure that you either include a very specific date («Fix by November 2005») or a very specific event («Remove this code when all clients can handle XML responses.»).

Formatting

Coding style and formatting are pretty arbitrary, but a project is much easier to follow if everyone uses the same style. Individuals may not agree with every aspect of the formatting rules, and some of the rules may take some getting used to, but it is important that all project contributors follow the style rules so that they can all read and understand everyone’s code easily.

To help you format code correctly, we’ve created a settings file for emacs.

Line Length

Each line of text in your code should be at most 80 characters long.

We recognize that this rule is controversial, but so much existing code already adheres to it, and we feel that consistency is important.

Those who favor this rule argue that it is rude to force them to resize their windows and there is no need for anything longer. Some folks are used to having several code windows side-by-side, and thus don’t have room to widen their windows in any case. People set up their work environment assuming a particular maximum window width, and 80 columns has been the traditional standard. Why change it?

Proponents of change argue that a wider line can make code more readable. The 80-column limit is an hidebound throwback to 1960s mainframes; modern equipment has wide screens that can easily show longer lines.

80 characters is the maximum.

A line may exceed 80 characters if it is

Non-ASCII Characters

Non-ASCII characters should be rare, and must use UTF-8 formatting.

You shouldn’t hard-code user-facing text in source, even English, so use of non-ASCII characters should be rare. However, in certain cases it is appropriate to include such words in your code. For example, if your code parses data files from foreign sources, it may be appropriate to hard-code the non-ASCII string(s) used in those data files as delimiters. More commonly, unittest code (which does not need to be localized) might contain non-ASCII strings. In such cases, you should use UTF-8, since that is an encoding understood by most tools able to handle more than just ASCII.

Use the u8 prefix to guarantee that a string literal containing \uXXXX escape sequences is encoded as UTF-8. Do not use it for strings containing non-ASCII characters encoded as UTF-8, because that will produce incorrect output if the compiler does not interpret the source file as UTF-8.

You shouldn’t use char16_t and char32_t character types, since they’re for non-UTF-8 text. For similar reasons you also shouldn’t use wchar_t (unless you’re writing code that interacts with the Windows API, which uses wchar_t extensively).

Spaces vs. Tabs

Use only spaces, and indent 2 spaces at a time.

We use spaces for indentation. Do not use tabs in your code. You should set your editor to emit spaces when you hit the tab key.

Function Declarations and Definitions

Return type on the same line as function name, parameters on the same line if they fit. Wrap parameter lists which do not fit on a single line as you would wrap arguments in a function call.

Functions look like this:

If you have too much text to fit on one line:

or if you cannot fit even the first parameter:

Some points to note:

Unused parameters that are obvious from context may be omitted:

Unused parameters that might not be obvious should comment out the variable name in the function definition:

Attributes, and macros that expand to attributes, appear at the very beginning of the function declaration or definition, before the return type:

Lambda Expressions

Format parameters and bodies as for any other function, and capture lists like other comma-separated lists.

For by-reference captures, do not leave a space between the ampersand (&) and the variable name.

Short lambdas may be written inline as function arguments.

Floating-point Literals

Floating-point literals should always have a radix point, with digits on both sides, even if they use exponential notation. Readability is improved if all floating-point literals take this familiar form, as this helps ensure that they are not mistaken for integer literals, and that the E / e of the exponential notation is not mistaken for a hexadecimal digit. It is fine to initialize a floating-point variable with an integer literal (assuming the variable type can exactly represent that integer), but note that a number in exponential notation is never an integer literal.

Function Calls

Either write the call all on a single line, wrap the arguments at the parenthesis, or start the arguments on a new line indented by four spaces and continue at that 4 space indent. In the absence of other considerations, use the minimum number of lines, including placing multiple arguments on each line where appropriate.

Function calls have the following format:

If the arguments do not all fit on one line, they should be broken up onto multiple lines, with each subsequent line aligned with the first argument. Do not add spaces after the open paren or before the close paren:

Arguments may optionally all be placed on subsequent lines with a four space indent:

Put multiple arguments on a single line to reduce the number of lines necessary for calling a function unless there is a specific readability problem. Some find that formatting with strictly one argument on each line is more readable and simplifies editing of the arguments. However, we prioritize for the reader over the ease of editing arguments, and most readability problems are better addressed with the following techniques.

If having multiple arguments in a single line decreases readability due to the complexity or confusing nature of the expressions that make up some arguments, try creating variables that capture those arguments in a descriptive name:

Or put the confusing argument on its own line with an explanatory comment:

If there is still a case where one argument is significantly more readable on its own line, then put it on its own line. The decision should be specific to the argument which is made more readable rather than a general policy.

Sometimes arguments form a structure that is important for readability. In those cases, feel free to format the arguments according to that structure:

Braced Initializer List Format

Format a braced initializer list exactly like you would format a function call in its place.

If the braced list follows a name (e.g., a type or variable name), format as if the <> were the parentheses of a function call with that name. If there is no name, assume a zero-length name.

Conditionals

In an if statement, including its optional else if and else clauses, put one space between the if and the opening parenthesis, and between the closing parenthesis and the curly brace (if any), but no spaces between the parentheses and the condition or initializer. If the optional initializer is present, put a space or newline after the semicolon, but not before.

For historical reasons, we allow one exception to the above rules: if an if statement has no else or else if clauses, then the curly braces for the controlled statement or the line breaks inside the curly braces may be omitted if as a result the entire if statement appears on either a single line (in which case there is a space between the closing parenthesis and the controlled statement) or on two lines (in which case there is a line break after the closing parenthesis and there are no braces). For example, the following forms are allowed under this exception.

Use this style only when the statement is brief, and consider that conditional statements with complex conditions or controlled statements may be more readable with curly braces. Some projects require curly braces always.

Finally, these are not permitted:

Loops and Switch Statements

case blocks in switch statements can have curly braces or not, depending on your preference. If you do include curly braces they should be placed as shown below.

If not conditional on an enumerated value, switch statements should always have a default case (in the case of an enumerated value, the compiler will warn you if any values are not handled). If the default case should never execute, treat this as an error. For example:

Fall-through from one case label to another must be annotated using the [[fallthrough]]; attribute. [[fallthrough]]; should be placed at a point of execution where a fall-through to the next case label occurs. A common exception is consecutive case labels without intervening code, in which case no annotation is needed.

Braces are optional for single-statement loops.

Empty loop bodies should use either an empty pair of braces or continue with no braces, rather than a single semicolon.

Pointer and Reference Expressions

No spaces around period or arrow. Pointer operators do not have trailing spaces.

The following are examples of correctly-formatted pointer and reference expressions:

When referring to a pointer or reference (variable declarations or definitions, arguments, return types, template parameters, etc), you may place the space before or after the asterisk/ampersand. In the trailing-space style, the space is elided in some cases (template parameters, etc).

You should do this consistently within a single file. When modifying an existing file, use the style in that file.

It is allowed (if unusual) to declare multiple variables in the same declaration, but it is disallowed if any of those have pointer or reference decorations. Such declarations are easily misread.

Boolean Expressions

When you have a boolean expression that is longer than the standard line length, be consistent in how you break up the lines.

In this example, the logical AND operator is always at the end of the lines:

Note that when the code wraps in this example, both of the && logical AND operators are at the end of the line. This is more common in Google code, though wrapping all operators at the beginning of the line is also allowed. Feel free to insert extra parentheses judiciously because they can be very helpful in increasing readability when used appropriately, but be careful about overuse. Also note that you should always use the punctuation operators, such as && and

Return Values

Do not needlessly surround the return expression with parentheses.

Variable and Array Initialization

Be careful when using a braced initialization list <. >on a type with an std::initializer_list constructor. A nonempty braced-init-list prefers the std::initializer_list constructor whenever possible. Note that empty braces <> are special, and will call a default constructor if available. To force the non- std::initializer_list constructor, use parentheses instead of braces.

Also, the brace form prevents narrowing of integral types. This can prevent some types of programming errors.

Preprocessor Directives

The hash mark that starts a preprocessor directive should always be at the beginning of the line.

Even when preprocessor directives are within the body of indented code, the directives should start at the beginning of the line.

Class Format

The basic format for a class definition (lacking the comments, see Class Comments for a discussion of what comments are needed) is:

Constructor Initializer Lists

Constructor initializer lists can be all on one line or with subsequent lines indented four spaces.

The acceptable formats for initializer lists are:

Namespace Formatting

The contents of namespaces are not indented.

Namespaces do not add an extra level of indentation. For example, use:

Do not indent within a namespace:

Horizontal Whitespace

Use of horizontal whitespace depends on location. Never put trailing whitespace at the end of a line.

General

Adding trailing whitespace can cause extra work for others editing the same file, when they merge, as can removing existing trailing whitespace. So: Don’t introduce trailing whitespace. Remove it if you’re already changing that line, or do it in a separate clean-up operation (preferably when no-one else is working on the file).

Loops and Conditionals

Operators

Templates and Casts

Vertical Whitespace

Minimize use of vertical whitespace.

This is more a principle than a rule: don’t use blank lines when you don’t have to. In particular, don’t put more than one or two blank lines between functions, resist starting functions with a blank line, don’t end functions with a blank line, and be sparing with your use of blank lines. A blank line within a block of code serves like a paragraph break in prose: visually separating two thoughts.

The basic principle is: The more code that fits on one screen, the easier it is to follow and understand the control flow of the program. Use whitespace purposefully to provide separation in that flow.

Some rules of thumb to help when blank lines may be useful:

Exceptions to the Rules

The coding conventions described above are mandatory. However, like all good rules, these sometimes have exceptions, which we discuss here.

Existing Non-conformant Code

You may diverge from the rules when dealing with code that does not conform to this style guide.

If you find yourself modifying code that was written to specifications other than those presented by this guide, you may have to diverge from these rules in order to stay consistent with the local conventions in that code. If you are in doubt about how to do this, ask the original author or the person currently responsible for the code. Remember that consistency includes local consistency, too.

Windows Code

Windows programmers have developed their own set of coding conventions, mainly derived from the conventions in Windows headers and other Microsoft code. We want to make it easy for anyone to understand your code, so we have a single set of guidelines for everyone writing C++ on any platform.

It is worth reiterating a few of the guidelines that you might forget if you are used to the prevalent Windows style:

However, there are just a few rules that we occasionally need to break on Windows:

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *