Skip to content
Nikita Kataev edited this page Feb 10, 2017 · 15 revisions

TSAR Coding Standards

Анализатор разрабатывается на языке программирования С++11, использование более новых стандартов в данный момент не допускается. Все комментарии, размещенные в коде и сопровождающие фиксации в репозитории должны быть написаны на английском языке. Комментарии к фиксациям должны начинаться с маркера, обозначающего принадлежность загружаемых изменений, например, [TSAR] или [TSAR-cmake] для анализатора и [BASE] для изменений связанных с базовыми конструкциями (Base Construction Library (BCL)).

Все конструкции относящиеся к TSAR должны располагаться в пространстве имен tsar, детали реализации можно скрыть в пространстве имен detail. Если создаются конструкции относящиеся к LLVM, например проходы анализа и преобразований, то они должны располагаться в пространстве имен llvm.

При разработке применяется Google C++ Style Guide в комбинации c LLVM Coding Standards и некоторыми модификациями. Основные правила приведены ниже.

Документацию по языку С++ можно найти в следующих источниках:

  • С++ Wiki ,также доступная на русском языке, но чуть менее полная.
  • Стандартные библиотеки и язык C++ - документация в Microsoft MSDN.
  • C++ 11 FAQ от Бьярна Страуструпа - краткое описание (на русском языке) возможностей, добавленных в стандарте С++ 11.
  • Описание стандарта С++ на английском языке.
  • Бьерн Страуструп. Программирование. Принципы и практика с использованием C++ (обновлено для С++11/С++14) // 2-е издание. Изд-во: Вильямс, 2016. 1328 с.
  • Скотт Мейерс. Эффективное использование C++. 55 верных советов улучшить структуру и код ваших программ // 3-е издание. Изд-во: ДМК Пресс, 2014. 300 с.
  • Скотт Мейерс. Эффективный и современный С++. 42 рекомендации по использованию C++11 и C++14 // Изд-во: Вильямс, 2015. 304 с.

Полезная информация по LLVM может быть получена с сайта http://llvm.org:

Стиль написания кода

Допускается длина строки не более 80 символов. Исключение делается только для строковых констант, не умещающихся в данный предел. Использование символа табуляции в коде не допускается, данные символы должны быть заменены на пробелы (автоматическая замена поддерживается в большинстве средств разработки). Стандартный размер отступа - 2 символа, все завершающие пробельные символы должны удаляться.

Документирование кода

Допускается два вида комментариев:

// Обычный комментарий

/// \brief Документирующий комментарий оформляется в формате
/// Doxygen-комментария (http://www.doxygen.org/).
///
/// Документирующие Doxygen-комментарии должны быть написаны для всех открытых
/// методов, членов, типов класса, всех доступных извне элементов (в том числе
/// классов, пространств имен, типов (typedef)).
/// \note Рекомендуется указывать комментарии и для остальных объектов.

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

//===--- tsar_private.h - Private Variable Analyzer -------------*- C++ -*-===//
//
//                       Traits Static Analyzer (SAPFOR)
//
//===----------------------------------------------------------------------===//
//
// This file defines passes to determine locations which can be privatized.
// We use data-flow framework to implement this kind of analysis. This file
// contains elements which is necessary to determine this framework.
// The following articles can be helpful to understand it:
//  * "Automatic Array Privatization" Peng Tu and David Padua
//  * "Array Privatization for Parallel Execution of Loops" Zhiyuan Li.
//
//===----------------------------------------------------------------------===//

Отступы

Перед объектами, расположенными в пространстве имен отступы не ставятся:

namespace tsar {
/// Color of a node which depth-first search (DFS) has visited.
enum DFSColor {
  ...

После заголовка функции, ключевого слова template, специализируемого класса или функции отступы не ставятся.

/// \brief Returns true if the specified directed graph is acyclic.
///
/// \param [in] G Directed graph, it can not be null.
/// \pre The llvm::GraphTraits class should be specialized by GraphType.
/// Note that GraphType is generally a pointer type, for example BasicBlock *.
template<class GraphType> inline bool isDAG(GraphType G) {
  return !findBackEdge<GraphType>(G).first;
}

Отступы ставятся после ключевых слов if, for, while и аналогичных.

Если параметры функции не умещаются в границы 80 символов, то перед ними ставится отступ 4 символа.

