С++: Типобезопасное перечисление (enum class) в unordered_map

Это статья о языке программирования C++.

Элементы обычного перечисление (enum) запросто могут применяться в качестве ключа в unordered_map. Следующий пример показывает, как сопоставить элемент перечисления с текстовым значением.

#include <string>
#include <iostream>
#include <unordered_map>

enum Color { Red, Blue, Green, };

int main()
{
    std::unordered_map<int, std::string> colors{
        {Red, "Red"},
        {Blue, "Blue"},
        {Green, "Green"},
    };
    
    std::cout << colors.at(Red) << std::endl;
}

Выполнить код.

Этот пример выведет в консоль слово «Red». Заметьте, что тип ключа — int.

Такое перечисление не типобезопасно и устарело. Язык C++11 предоставляет типобезопасные перечисления (enum class). У такого перечисления для доступа к элементам надо указывать имя перечисления.

Ниже приведен пример типобезопасного перечисления.

enum class Month
{
    January,
    February,
    March,
    April,
    May,
    June,
    July,
    August,
    September,
    October,
    November,
    December,
};

Чтобы получить доступ к элементу January, нужно написать Month::January.

Элементы такого перечисления можно использовать в аналогичном unordered_map, но только с явным привидением типа с помощью static_cast. Пример:

int main()
{
    std::unordered_map<int, std::string> colors{
        {static_cast<int>(Month::January), "January"},
        {static_cast<int>(Month::February), "February"},
    };
    
    std::cout << colors.at(static_cast<int>(Month::January)) << std::endl;
}

Использование static_cast делает код громоздким.

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

#include <string>
#include <iostream>
#include <unordered_map>

struct EnumClassHash
{
    template <typename T>
    std::size_t operator()(T t) const
    {
        return static_cast<std::size_t>(t);
    }
};

enum class Month
{
    January,
    February,
    March,
    April,
    May,
    June,
    July,
    August,
    September,
    October,
    November,
    December,
};

int main()
{
    std::unordered_map<Month, std::string, EnumClassHash> months{
        {Month::January,   "January"},
        {Month::February,  "February"},
        {Month::March,     "March"},
        {Month::April,     "April"},
        {Month::May,       "May"},
        {Month::June,      "June"},
        {Month::July,      "July"},
        {Month::August,    "August"},
        {Month::September, "September"},
        {Month::October,   "October"},
        {Month::November,  "November"},
        {Month::December,  "December"},
    };
    
    std::cout << months.at(Month::January) << std::endl;
}

Выполнить код.

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

Таким образом можно создавать сколько угодно unordered_map c различными перечислениями, используя один единственный класс EnumClassHash. Иначе пришлось бы в пространстве имен std определять хеш-функцию для каждого перечисления.

Данный способ почерпнут в одном вопросе на StackOverflow.

Реклама

Что происходит при выравнивании кода табуляцией

Среди программистов существует спор, что использовать для отступа: табуляцию или пробелы.

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

Следует различать отступы и выравнивание. Отступ (indent) используется, чтобы сместить начало строки. Выравнивание (alignment) используется внутри строки, чтобы сместить одну часть строки относительно другой.

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

Допустим, программист сделал выравнивание табуляцией шириной 4 пробела, а потом решил поменять ширину.

Ширина: 4 пробела (эталонное выравнивание)

int variable                = 1;
int superVariable           = 2;
int anotherSuperVariable    = 3;

Ширина: 2 пробела

int variable        = 1;
int superVariable     = 2;
int anotherSuperVariable  = 3;

Ширина: 3 пробела

int variable            = 1;
int superVariable       = 2;
int anotherSuperVariable   = 3;

Ширина: 8 пробелов

int variable                            = 1;
int superVariable                       = 2;
int anotherSuperVariable        = 3;

Как видно, выравнивание разваливается.

Иллюстрация табуляция для выравнивания кода в программировании

Сборка Qt Virtual Keyboard и добавление русской раскладки

Qt Virtual Keyboard Русская раскладка

Qt Virtual Keyboard — это библиотека предоставляющая виртуальную экранную клавиатуру. Она входит в библиотеку Qt начиная с Qt 5.7. Точнее, она и раньше входила, но была доступна только в коммерческой версии.

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

Далее приводится инструкция по сборке Qt Virtual Keyboard и добавлению русской раскладки в Линукс. Все действия выполняются в консоле. Для Виндовс инструкция не приводится, но она похожа.

Библиотека Qt должна быть установлена вместе с исходным кодом. Для этого при установке нужно отменить пункт Sources.

Установка исходного кода Qt

Создадим папку для сборки и перейдем в нее.

mkdir /tmp/build-qtvirtkb
cd /tmp/build-qtvirtkb

Создадим переменную, содержащую путь к папке, в которой установлен Qt.

QTDIR=/opt/Qt5.9.1/5.9.1

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

Сконфигурируем проект Qt Virtual Keyboard с помощью qmake.

$QTDIR/gcc_64/bin/qmake "CONFIG += lang-en lang-ru" $QTDIR/Src/qtvirtualkeyboard
 

Языки раскладок задаются в строке "CONFIG += lang-en lang-ru". Таким образом клавиатура будет поддерживать английскую и русскую раскладки. Полный список языков приведен в официальной документации. Если указать"CONFIG += lang-all", то клавиатура будет поддерживать все возможные языки.

Соберем проект с помощью make.

make

Сборка занимает некоторое время.

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

sudo make install

Готово. Теперь в клавиатуре будет русская раскладка.

Если нужно изменить раскладки, то нужно пересобрать проект следующими командами.

