Правила кодирования в 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;
};

Ссылки

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

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

Использование глобальных переменных считается дурным тоном, потому что создает много проблем. Избавиться от них можно с помощью ключевого слова 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

Такой код по разному воспринимается разными компиляторами:

const int data_size = 7;
int data[data_size] = {1,2,3,4,5,6,7};

Если использовать компилятор из Keil MDK-ARM, то код компилируется и работает нормально. Но компилятор (gcc) из Sourcery Сodebench выдает ошибку:

variable-sized object may not be initialized

При этом, без ошибок компилируется код:

const int data_size = 7;
int data[data_size];

Поддержка ввода двоичных чисел в Си

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

Чтобы иметь возможность ввода двоичных чисел и сохранить переносимость, вам пригодится этот заголовочный файл: bit.h.

Файл содержит код следующего вида:

#ifndef BIT_H
#define BIT_H

#define b0000_0000 0
#define b0000_0001 1
#define b0000_0010 2
// ...
#define b1111_1110 254
#define b1111_1111 255

#define b0000_0000_1 0
// ...
#define b1111_1111_1 65280

#define b0000_0000_2 0
// ...
#define b1111_1111_2 16711680

#define b0000_0000_3 0
// ...
#define b1111_1111_3 4278190080

#endif

Просто подключаем bit.h:

#include "bit.h"

и вводим двоичные числа, используя префикс b и разделяя тетрады подчеркиванием. Например:

#define BIT_MASK b0001_0101 // 21
const int a = b0011_1110;
...

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

  • _1 — для смещения числа на 1 байт влево, то есть задания второго байта,
  • _2 — для смещения числа на 2 байта влево, то есть задания третьего байта,
  • _3 — для смещения числа на 3 байта влево, то есть задания четвертого байта,

при этом между числами требуется производить операцию или (|).

Например:

// 0xFF00FF00
uint32_t mask = b1111_1111_3 | b0000_0000_2 | b1111_1111_1 | b0000_0000;
...

Можно задавать один какой-то байт или несколько байт идущих не по порядку, например:

uint32_t mask = b1111_1111_3 | b1111_1111_1; // 0xFF00FF00
...

Поиск свободного места методом половинного деления

Допустим. имеется какая-то область для хранения данных. Например, в флеш-памяти. Она последовательно заполняется блоками данных и с конца остается свободное место. Метод половинного деления позволяет избежать перебора всех данных. Если область вмещает 1000 позиций данных, тогда метод позволяет найти свободную позицию всего за 9 или 10 выборок данных. Если область вмещает миллион позиций, то выборок понадобится 19 или 20.

Метод реализован на языке Си в виде библиотеки из двух файлов. Файл div2-search.h пользователь должен приинклудить в своем проекте. А также пользователю требуется реализовать функцию bool div2_search_is_data(const unsigned position), которая будет сообщать функции поиска, есть ли данные в позиции position или нет. Функция поиска div2_search_free_position() вызывается пользователем. В нее передается количество позиций. Отработав, возвращает свободную позицию.

Архив с исходниками.

Файл div2-search.h:


//  Дата: 01 сентября 2012
// Автор: Дмитрий Бравиков (bravikov@gmail.com)
//
// Реализация поиска свободного места в областе для хранения данных
// методом половинного деления.
//
// Функция div2_search_is_data() реализуется пользователем.

#ifndef DIV2_SEARCH_H
#define DIV2_SEARCH_H

#include <stdbool.h>

// Количество итераций в последнем поиске
extern unsigned div2_search_iteration_count;

// Возвращает true, если удалось обнаружить данные в позиции position.
bool div2_search_is_data(const unsigned position);

// Возвращает: свободную позицию; -1, если size = 0; size, если нет свободной.
int div2_search_free_position(const unsigned size);

#endif //DIV2_SEARCH_H

Файл div2-search.с:


//     Дата: 01 сентября 2012
//    Автор: Дмитрий Бравиков (bravikov@gmail.com)
// Описание: см. div2-search.h

