Используя современные методы C ++ с Arduino

Используя современные методы C ++ с Arduino

January 13, 2023 Uncategorized 0

C ++, быстро модернизировали себя за последние несколько лет. Начиная с введения C ++ 11, язык сделал большой шаг вперед, а также все изменились под капотом. К типичному пользователю Arduino несколько из этого неактуально, возможно, многие из него, однако, язык по-прежнему предоставляет нам некоторые хорошие функции, которые мы можем воспользоваться, как мы программируем наши микроконтроллеры.

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

Насколько огромные ваши целые числа?

C ++ 11 представил серию псевдонимов целочисленных типов фиксированной ширины, которые обеспечит вам количество битов (умноженных 8), которые вы хотите. Есть как подписанные, а также версии без знака. Если вы используете INT16_T для переменной, вы понимаете, это 16 бит, независимо от того, на каких Arduino целенаправляется.

1.
2.
3.
4.
INT8_T / uint8_t – подписанный / не подписанный тип, именно 8 бит в размере.
INT16_T / UINT16_T – подписанный / не подписанный тип, именно 16 бит в размере.
int32_t / uint32_t – подписанный / не подписанный тип, именно 32 бита в размере.
INT64_T / uint64_t – подписанный / не подписанный тип, именно 64 бита в размере.

Это псевдонимы базовых типов, так что на Arduino Uno, INT8_T является точным типом, что и в качестве символа, а также UINT16 – это тот же размер, что и unsigned. Одна заметка, если вы редактируете файл «.CPP», вам придется #include , однако в файле ‘.ino’, вы не делаете.

Анонимные пространства имен

Идея анонимного пространства имен находилось на некоторое время в C ++, однако это касается известности с введением C ++ 11. В C ++ 11 анонимное пространство имен теперь является предпочтительным методом для уточнения переменной, функции или класса, доступного только в настоящих данных (т. Е., они имеют внутреннюю, а не внешнюю, связь). Также возможно хороший метод для консолидации вещей, которые требуются только требование к настоящему файлу. Анонимное пространство имен также называют «Безмятенными пространствами имен», потому что, как следует из названия, они определяются без имени:

1.
2.
3.
пространство имен {

}

Анонимные пространства имен принимают расположение декларированных вещей статическими. Что-нибудь, что вы, возможно, объявили как статическое или const, или записаны как #define, теперь можно вводить в анонимное пространство имен, а также иметь точное одинаковое влияние – все, что определено внутри внутри, не может быть доступен за пределами данных «CPP », которые пространство пространства имен Это в. В Arduino IDE, хотя, если вы добавите вкладку, а также предоставьте новую вкладку имя, которое не заканчивается «.CPP» или «.h», то данные предоставлены расширением ‘.ine , «Когда вы нажмете кнопку« Проверьте », все данные« .ino »объединяются в один файл« .CPP ». Это подразумевает, что все, что вы указываете как статический или постоянный или в анонимном пространстве имен, будут предложены в каждом «данных», потому что, когда они объединяются, они все в конечном итоге в том же файле «CPP ». Обычно это не проблема для маленьких программ Arduino, однако это здорово осознавать его.

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

вместо того, чтобы писать:

1.
2.
3.
4.
5.
6.
// это должно быть использовано экономно в любом случае
#define uver_var 1000.
// Статические переменные, объявленные в данных, являются региональными для файла
Статическое int16_t count = 0;
// Переменные Const, объявленные в данных, также являются региональными к данным
const int16_t memleds = 4;

Теперь вы можете написать:

1.
2.
3.
4.
5.
6.
7.
8.
9.
пространство имен {
const int16_t uvery_var = 1000; // Теперь это безопасно
INT16_T COUNT = 0; // Нет требований для использования статическими
const int16_t memleds = 0; // все еще объявлено const.

класс thisclasshasacommonname {

};
}

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

Автоматически для людей

Ключевое слово CAR, добавлено в C ++ 11, позволяет определить переменную, не понимая его типа. При определении, хотя, как и другие переменные, это тип не может быть изменен, как Rнеопределенные переменные C ++. Компилятор C ++ использует вычет выяснить тип переменной.

1.
2.
3.
авто I = 5; // Я имеет тип INT
AUTO J = 7.5F; // j имеет тип float
auto k = getresult (); // К любой тип GetResult () возвращается

При покупке для вас указать, что вы хотите ссылку, вы можете сделать это:

1.
2.
Auto & Amp; Temp1 = MyValue;
const auto & amp; Temp2 = MyValue;

Соответствующий тип также выводится для советов:

1.
2.
int myvalue = 5;
auto myvalueptr = & amp; myvalue; // myvalueptr – это совет к Int.

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

Используя использование

Там было несколько способов получения псевдонимов в C ++: низко (и опасно) #define, а также менее вредным типом. Использование Typedef предпочитает #define, однако у них есть пара проблем. Сама первая – это читаемость. Подумайте о соблюдении Typedef:

1.
Typedef void (* fp) (int, const char *);

На первый взгляд, особенно если вы не будете использовать C ++, он может быть сложно определить, что это производит псевдоним, FP, то есть совет к функции, которая возвращает пространство, а также требует int, а также Струнный параметр. Теперь давайте посмотрим на C ++ 11 Way:

1.
Использование fp = void (*) (int, const char *);

Теперь мы можем видеть, что FP представляет собой псевдоним для чего-либо, а также немного проще, чтобы определить, что это функциональный совет, который это псевдоним.

Вторая разница немного гораздо более эзотерическая, по крайней мере, в программировании Arduino. Использование может быть примежена для Typedefs не может.

