Добавление настроек к отдельным файлам в CMake

CMake поддерживает следующие команды для добавления настроек к цели:

  • target_compile_definitions — добавляет определения,
  • target_compile_options — добавляет опции компилятора,
  • target_include_directories — добавляет пути поиска заголовочных файлов,
  • target_link_libraries — добавляет линкуемые библиотеки.

Эти команды принимают имя цели в качестве первого аргумента. Цели создаются командами add_executable и add_library. Для add_executable целью является исполняемый файл, а для add_library — библиотека (статическая или динамическая).

CMake поддерживает следующие команды для добавления глобальных настроек:

  • add_compile_definitions — добавляет определения,
  • add_compile_options — добавляет опции компилятора,
  • include_directories — добавляет пути поиска заголовочных файлов.
  • link_libraries — добавляет линкуемые библиотеки.

Эти настройки применяются к целям в текущей папке и всех вложенных папках.

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

Чтобы добавить настройки к исходному файлу, можно воспользоваться командой set_property или set_source_files_properties. Пример использования:

set_property(SOURCE LibPQExt.cpp PROPERTY INCLUDE_DIRECTORIES
    ${CMAKE_SOURCE_DIR}/libs/postgresql-9.6/src/include
)

set_property позволяет добавлять настройки для нескольких исходных файлов одновременно.

Более удобный способ — создать из исходного файла или их набора специальную цель, которая называется объектной библиотекой.

Синтаксис команды для создания объектных библиотек:

add_library(<name> OBJECT <src>...)

К такой цели можно применять команды с префиксом target_. Например, можно добавить определение LOGGER_TYPE=3 только для файла logger.cpp. Делается это следующим образом

add_library(logger OBJECT logger.cpp)
target_compile_definitions(logger PUBLIC LOGGER_TYPE=3)

Объектную библиотеку можно добавить к другим целям как набор исходных файлов. Для этого в команды add_executable или add_library надо добавить выражение $<TARGET_OBJECTS:objlib>, где objlib — имя объектной библиотеки. Например:

add_library(logger OBJECT logger.cpp)
target_compile_definitions(logger PUBLIC LOGGER_TYPE=3)

add_executable(my_application
    main.cpp
    archiver.cpp
    $<TARGET_OBJECTS:logger>
)

Поддержка объектных библиотек была введена в CMake версии 2.8.8 (апрель 2012 года). При использовании объектных библиотек рекомендуется в файле CMakeLists.txt ограничить версию CMake на уровне 2.8.8 или выше:

cmake_minimum_required(VERSION 2.8.8)

Аргументы командной строки в разных языках программирования

C и C++

int  main(int argc, char* argv[])
{
    argc;    // количество аргументов + 1
    argv[0]; // имя программы
    argv[1]; // первый аргумент
    argv[2]; // второй аргумент
    // ...
}

Bash

#!/bin/bash

# $# — количество аргументов
#
# $* — аргументы одной строкой
# $@ — список аргументов

echo $0    # имя скрипта
echo $1    # первый аргумент
echo $2    # второй аргумент
# ...
echo $9    # девятый аргумент
echo $(10) # десятый аргумент
echo $(11) # одиннадцатый аргумент
# ...

Cmd.exe

echo %0 REM имя срипта
echo %1 REM первый аргумент
echo %2 REM первый аргумент
REM ...
echo %9 REM девятый аргумент

Python

import sys

sys.argv      # список аргументов
len(sys.argv) # количество аргументов + 1
sys.argv[0]   # имя скрипта
sys.argv[1]   # первый аргумент
sys.argv[2]   # второй аргумент
# ...

Java

public class CommandLine {
    static public void main(String args[]) {
        args.length; // количество аргументов
        args[0];     // первый аргумент
        args[1];     // второй аргумент
        // ...
    }
}

C Sharp

class MainClass
{
    static int Main(string[] args)
    {
        args.Length; // количество аргументов
        args[0];     // первый аргумент
        args[1];     // второй аргумент
        // ...
    }
}

Обработка ошибок при выделение памяти с помощью new в C++

Есть два способа определить выделена ли память оператором new.

Способ 1. Обработка исключения

Если память не выделена, то бросается исключение std::bad_alloc.

Пример обработки исключения.

// bad_alloc example
#include <iostream>     // std::cout
#include <new>          // std::bad_alloc

int main () {
  try
  {
    int* myarray= new int[10000];
  }
  catch (std::bad_alloc& ba)
  {
    std::cerr << ba.what() << std::endl;
  }
  return 0;
}

Способ 2. Проверка указателя

При вызове new в качестве аргумента можно использовать константу std::nothrow. Тогда, исключение std::bad_alloc не испускается, а вместо него возвращается нулевой указатель.

Пример обработки нулевого указателя.

// nothrow example
#include <iostream>     // std::cout
#include <new>          // std::nothrow