make distclean
$QTDIR/gcc_64/bin/qmake "CONFIG += lang-all" $QTDIR/Src/qtvirtualkeyboard
make
sudo make install

Первая команда нужна, чтобы очистить сборку.

Поиск использования сигналов в Qt Creator

В Qt Creator можно встать курсором на переменную или функцию, вызвать контекстное меню и выбрать «Найти использование».

Qt Creator найти использование

Тогда Qt Creator найдет все места, где используется эта переменная или функция.

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

connect(
    object1,
    SIGNAL(superSignal()),
    object2,
    SLOT(superSlot())
);

Qt Creator просто не будет ничего искать, если вы попробуете сделать это на функции, которая заключена в SIGNAL().

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

class SuperClass
{
...
signals:
    superSignal();
}

В этому случае Qt Creator найдет только испускания сигнала (emit).

Решение есть — использовать современный способ подключения сигналов и слотов, без макросов SIGNAL() и SLOT().


connect(
    obj1,
    &SuperClass1::superSignal,
    obj2,
    &SuperClass2::superSlot
);

Данный синтаксис был введен в Qt 5.0 в 2012 году.

Официальное описание сигналов и слотов в Qt на английском.

Удаление объектов в Qt

Эта статья учит избегать утечек памяти и ошибок сегментирования при программировании на C++ с библиотекой Qt.

qt segmetation fault fuuu

Читать далее Удаление объектов в Qt

Файловый хостинг Amazon S3 + Django + Heroku

Django — библиотека для создания сайтов на языке Python.

Heroku — платформа, которая позволяет размещать сайты, сделанные на Django.

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

Допустим, что мы уже зарегистрировались на Heroku и Amazon.

Настройка Amazon

Перейдем в My Security Credentials (учетные данные безопасности).

My Security Credentials

Создадим новый ключ доступа с помощью кнопки Create New Access Key.

Create New Access Key

Появится окно с идентификатором ключа и секретным ключом. Их надо сохранить, чтобы потом настроить Heroku. Сохранить можно в файл с помощью кнопки Download Key File.

Access Key

Теперь нужно перейти в Amazon S3 и создать хранилище (кнопка Create Bucket).

Amazon S3

Зададим DNS-совместимое имя хранилища и регион такой же как на Heroku, чтобы минимизировать задержки при передаче данных. Остальные настройки можно оставить без изменения. Имя понадобится для настройки Heroku.

Create Bucket

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

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

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

example.txt

Настройка Heroku

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

Heroku Config Variables

Требуется назначить 4 переменные окружения на основе настроек Amazon:

  • AWS_ACCESS_KEY_ID — идентификатор ключа.
  • AWS_SECRET_ACCESS_KEY — секретный ключ.
  • AWS_STORAGE_BUCKET_NAME — имя хранилища.
  • AWS_URL — адрес, по которому располагается хранилище.

Настройка проекта Django

Для работы с Amazon S3 нужно установить пакеты django-storages и boto3.

  • django-storages — это коллекция хранилищ для Django.
  • boto3 — библиотека для работы с сервисами Amazon.

Если используется файл requirements.txt для удовлетворения зависимостей, то в него нужно прописать пакеты нужных версий, например:

django-storages==1.6.5
boto3==1.4.7

Установить пакеты можно следующей командой:

pip install -r requirements.txt

В файле settings.py нужно прописать следующий код.

INSTALLED_APPS = (
    ...
    'storages',
    ...
)

AWS_ACCESS_KEY_ID = os.environ.get('AWS_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = os.environ.get('AWS_SECRET_ACCESS_KEY')
AWS_STORAGE_BUCKET_NAME = os.environ.get('AWS_STORAGE_BUCKET_NAME')
AWS_URL = os.environ.get('AWS_URL')

DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'

AWS_MEDIA_URL = "{}/{}/".format(AWS_URL, AWS_STORAGE_BUCKET_NAME)

MEDIA_URL = AWS_MEDIA_URL

Продолжение будет…

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

Ссылки

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

Подсветка кода с помощью Markdown в Gitlab и на Github

В файле README.md можно разместить код с подсветкой синтаксиса.

Делается это с помощью обратных кавычек (`).

Код внутри строк можно выделить если с двух сторон поставить по одной обратной кавычки. Пример: `код`.

Многострочный код выделяется тремя обратными кавычками с каждой стороны:

```
function test() {
    console.log("notice the blank line before this function?");
}
```

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

```javascript
function test() {
    console.log("notice the blank line before this function?");
}
```

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

Ссылки

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;
}

Добавление Sitemap с помощью DjangoCMS

Sitemap — XML-файлы с информацией для поисковых систем (таких как Google, Яндекс, Bing, Поиск@Mail.Ru) о страницах веб-сайта, которые подлежат индексации.

DjangoCMS может самостоятельно сгенерировать Sitemap с опубликованными на сайте страницами.

Настроить выдачу Sitemap очень просто, следуя следующей инструкции.

  1. В файле настроек settings.py добавляем django.contrib.sitemaps в INSTALLED_APPS.
  2. Добавляем следующую строчку в начало файла urls.py.
    from cms.sitemaps import CMSSitemap
  3. В том же файле (urls.py) добавляем в список urlpatterns следующий код.
    url(
        r'^sitemap\.xml$',
        'django.contrib.sitemaps.views.sitemap',
        {'sitemaps': {'cmspages': CMSSitemap}}
    ),
    
  4. Запускаем локальный сайт. Вводим в адресную строку браузера http://127.0.0.1:8000/sitemap.xml (или кликаем по ссылке) и убеждаемся в наличие Sitemap.

Ссылки

How to create sitemaps (официальная документация на английском)