Требуется: идея шаблона C++ для решения проблемы, но во время компиляции?
У нас есть константный массив структур, примерно такой:
static const SettingsSuT _table [] = {{5,1}, {1,2}, {1,1} и т. д.};
структура имеет следующее:
- size_bytes:
- число_объекты:
- Другие члены "метаданных"
Таким образом, "общий размер" равен size_bytes*num_items для одного элемента. Вся эта информация находится в массиве const, доступном во время компиляции. Но, обратите внимание, общий размер _table не связан с размером самой EEPROM. _table не отражает EEPROM, он описывает только макет, использование и другую информацию типа метаданных, которая нам нужна. Но вы можете использовать эти метаданные, чтобы определить объем используемой нами ЭСППЗУ.
Массив просто описывает данные, которые хранятся во внешней EEPROM, которая имеет фиксированный / максимальный размер. По мере добавления и удаления функций записи в массиве const изменяются. В настоящее время мы выполняем проверку общего размера данных во время выполнения, чтобы убедиться, что они не превышают размер EEPROM.
Однако во многих из этих проверок во время выполнения мы перешли к проверкам шаблонов в стиле static_assert, поэтому сборка немедленно останавливается. Я не эксперт по шаблонам, поэтому мог бы помочь с этим.
Итак, вопрос: как создать шаблон, чтобы сложить размер всех элементов (умножить значения каждого элемента, а затем сложить все результаты), а затем выполнить static_assert и остановить сборку, если они превышают размер магического числа ЭСППЗУ. Я рассматривал типичный пример рекурсивного факториального шаблона как один из подходов, но он не может получить доступ к массиву, он требует значения const (я думаю).
большое спасибо за любую помощь,
4 ответа
Ваша проблема в том, что они являются постоянными, но они не являются постоянными выражениями при оценке:
// f is constant, but its value not known at compile-time
int const f = rand() % 4;
Что вам нужно, так это настоящие константные выражения. Ты можешь использовать boost::mpl
составить вектор mpl из пар mpl, каждая из которых имеет пару интегральных констант:
using namespace boost::mpl;
typedef vector<
pair< int_<5>, int_<1> >,
pair< int_<1>, int_<2> >,
pair< int_<1>, int_<1> >,
> numbers;
Теперь вы можете перебирать его элементы, используя boost::mpl
алгоритмы. каждый int_
это выставляет статическую константу Int value
установите значение, которое вы сказали. Это будет оцениваться до постоянного выражения:
// get at the first element of the pair, located in the first element
// of the vector. Then get its ::value member.
int array[at<numbers, 0>::type::first::value];
И это фактически сделало бы этот массив, содержащий 5 элементов.
Сайт boost:: mpl Справочное руководство: здесь
Если вы измените значения на параметры шаблона, а не на аргументы конструктора или другое инициализированное значение во время выполнения, они будут константами, которые можно использовать для ваших static_asserts.
Я не уверен, как это может работать для массива структур, хотя. Возможно, вам придется объявить свои структуры с помощью некоторой макропроцессорной магии, чтобы она отслеживала ваши выделения для вас.
BEGIN_EEPROM_STRUCT_TABLE()
STRUCT(size, num_bytes)
// etc.
END_EEPROM_STRUCT_TABLE()
Это может объявить вашу таблицу и const, который суммирует все размеры, при условии, что они постоянны во время компиляции (и что вы, конечно, пишете макросы соответствующим образом).
С моего кресла я был бы склонен попытаться выразить данные конфигурации в виде структуры, чтобы можно было проверить размер во время компиляции (любой статический оператор может сделать это). Трудно сказать, будет ли это подходящим подходом, основанным на предоставленной информации, но в равной степени на тех же основаниях я не вижу какой-либо непосредственной причины отклонить его.
Базовый перевод таблицы будет выглядеть примерно так:
struct SettingsTable
{
char a[5][1];//maybe call it "featureX_conf", or whatever...
char b[1][2];
char c[1][1];
};
(Вставьте подходящую магию компилятора, чтобы удалить отступы и выравнивание, хотя на практике char
В большинстве комбинаций платформа / компилятор кажется невосприимчивым к подобным вещам.)
Затем, чтобы получить размеры можно использовать sizeof
по мере необходимости. Если фактическая таблица значений все еще необходима, она может быть сгенерирована таким образом. Это было бы немного некрасиво, но достаточно легко спрятано за макросом.
Будет ли это "лучше", чем более сложный подход, вероятно, зависит от целевого рынка.
По сути, это тот же ответ, что и litb, но также показывает, как добавить размеры без жесткого кодирования размера массива. Вот почему это кажется таким сложным.
Не ясно, с какой частью вам нужна помощь. Ниже описано, как вы используете типы для отслеживания всех метаданных, а не массива в памяти. Это позволяет скомпилировать проверки времени с перечислениями, которые вы не можете сделать с const int в структурах. Если у вас есть больше метаданных, добавьте дополнительные параметры в шаблон для настроек и сохраните их как значения перечисления.
Использование библиотеки Loki: http://loki-lib.sourceforge.net/index.php?n=Main.Development. Он ограничен 18 "настройками" из-за того, как реализован MakeTypeList.
Ты можешь использовать TypeAt<TList, index>::Result::size_bytes
(например) для доступа к метаданным.
#include "loki-0.1.7\include\loki\typelist.h"
#include "loki-0.1.7\include\loki\static_check.h"
using namespace Loki;
using namespace Loki::TL;
// based on the Length<> template from loki for adding up the sizes
template <class TList> struct TotalSize;
template <> struct TotalSize<NullType>
{ enum { value = 0 }; };
template <class T, class U>
struct TotalSize< Typelist<T, U> >
{ enum { value = T::size_bytes*T::num_items + TotalSize<U>::value }; };
// struct for holding the sizes (and other meta data
// if you add extra template args and enum values)
template <size_t s, size_t n> struct Settings
{ enum { size_bytes = s, num_items = n }; };
// the table of setting structs (limited to 18)
typedef MakeTypelist< Settings<5,1>, Settings<1,2>, Settings<1,1> >
SettingsSuT;
int _tmain(int argc, _TCHAR* argv[])
{
LOKI_STATIC_CHECK(TotalSize< SettingsSuT::Result >::value == 8,is8);
// this will trigger at compile time if uncommented
//LOKI_STATIC_CHECK(TotalSize< SettingsSuT::Result >::value != 8,isnt8);
int x = TotalSize< SettingsSuT::Result >::value;
return 0;
}