int main () {
  std::cout << "Attempting to allocate 1 MiB... ";
  char* p = new (std::nothrow) char [1048576];

  if (!p) {
    std::cout << "Failed!\n";
  }
  else {
    std::cout << "Succeeded!\n";
    delete[] p;
  }

  return 0;
}

Ссылки

  1. Описание std::bad_alloc.
  2. Описание std::nothrow.

Правила кодирования в Qt

640px-qt_logo_2016-svg

При использовании Qt я предпочитаю использовать стиль кодирования, который используется в библиотеке, чтобы добиться единообразия кода.

Директивы препроцессора

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

Пример из файла Qt5.9.0/5.9/gcc_64/include/QtCore/qobjectdefs.h.

# if defined(QT_NO_KEYWORDS)
#  define QT_NO_EMIT
# else
#   ifndef QT_NO_SIGNALS_SLOTS_KEYWORDS
#     define slots Q_SLOTS
#     define signals Q_SIGNALS
#   endif
# endif

Ссылки и указатели

Операторы & и * в обозначении типа смещены вправо и примыкают к имени.

Пример из файла Qt5.9.0/5.9/gcc_64/include/QtCore/qbytearray.h.

class Q_CORE_EXPORT QByteArray
{
    <...>
    QByteArray &prepend(char c);
    QByteArray &prepend(int count, char c);
    QByteArray &prepend(const char *s);
    QByteArray &prepend(const char *s, int len);
    QByteArray &prepend(const QByteArray &a);
    QByteArray &append(char c);
    QByteArray &append(int count, char c);
    QByteArray &append(const char *s);
    QByteArray &append(const char *s, int len);
    QByteArray &append(const QByteArray &a);
    <...>
}

Приватные поля

В имени приватного поля используется префикс m_.

Пример из файла Qt5.9.0/5.9/gcc_64/include/QtCore/qstring.h.

class QLatin1String
{
public:
    <...>
private:
    int m_size;
    const char *m_data;
};

Ссылки

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

C++. Частичная специализация шаблонного класса

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

Пример.

Имеется следующий шаблонный класс в файле value.h.

template<typename T>
class Value {
public:
    Value(const T &val): m_value(val) {}
    T value() const { return m_value; }
private:
    T m_value;
};

Реализуем метод value для типа int в файле value.cpp.

template<>
int Value<int>::value() const
{
    return m_value + 1;
}

ARM и специфичный для компиляторов код

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

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

Ниже приведен пример кода для некоторых ARM-компиляторов.


#if defined (__CC_ARM)
    /* Код для компилятора Keil */
#elif defined (__ICCARM__)
    /* Код для компилятора IAR */
#elif defined ( __GNUC__ )
    /* Код для компилятора GNU */
#elif defined (__TASKING__)
    /* Код для компилятора TASKING */
#else
    /* Неизвестный компилятор */
    #error Unknown compiler
#endif

Библиотека на языке C для конвертации строк из кодировки UTF-8 в CP1251

Написал легкую библиотеку utf8_to_cp1251 для конвертации из UTF-8 в CP1251 для применения в программах для микроконтроллеров. Охватывает языки от С89 до C++.

Библиотека и ее описание опубликованы на Github.

Статические переменные вместо глобальных на Си/Си++

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

Есть простой способ избавиться от большинства глобальных переменных.

void fun(void) {
    static int A = 10;
    ...
}

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

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

static int A = 10;

void setA(const int value) {
    A = value;
}

int getA(void) {
    return A;
}

Область видимости переменной A — только этот файл. Никаким способом к ней нельзя получить доступ из других файлов. Функции setA и getA позволяют получить и установить значение переменной.

Приоритеты прерываний в Cortex-M и их настройка в STM32Cube

STM32Cube — это набор библиотек фирмы STM для своих микроконтроллеров (STM32). Эти библиотеки пришли на смену библиотекам SPL.

Микроконтроллеры STM32 сделаны на базе ядра ARM Cortex-M. Это ядро служит основой микроконтроллеров и у других производителей.

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

Приоритет прерывания зависит от 3 факторов:

Читать далее Приоритеты прерываний в Cortex-M и их настройка в STM32Cube

Форматная строка для представления даты и времени в разных системах

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

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

2016.06.14 23:10:06

(14 июня 2016 год, 23 часа 10 минут 6 секунд. Год из 4 цифр. Месяц, день месяца, часы, минуты и секунды из двух цифр, дополняются нулями. Часы в 24 формате).

Система шаблонов Форматная строка
date (Linux) %Y.%m.%d %H:%M:%S
C/C++ %Y.%m.%d %H:%M:%S
Python %Y.%m.%d %H:%M:%S
SQLite %Y.%m.%d %H:%M:%S
ISO 8601 YYYY.MM.DD hh:mm:ss
Qt yyyy.MM.dd hh:mm:ss
C# yyyy.MM.dd HH:mm:ss