В том типе встроенного программирования, которым я занимаюсь, очень ценятся детерминизм и прозрачность работающего кода. Под прозрачностью я подразумеваю, например, возможность просматривать произвольные участки памяти и знать, какая переменная там хранится. Так что, как я уверен, программисты встраиваемых систем ожидают, new следует избегать, если это вообще возможно, а если этого нельзя избежать, то ограничиться инициализацией.
Я понимаю необходимость этого, но не согласен с тем, как это сделали мои коллеги, и я не знаю лучшей альтернативы.
У нас есть несколько глобальных массивов структур и несколько глобальных классов. Существует один массив структур для мьютексов, один для семафоров и один для очередей сообщений (они инициализируются в main). Для каждого запущенного потока класс, которому он принадлежит, является глобальной переменной.
Самая большая проблема, с которой я столкнулся, заключается в модульном тестировании. Как я могу вставить фиктивный объект, когда класс, который я хочу протестировать, глобальные переменные #include
s, которых я не делаю?
Вот ситуация в псевдокоде:
foo.h
#include "Task.h"
class Foo : Task {
public:
Foo(int n);
~Foo();
doStuff();
private:
// copy and assignment operators here
}
бар.ч
#include <pthread.h>
#include "Task.h"
enum threadIndex { THREAD1 THREAD2 NUM_THREADS };
struct tThreadConfig {
char *name,
Task *taskptr,
pthread_t threadId,
...
};
void startTasks();
bar.cpp
#include "Foo.h"
Foo foo1(42);
Foo foo2(1337);
Task task(7331);
tThreadConfig threadConfig[NUM_THREADS] = {
{ "Foo 1", &foo1, 0, ... },
{ "Foo 2", &foo2, 0, ... },
{ "Task", &task, 0, ... }
};
void FSW_taskStart() {
for (int i = 0; i < NUMBER_OF_TASKS; i++) {
threadConfig[i].taskptr->createThread( );
}
}
Что делать, если я хочу больше или меньше задач? Другой набор аргументов в конструкторе foo1? Я думаю, что мне нужно было бы иметь отдельные bar.h и bar.cpp, что кажется гораздо более трудоемким, чем необходимо.