Использование C ++ во встроенных системах - PullRequest
25 голосов
/ 23 сентября 2008

Каких особенностей C ++ следует избегать во встроенных системах?

Пожалуйста, классифицируйте ответ по причине, такой как:

  • использование памяти
  • размер кода
  • Скорость
  • Портативность

РЕДАКТИРОВАТЬ: позволяет использовать ARM7TDMI с оперативной памятью 64 КБ в качестве цели для контроля объема ответов.

Ответы [ 17 ]

1 голос
/ 24 сентября 2008

функции времени обычно зависят от ОС (если вы их не переписываете). Используйте свои собственные функции (особенно если у вас есть RTC)

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

исключения не очень переносимы и

функции printf, которые не записывают в буфер, не являются переносимыми (вам нужно каким-то образом подключиться к файловой системе для записи в ФАЙЛ * с помощью printf). Используйте только функции sprintf, snprintf и str * (strcat, strlen) и, конечно, их широкие символьные компоненты (wcslen ...).

Если проблема в скорости, возможно, вам следует использовать свои собственные контейнеры, а не STL (например, контейнер std :: map, чтобы удостовериться, что ключ совпадает, 2 (да 2) сравнивает с ' Оператор less '(a [меньше чем] b == false && b [меньше] a == false означает a == b).' less '- единственный параметр сравнения, полученный классом std :: map (и не только Это может привести к некоторой потере производительности в критических подпрограммах.

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

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

malloc использует переменную _end (обычно объявляется в сценарии компоновщика) для выделения памяти, но это не безопасно для потоков в «неизвестных» средах.

иногда вам следует использовать Thumb вместо режима Arm. Это может улучшить производительность.

Так что для 64k памяти я бы сказал, что C ++ с некоторыми его приятными функциями (STL, исключения и т. Д.) Может быть излишним. Я бы определенно выбрал C.

1 голос
/ 23 сентября 2008

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

Например:

// foo.h
template <typename T> void foo () { /* some relatively large definition */ }

// b1.cc
#include "foo.h"
void b1 () { foo<int> (); }

// b2.cc
#include "foo.h"
void b2 () { foo<int> (); }

// b3.cc
#include "foo.h"
void b3 () { foo<int> (); }

Компоновщик, скорее всего, объединит все определения 'foo' в одну единицу перевода. Поэтому размер 'foo' не отличается от размера любой другой функции пространства имен.

Если ваш компоновщик не делает этого, то вы можете использовать явное создание экземпляра, чтобы сделать это для вас:

// foo.h
template <typename T> void foo ();

// foo.cc
#include "foo.h"
template <typename T> void foo () { /* some relatively large definition */ }
template void foo<int> ();        // Definition of 'foo<int>' only in this TU

// b1.cc
#include "foo.h"
void b1 () { foo<int> (); }

// b2.cc
#include "foo.h"
void b2 () { foo<int> (); }

// b3.cc
#include "foo.h"
void b3 () { foo<int> (); }

Теперь рассмотрим следующее:

// foo.h
inline void foo () { /* some relatively large definition */ }

// b1.cc
#include "foo.h"
void b1 () { foo (); }

// b2.cc
#include "foo.h"
void b2 () { foo (); }

// b3.cc
#include "foo.h"
void b3 () { foo (); }

Если компилятор решит встроить 'foo' для вас, вы получите 3 разных копии 'foo'. В поле зрения нет шаблонов!

РЕДАКТИРОВАТЬ: Из комментария выше от InSciTek Джефф

Используя явные экземпляры для функций, которые, как вы знаете, будут использоваться только, вы также можете гарантировать, что все неиспользуемые функции будут удалены (что может фактически уменьшить размер кода по сравнению со случаем без шаблона):

// a.h
template <typename T>
class A
{
public:
  void f1(); // will be called 
  void f2(); // will be called 
  void f3(); // is never called
}


// a.cc
#include "a.h"

template <typename T>
void A<T>::f1 () { /* ... */ }

template <typename T>
void A<T>::f2 () { /* ... */ }

template <typename T>
void A<T>::f3 () { /* ... */ }

template void A<int>::f1 ();
template void A<int>::f2 ();

Если ваша цепочка инструментов не будет полностью разорвана, вышеприведенный код будет генерировать код только для 'f1' и 'f2'.

0 голосов
/ 24 сентября 2008

Обратите внимание, что стоимость исключений зависит от вашего кода. В одном профилированном приложении (относительно небольшое на ARM968) поддержка исключений добавила 2% к времени выполнения, а размер кода был увеличен на 9,5 КБ. В этом приложении исключения генерировались только в том случае, если произошло что-то серьезно плохое - то есть никогда не было на практике - из-за чего время выполнения было очень низким.

0 голосов
/ 23 сентября 2008

Одна особая проблема, которая удивила меня ATMega GCC 3.something: когда я добавил виртуальную функцию ember в один из моих классов, мне пришлось добавить виртуальный деструктор. В этот момент компоновщик запросил оператор delete (void *). Я понятия не имею, почему это происходит, и добавление пустого определения для этого оператора решило проблему.

0 голосов
/ 23 сентября 2008

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

0 голосов
/ 23 сентября 2008

Для встраиваемых систем вам преимущественно нужно избегать вещей, которые имеют определенные ненормальные затраты времени выполнения. Некоторые примеры: исключения и RTTI (включая dynamic_cast и typeid ).

0 голосов
/ 23 сентября 2008

Убедитесь, что вы знаете, какие функции поддерживаются компилятором для вашей встроенной платформы, а также убедитесь, что вы знаете особенности вашей платформы. Например, компилятор CodeComposer от TI не выполняет автоматическое создание шаблонов. В результате, если вы хотите использовать сортировку STL, вам нужно вручную создать пять разных вещей. Он также не поддерживает потоки.

Другим примером является то, что вы можете использовать чип DSP, который не имеет аппаратной поддержки для операций с плавающей запятой. Это означает, что каждый раз, когда вы используете float или double, вы оплачиваете стоимость вызова функции.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...