  template<class location_iterator, class ResultSet>
  static void difference(
      const location_iterator &LocBegin, const location_iterator &LocEnd,
      const LocationSet &LocSet, ResultSet &Result) {
    if (LocSet.mLocations.empty())
      ...

Открывающая фигурная скобка размещается на той же строке что и предшествующая ей конструкция. Ключевое слово else размещается на той же строке, что и предшествующая закрывающая фигурная скобка. Если в блоке if-then-else хотя бы один раз встречаются фигурные скобки, то фигурные скобки должны стоят во всех ветках. *Строки идут подряд без пропусков за исключением отступов перед введением новых классов, типов, функций, блока конструкций using и #include.

  if (SToNode == Blocks.end()) {
    BBToN.second->addSuccessor(ExitNode);
    ExitNode->addPredecessor(BBToN.second);
  } else if (*SI == LT::getHeader(L)) {
    ...
  } else if (SToNode->second != BBToN.second) {
    BBToN.second->addSuccessor(SToNode->second);
  }

Имена

Имена классов и типов должны начинаться с заглавной буквы. Имена функций начинаются со строчной буквы. Использование нижнего подчеркивания _ и строчных букв в именах классов и типов допускается только для stl-подобных конструкций.

  ...
  /// This type used to iterate over all nodes in the region body.
  typedef std::vector<DFNode *>::const_iterator node_iterator;

  /// This type used to iterate over internal regions.
  typedef std::vector<DFRegion *>::const_iterator region_iterator;
  ...
  /// Returns iterator that points to the beginning of the nodes list.
  node_iterator node_begin() const { return mNodes.begin(); }

  /// Returns iterator that points to the ending of the nodes list.
  node_iterator node_end() const { return mNodes.end(); }
  ...

Имена переменных должны начинаться с заглавной буквы. Для членов класса должен ставиться префикс m, для глобальных переменных (использование которых не рекомедуется) - префикс g.

Имена должны быть понятны и точно описывать назначение именуемой сущности. Допускаются общеупотребительные в рамках проекта сокращения имен (BasicBlock - BB, Function - F, Control Flow Graph - CFG и т.д.), или сокращения имен с небольшой областью видимости для более компактного размещения кода.

  ...
  for (auto I = LT::block_begin(L), E = LT::block_end(L); I != E; ++I) {
    if (Blocks.count(*I))
      continue;
    DFBlock *N = new DFBlock(*I);
    R->addNode(N);
    Blocks.insert(std::make_pair(*I, N));
  }
  ...

Порядок объявления методов методы и члены классов

Закрытые методы и члены классов должны быть размещены в конце описания класса. Объявление класса должно начинаться с секции public, затем следует секция protected и секция private.

  ...
  /// Specifies that there are unknown instructions in the node.
  ///
  /// \return False if it has been already specified.
  bool addUnknownInst(llvm::Instruction *I) {
    assert(I && "Instruction must not be null!");
    return mUnknownInsts.insert(I).second;
  }

private:
  LocationSet mDefs;
  LocationSet mMayDefs;
  LocationSet mUses;
  llvm::AliasSetTracker mExplicitAccesses;
  PointerSet mAddressAccesses;
  InstructionSet mUnknownInsts;
};

Классы не допускающие копирования

Классы не допускающие копирования должны закрыто наследовать класс bcl::Uncopyable определенный в файле idb/trunk/src/base/utility.h

namespace llvm {
/// This pass determines locations which can be privatized.
class PrivateRecognitionPass :
    public FunctionPass, private bcl::Uncopyable {
  ...

Функции

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

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

Перечисления

Перечисления должны начинаться со вспомогательного элемента с префиксом FIRST и заканчиваться тремя вспомогательными элементами с префиксами LAST, INVALID, NUMBER соответственно.

  /// Kind of a node.
  /// If you add a new kind of region it should be in the range between
  /// FIRST_KIND_REGION and LAST_KIND_REGION
  enum Kind {
    FIRST_KIND = 0,
    KIND_BLOCK = FIRST_KIND,
    KIND_ENTRY,
    KIND_EXIT,
    KIND_LATCH,

    FIRST_KIND_REGION,
    KIND_LOOP = FIRST_KIND_REGION,
    KIND_FUNCTION,
    LAST_KIND_REGION = KIND_FUNCTION,

    LAST_KIND = LAST_KIND_REGION,
    INVALID_KIND,
    NUMBER_KIND = INVALID_KIND,
  };

Отладка кода

Для сокращения количества ошибок в коде и времени затраченного на их поиск рекомендуется использовать конструкции assert. Для точного описания проверяемого утверждения следует использовать строковую константу:

  assert(*I == GT::getEntryNode(DFG) &&
    "The first node in the topological order differs from the entry node in the data-flow framework!");

В данном случае аварийный останов программы произойдет, если условие *I == GT::getEntryNode(DFG) окажется ложно. При этом текстовое сообщение, описывающее проверяемое утверждение, не влияет на выполнение проверки и используется в пояснительных целях.

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

Для проверки утверждений на этапе компиляции могут использоваться конструкции static_assert:

  static_assert(!IsCellExist<CellNext, CellKey>::value,
    "Each cell must be presented only once in the map!");
Clone this wiki locally