Как правило, я хотел бы реализовать класс SerialLog
, содержащий коллекцию методов, которые форматируют строки с использованием API-стиля printf
и выводят скомпилированное сообщение на последовательную консоль Arduino с помощью метода Serial.print()
, содержащегося вбазовая arduino
библиотека.
Такой код позволил бы намного более чистые вызовы журнала последовательной консоли в моем коде (ниже показано, как показаны необходимые вызовы вложенных функций базовой библиотеки Arduino c ++, тогда как последние два вызова показываютAPI, который я хотел бы реализовать):
# normal debug logging for Arduino using the provided functions
Serial.log(sprintf("A log message format string: %d/%f", 123, 456.78));
# the call I would like to use for string formatting
String message = SerialLog.sprintf("A log message format string: %d/%f", 123, 456.78);
# the call I would like to use to output a formatted string
SerialLog.printf("A log message format string: %d/%f", 123, 456.78);
Как видно из приведенных выше примеров, я намерен создать набор методов класса для последовательного вывода на консоль с аргументами, отражающими нормальный C ++printf
function .
Я пытался реализовать такой printf
-стиль API, используя простые вариационные определения, такие как myVariadicPrintfFunction(const char * format, ...)
, но такое определение функции, похоже, требует, чтобы все аргументыимеют тип const char *
. Это не то поведение, которое я хочу. Таким образом, моя текущая реализация использует шаблоны для включения аргументов любого типа (хотя, очевидно, тип должен быть в конечном счете приемлемым для функции ядра printf
C ++).
Моя реализация включает в себя следующие открытые методы в SerialLog
класс:
SerialLog::sprint
(String sprint(const char * format)
): принимает аргумент const char *
. Возвращает строку в виде объекта Arduino String
.
SerialLog::sprintf
(template <typename ...Args> String sprintf(const char * format, Args ...args)
): принимает аргумент const char *
в качестве строки формата и любойколичество дополнительных аргументов (различных типов), которые будут подставлены в строку формата. Возвращает строку как объект Arduino String
.
SerialLog::print
(SerialLog& print(const char * format)
): То же, что и SerialLog::sprint
для вывода строки в последовательный портконсоль использует Serial.print()
вместо простого ее возврата.
SerialLog::printf
(template <typename ...Args> SerialLog& printf(const char * format, Args ...args)
): использует возвращаемое значение SerialLog::sprintf
для вывода строкик последовательной консоли, используя Serial.print()
вместо простого ее возврата.
Как и в случае нормальной функции C ++ printf
, оба SerialLog::sprintf
и SerialLog::printf
должен принять строку формата в качестве первого аргумента, за которым следует любое количество допустимых аргументов любого приемлемого типа , которые используются в качестве значений замещения для предоставленной строки формата.
Например, формат"This %s contains %d substituted %s such as this float: %d."
с дополнительными аргументами string
(как char *
), 4
(как int
), "values"
(как char *
) и 123.45
(как * 1085)*) приведет к следующей скомпилированной строке: "This string contains 4 substituted values such as this float: 123.45."
.
Мне не удалось выполнить описанное поведение, используя следующий код:
debug.h
#include <stdio.h>
#include <Arduino.h>
namespace Debug
{
class SerialLog
{
public:
String sprint(const char * format);
template <typename ...Args>
String sprintf(const char * format, Args ...args);
SerialLog& print(const char * format);
template <typename ...Args>
SerialLog& printf(const char * format, Args ...args);
} /* END class SerialLog */
} /* END namespace Debug */
debug.cpp
#include <debug.h>
namespace Debug
{
String SerialLog::sprint(const char * format)
{
return String(format);
}
template <typename ...Args>
String SerialLog::sprintf(const char * format, Args ...args)
{
char buffer[256];
snprintf(buffer, 256, format, args...);
return String(buffer);
}
SerialLog& SerialLog::print(const char * format)
{
Serial.print(format);
return *this;
}
template <typename ...Args>
SerialLog& SerialLog::printf(const char * format, Args ...args)
{
Serial.print(this->sprintf(format, args...));
return *this;
}
} /* END namespace Debug */
В это время возникают следующие ошибкиво время компиляции:
C:\Temp\ccz35B6U.ltrans0.ltrans.o: In function `setup':
c:\arduino-app/src/main.cpp:18: undefined reference to `String RT::Debug::SerialLog::sprintf<char const*>(char const*, char const*)'
c:\arduino-app/src/main.cpp:22: undefined reference to `RT::Debug::SerialLog& RT::Debug::SerialLog::printf<char const*>(char const*, char const*)'
c:\arduino-app/src/main.cpp:26: undefined reference to `RT::Debug::SerialLog& RT::Debug::SerialLog::printf<char const*>(char const*, char const*)'
c:\arduino-app/src/main.cpp:29: undefined reference to `RT::Debug::SerialLog& RT::Debug::SerialLog::printf<char const*>(char const*, char const*)'
c:\arduino-app/src/main.cpp:30: undefined reference to `RT::Debug::SerialLog& RT::Debug::SerialLog::printf<char const*, int, double>(char const*, char const*, int, double)'
collect2.exe: error: ld returned 1 exit status
*** [.pio\build\debug\firmware.elf] Error 1
Примечание. Приведенный выше код извлекается из большего пространства имен Debug
и расширенного класса SerialLog
, который содержит дополнительные методы, поэтому следующая строка сообщения об ошибкечисла не будут правильно отображать приведенный пример кода.
Полный журнал сборки VSCode (с использованием расширения PlatformIO) можно найти в виде Gist по адресу gist.github.com / robfrawley / 7ccbdeffa064ee522a18512b77d7f6f9 . Кроме того, на всю кодовую базу проекта можно ссылаться по адресу github.com / src-run / raspetub-arduino-app , а соответствующие проекты для этого вопроса расположены по адресу lib / Debug / Debug.h и lib / Debug / Debug.cpp .
Наконец, хотя я владею многими другими языками, такими как Python, PHP, Ruby и другими, , это первый проект C ++ ! Я изучаю язык C ++ с помощью реализации этого приложения и знаю, что в базе кода существует много неоптимальных вариантов;различные аспекты этого приложения будут исправлены и улучшены по мере развития моих знаний C ++. Таким образом, Меня не особенно интересуют комментарии относительно недостатков в моей реализации или подробные мнения, объясняющие недостатки в моем понимании C ++ . Пожалуйста, продолжайте обсуждение с единственным вопросом, изложенным выше.
Спасибо, что нашли время, чтобы прочитать весь этот вопрос, и я очень признателен за любую предоставленную помощь!