Nulleptr.

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

Когда вы начинаете использование авто, однако, вы начнете видеть код, как это:

1.
2.
3.
Авто результат = getResult (…);

Если (результат == 0) {…}

Просто посмотрите на это, вы не можете сказать, если GetResult возвращает наконечник или целое число. Тем не менее, даже среди тех, кто все еще использует C, не очень много не будет проверять, если результат == 0, они проверят, если результат == NULL.

1.
2.
3.
Авто результат = getResult (…);

Если (Результат == Нуль) {…}

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

C ++ 11 Модификации этого путем введения NULLPTR, который является фактическим типом наконечника. Теперь вы напечатаете:

1.
2.
3.
Авто результат = getResult (…);

Если (Результат == NULLPTR) {…}

Теперь вы понимаете, что GetResult может вернуть только указатель. Как и если кто-то модифицирует его на вас, то они получит ошибку компиляции, если они не также могут модифицировать то, если вы утверждено. Введение Nulleptr также подразумевает, что безопаснее перегружать технику на интегральный тип, а также тип наконечника. Например:

1.
2.
3.
4.
5.
void setValue (INT I); // (1)
void setValue (виджет * Вт); // (2)

SetValue (5); // Телефонные звонки 1
SetValue (NULL); // Точно так же телефонные звонки 1

Поскольку NULL не является указателем, второй контакт нами для SetValue Phone вызывает версию, которая принимает целочисленный параметр. Теперь мы можем правильно позвонить в второй SetValue, используя NULLPTR:

1.
SetValue (NULLPTR); // Телефонные звонки 2

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

Инициализация по умолчанию

Учитывая соблюдение класса:

1.
2.
3.
4.
5.
6.
7.
8.
класс foo {
Foo (): FOOSTRING (NULLPTR) {…}
Foo (const char * str): foostring (nullptr) {…}
Foo (const foo & amp; Другое): foostring (nullptr) {…}

частный:
CHAR * FOOOSTRING;
};

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

1.
2.
3.

частный:
CHAR * FOOOSTRING = NULLPTR;

С C ++ 11, мы можем указать предварительное значение по умолчанию – мы все еще можем переопределить это в каждом конструкторе, если мы обязаны, но, если мы этого не имеем, это не имеет значения, насколько многочисленные конструкторы мы добавляем, мы только требоваем установить значение в одном месте. Если мы разделили наш класс в данных «.h», а также файл «.CPP», то дополнительное преимущество в том, что нам нужно только открыть данные «.h», чтобы добавить, а также инициализировать Переменная.

Очистить ваши перемычки

Одной из вещей, которые пытаются сделать C ++, – это позволяет программисту к инкапсулированию вещей, чтобы, например, когда вы называете переменную, вы не непреднамеренно называете вашу переменную то же самое, что и что-то еще с той же именем. C ++ предоставляет вам инструменты, чтобы позволить вам сделать это, например, пространства имен, а также классы. Смертный enum, однако, утечки его входовв окружающий объем:

1.
2.
3.
4.
5.
6.
7.
enum color {
белый,
синий,
желтый
};
// не скомпилируется. Есть уже что-то в этом диапазоне с именем «белый»
Авто белый = 5;

Переменная «белая» не может быть определена, поскольку «белый» является частью enum, а также, что enum утечки его входы в окружающий объем. C ++ 11 представляет определенные отклонения, которые позволяют пару вещей, которые C ++ 98 не позволяли. Первое, как следует наименование, заключается в том, что перечисление полностью выделены. Способ получения нанесения налегания – это использование ключевого слова «класса»:

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
enum class color {
белый,
синий,
желтый
};

Авто белый = 5; // сейчас работает.

Цвет c = белый; // Ошибка, ничего в этом диапазоне не было определено под названием белый.
Цвет C = цвет :: белый; // Правильный.

По умолчанию Scoped Enums имеет базовый тип: int, поэтому какой размер INT INT находится на вашей платформе, это размер, который Sizeof вернет для вашего Enum. До того, как C ++ 11, unscoped Enums также имел базовый тип, однако компилятор пытался быть в том числе в этом, поэтому он определил, как был бы, а в базовом типе, а не сообщите нам – это может улучшить его для размера, а также для продуктов Основной тип, который был наименьшим, который может в форме количества записей. Или это может улучшить скорость, а также произвести тип, который был самым быстрым для платформы. Все это подразумевает, что компилятор понимал, какой тип Enum был, однако мы не сделали, поэтому мы не могли заранее указать его в другой файл. Например,

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
file1.h:

enum color {
белый,
синий,
желтый
};

file2.h:

Цвет Enum; // Ошибка в этом файле компилятор не понимает, что такое тип цвета.

недействительно (цвет C);

Единственный метод – для #include header1.h в Header2.h. Для небольших проектов это нормально, однако в более крупных проектах, добавляя ввод в цвету Enum, будет подразумевать перекомпиляцию всего, что включает в себя header1.h или header2.h. Scoped Enums имеют размер по умолчанию: int, поэтому компилятор всегда понимает, какой размер они есть. И, вы можете модифицировать размер, если хотите:

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
file1.h:

Цвет класса Enum: std :: int8_t {
белый,
синий,
желтый
};

file2.h:

Цвет класса Enum: std :: int8_t;

недействительно (цвет C);

Теперь любой тип данных, которые включают в себя файл2.h, не обязательно нужно перекомпилировать (файл2.cpp должен будет, поскольку вам придется #include file1.h в нем в покупке заставить его компилировать.)

В заключение

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

Leave a Reply

Your email address will not be published. Required fields are marked *