Набор параметров шаблона для парных аргументов

#c #c 11 #arduino #variadic-templates #microcontroller

Вопрос:

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

 // Saves the configuration values to the file system
template <typename... Args>
void SaveConfig(const char *name, String amp;value, Args amp;...args)
{
    Serial.println("Saving config...");
    StaticJsonDocument<JSON_OBJECT_SIZE(2)   200> doc;
    SetData(doc, name, value, args...);

    File configFile = SPIFFS.open("/config.json", "w");
    if (!configFile)
        Serial.println("Failed to open config file for writing!");

    serializeJson(doc, configFile);
    serializeJson(doc, Serial);
    configFile.close();
}
 

Все, что мне нужно было бы сделать, это:

 doc[name] = value;
 

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

 template <typename... Args>
static void SetData(JsonDocument amp;doc, const char *name, String amp;value, Args amp;...args)
{
    doc[name] = value;
    SetData(doc, args...);
}
 

Но это создает еще одну проблему. Когда пакет параметров «заканчивается», он хочет вызвать setData() без параметров. Поэтому теперь мне нужно создать перегрузку этой функции без параметров (кроме doc).

Так есть ли лучший способ сделать это?

Комментарии:

1. Вы настаиваете на шаблоне? Я бы использовал a std::vector (или, возможно std::initializer_list ) структур (или std::pair s),

2. std на самом деле недоступен на микроконтроллерах. Я имею в виду, что это вроде как работает, но std::вектор выделяет память в куче.

3. std::initializer_list не. Я также был бы обеспокоен шаблонами (особенно рекурсивными, такими как ваш), раздувающими двоичный файл.

4. @Колебаться. Отсутствие STL в MCU также неверно. Он доступен и может быть использован, нет проблем зарезервировать части памяти для кучи. Правда, однако, в том, что его нужно использовать очень осторожно. Небольшой размер кучи и частые выделения могут легко привести к тому, что память будет фрагментирована настолько, что дальнейшее выделение станет невозможным.

5. Если вы ограничены C 11, я почти уверен, что решение, которое у вас есть, настолько чистое, насколько это возможно. С C 17 вы могли бы улучшить его с помощью выражений сгиба.

Ответ №1:

Если вы действительно хотите использовать шаблоны вместо контейнеров, вы можете попробовать следующее:

 template<typename ...Args, std::size_t ...I>
void SetDataImpl(JsonDocumentamp; doc, std::tuple<Args...> tup, std::index_sequence<I...>) {
  int dummy[] = {
    (doc[std::get<2*I>(tup)] = std::get<2*I 1>(tup), 0)...
  };
}

template<typename ...Args>
void SetData(JsonDocumentamp; doc, Args amp;...args) {
  static_assert(sizeof...(args) % 2 == 0, "");
  SetDataImpl(doc, std::forward_as_tuple(args...), std::make_index_sequence<sizeof...(args) / 2>{});
}
 

Но, как упоминал @HolyBlackCat, этот способ был бы лучше.

 void SetData(JsonDocumentamp; doc, std::initializer_list<std::pair<const char*, String>> il = {}) {
  for(const autoamp; elem : il) { // Just autoamp; maybe. Depends on json library implementation
    doc[elem.first] = elem.second;
  }
}