Comments 21
Похоже на std::variant для enum.
// categories.h
enum { error_category_shift=16, error_code_mask=(1<<error_category_shift)-1 };
enum { cpu_category, measure_category, my_category };
// cpu.h #include "categories.h"
enum {
cpu_ok=cpu_category << error_category_shift,
cpu_alu,
cpu_rom,
cpu_ram
};
// measure.h #include "categories.h"
enum {
measure_ok=measure_category << error_category_shift,
measure_outoflimits,
measure_badcode
};
// my.h #include "categories.h"
enum {
my_ok=my_category << error_category_shift,
my_error1,
my_error2,
my_error3
};
// errorcodes.h
int any_error(int code);
int get_error_category(int code);
const char* get_category_name(int category);
const char* get_error_text(int code);
int error_code;
...
error_code=some_function();
if (any_error(error_code)) {
log_error(error_code);
}
...
// in cpu
const char* get_error_text_cpu(int code) {
switch(code) {
case cpu_ok: return "cpu_ok";
case cpu_alu: return "cpu_alu";
case cpu_rom: return "cpu_rom";
case cpu_ram: return "cpu_ram";
}
return 0;
}
// in measure
const char* get_error_text_measure(int code) {
switch(code) {
case measure_ok: return "measure_ok";
case measure_outoflimits: return "measure_outoflimits";
case measure_badcode: return "measure_badcode";
}
return 0;
}
// in my
const char* get_error_text_my(int code) {
switch(code) {
case my_ok: return "my_ok";
case my_error1: return "my_error1";
case my_error2: return "my_error2";
case my_error3: return "my_error3";
}
return 0;
}
// in errorcodes
int get_error_category(int code) { return code >> error_category_shift; }
int any_error(int code) { return code & error_code_mask; }
const char* get_category_name(int category) {
switch(category) {
case cpu_category: return "cpu_category";
case measure_category: return "measure_category";
case my_category: return "my_category";
};
return 0;
}
const char* get_error_text(int code) {
const char* res;
res=get_error_text_cpu(code); if (res) return res;
res=get_error_text_measure(code); if (res) return res;
res=get_error_text_my(code); if (res) return res;
return 0;
}
Подобный код элементарно генерировать скриптом что бы не писать руками.
1. Надо писать скрипт
2. Каждый такой метод get_error, даже если он будет возвращать просто номер придется юнит тестить. В вашем примере, вообще ад для юнит теста — каждую ветку switchа, хотя она и сгенерирована автоматически. Но покрытие юнит тестами должно 100% иначе не пройдет сертификацию на безопасность. В примере же на С++ там этого делать не надо, так как методов(GetEnumPosition() аналог) таких нет в коде, они все посчитают на этапе компиляции.
3. Ну и минорное методы занимают место, так как существуют в рантайме. Хотя справедливости ради, надо сказать, что их место на С++ занимает конструктор, который типизирован, но там только инициализация идет, т.е. по сути кода нет. Т.е. я полагаю, что на Си кода получится больше. Надо проверить.
4. Не требуется современный компилятор C++
5. Функции с текстом можно разложить по отдельным объектникам и сложить в библиотеку, и если они не используются то и не прилинкуются.
6. Скрипт это для ленивых («моется только тот кому лень чесаться»), остальные могут писать руками. (скриптом можно и тесты генерить, причем много и быстро)
7. Проще, короче, нагляднее и собирается быстрее.
«сертификация на безопасность» это обязательное требование для любого кода?
ps: компилятор тоже сертифицированный?
5. Это если енумы совпадают, а если в новом проекте надо расширить енум, или ввести новый. В одном проекте один объектник, в другом — другой.
6. Согласен, либо скрипт, либо руками писать. Одна из задач звучала, как практически «ничего не делать».
7. На Си проще — согласен, также как и быстрее собирается. На С++ код выглядит страшненько, для нормального программиста встроенного софта.
Плюсы С++ все равно перевешивают :) Кода то меньше в итоге в ПЗУ.
Компилятор, да сертифицированный. IAR Certified by TÜV SÜD
Кстати на C++14 можно тоже без рекурсии через constexpr.
template <typename... Ts> class TypeList {
private:
static constexpr auto no_index = std::numeric_limits<size_t>::max();
template <typename T> static constexpr size_t getIndexImpl() {
auto seq = {same<T, Ts>()...};
size_t i = 0u;
for (auto s : seq) {
if (s) {
return i;
}
i++;
}
return no_index;
}
template <typename U, typename V> static constexpr bool same() {
return std::is_same<U, V>::value;
}
public:
template <typename T> static constexpr size_t getIndex() {
constexpr bool found = getIndexImpl<T>() != no_index;
static_assert(found, "Type T not found in TypeList");
return getIndexImpl<T>();
}
};
using ClassList = TypeList<C1, C2>;
static_assert(ClassList::getIndex<C1>() == 0, "Test failed");
static_assert(ClassList::getIndex<C2>() == 1, "Test failed");
Да, но я не до конца понял как работает constexpr. Вроде не обязательно всегда именно if constexpr писать, потому что это больше для замены enable_if и свинае ада. Но я проверил что действительно в моем примере вызовы заменяются на константу. Но в общем случае это уже на усмотрение компилятора, т.е. если я присваиваю constexpr value что то, то выражение справа вычислится во время компиляции. Но всегда ли constexpr функции во время компиляции считается?
Отвечу сам себе. Да constexpr функция не обязательно вызывается во время компиляции. Её можно вызывать и во время выполнения. В случае если аргументы известны только в ран тайм, это удобно чтобы не писать две версии одной и той же функции.
Да, тогда в моем коде лучше получать index, потом сверять его в static_assert с no_index а потом return index. Тогда можно быть уверенным что оно посчитает в compile time.
Есть вариант проще, называется проверено временем… Как например РТОС сертифицируется, основывается на расчете отказов у большого количества пользователей за определенное время, скажем 3-5 лет. Но тоже надо чтобы на предприятии была система отслеживания отказов.
template <typename Tuple, typename T, template <typename, typename> typename Pred = std::is_same, size_t I = 0>
constexpr size_t tuple_index() noexcept
{
if constexpr(I >= std::tuple_size_v<Tuple>)
return I;
else if constexpr(Pred<T, std::tuple_element_t<I, Tuple>>::value)
return I;
else
return tuple_index<Tuple, T, Pred, I + 1>();
}
Унифицированная обработка ошибок (C++ вариант для микроконтроллеров)