#include "div2-search.h"

unsigned div2_search_iteration_count = 0;

int div2_search_free_position(const unsigned size)
{
    // Диапазон поиска: [l,r)
    unsigned  l = 0;     // Ограничение слева
    unsigned  r = size;  // Ограничение справа
    unsigned  c = r / 2; // Текущая позиция

    div2_search_iteration_count = 0;

    if (size == 0) return -1;

    while (r != l)
    {
        if ( div2_search_is_data(c) )
        {
            l = c + 1;
        }
        else
        {
            r = c;
        }

        c = l + (r - l) / 2;
        
        div2_search_iteration_count++;
    }

    return c;
}

// Примеры поиска:
//
// ---------------------------------------------------------------------------
// Итерация:       2 4 3 1
// Позиция:  0 1 2 3 4 5 6 7 8 9 10 11
// Данные:   * * * *
//
// Результат: 4
// ---------------------------------------------------------------------------
// Итерация:             1 4 3 2
// Позиция:  0 1 2 3 4 5 6 7 8 9 10 11
// Данные:   * * * * * * *
//
// Результат: 7
// ---------------------------------------------------------------------------
// Итерация: 4 3   2     1
// Позиция:  0 1 2 3 4 5 6 7 8 9 10 11
// Данные:
// 
// Результат: 0
// ---------------------------------------------------------------------------
// Итерация:             1     2     3
// Позиция:  0 1 2 3 4 5 6 7 8 9 10 11
// Данные:   * * * * * * * * * *  *  *
//
// Результат: 12
// ---------------------------------------------------------------------------

Программа под linux для тестирования алгоритма, файл div2-search-test.c:

//     Дата: 01 сентября 2012
//    Автор: Дмитрий Бравиков (bravikov@gmail.com)
//   Сборка: gcc -Wall div2-search-test.c div2-search.c -o div2-search-test
//    Вызов: ./div2-search-test <size> [free_position]
// Описание: size - количество позиций, free_position - свободная позиция.

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "div2-search.h"

unsigned size;
unsigned free_position;

int main(int argc, char *argv[])
{
    bool free_position_specify = false;
    
    if (argc > 1)
    {
        int s = atoi( argv[1] );
        if (s < 0)
        {
            printf("error arguments: size should not be less than zero\n");
            return 0;
        }
        size = s;
    }
    
    if (argc > 2)
    {
        int fp = atoi( argv[2] );
        
        if (fp < 0)
        {
            printf("error arguments: free position should not be less than zero\n");
            return 0;
        }
        
        free_position = fp;
        
        if (free_position >= size)
        {
            printf("error arguments: free position must be less than size\n");
            return 0;
        }

        free_position_specify = true;
    }
    
    printf("size = %i, ", size);
    
    if (free_position_specify)
    {
        printf("free position = %i; test...\n", free_position);
        int result = div2_search_free_position(size);
        if (result == free_position)
            printf(" ok, ");
        else
            printf(" fall, ");
        printf("free position = %i, ", result);
        printf("iterations = %i\n", div2_search_iteration_count);
        return 0;
    }
    
    printf("free position = 0...%i; test...\n", size-1);
    
    int fall_count = 0;
    
    for(free_position = 0; free_position < size; free_position++)
    {
        int result = div2_search_free_position(size);
        if (result == free_position)
            printf(" ok, ");
        else
        {
            fall_count++;
            printf(" fall, ");
        }
        printf("free position = %i, ", result);
        printf("iterations = %i\n", div2_search_iteration_count);
    }
    
    printf("fall = %i\n", fall_count);
    
    return 0;
}

bool div2_search_is_data(const unsigned position)
{
    if (position < free_position)
        return true;
    return false;
}

Си: преобразование double или float в массив char:

double d;
char * ba = (char *)(&d);

Теперь, обращаясь к элементам массива ba, можно узнать из чего состоит переменная типа double или сформировать её из отдельных байт:

ba[0]; // Первый байт
ba[sizeof(d)-1]; // Последний байт