通常,我希望实现一个SerialLog
类,其中包含使用printf
-style API格式化字符串的方法集合,并使用包含在基本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);
从上面的示例中可以看到,我的目的是为串行控制台输出创建一组类方法,其中包含反映函数的参数。
我尝试使用简单的变量定义(如printf
)来实现这样的-style-style API,但是这样的函数定义似乎要求所有参数都是const char *
类型的。这不是我想要的行为因此,我的当前实现使用模板来启用任何类型的参数(显然,该类型最终必须为C++核心printf
函数所接受)。
我的实现包括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()
将字符串输出到串行控制台,而不是简单地返回字符串。与函数一样,SerialLog::sprintf
和SerialLog::printf
都必须接受一个格式字符串作为第一个参数,后面跟着任意数量的可接受类型的可接受参数,这些参数用作提供的格式字符串的替换值。
例如,带有附加参数的"This %s contains %d substituted %s such as this float: %d."
格式( string
(作为char *
)、4
(作为int
)、"values"
(作为char *
)和123.45
(作为float
)将产生以下已编译字符串:"This string contains 4 substituted values such as this float: 123.45."
。
我无法使用以下代码实现所描述的行为:
调试器.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.github.com/robfrawley/7ccbdeffa064ee522a18512b77d7f6f9的Gist。此外,整个项目代码库可以在github.com/src-run/raspetub-arduino-app上引用,相关项目位于lib/Debug/Debug.h和lib/Debug/Debug.cpp。
最后,虽然我精通许多其他语言,如Python、PHP、Ruby和其他语言,但这是第一个C++项目!我正在通过这个应用程序的实现学习C++语言,并且意识到在代码库中存在许多次优选择;随着我对C++知识的发展,这个应用程序的不同方面将被修改和改进。因此,我并不特别感兴趣的是关于我的实现中的缺陷的评论,或者解释我对C++的理解中的缺点的冗长的意见部分。请集中讨论上述单数问题.
谢谢您抽出时间阅读这个问题,我非常感谢您提供的任何帮助!
发布于 2019-10-03 23:00:59
不确定(没有完整的示例,这很困难),但我认为问题在于,您只在debug.h
中声明了模板方法,并且在debug.cpp
中定义了它们。
一般建议:在C++中,请在头文件中声明和定义模板(类、函数、方法、变量)。
关键是,在这种情况下,编译器在需要时实现特定的模板方法。所以如果你用main.cpp写
char const * bar = "bar";
RT::Debug::SerialLog::printf("foo format: %s %i %lf", bar, 0, 1.1);
编译器知道需要一个RT::Debug::SerialLog::printf<char const*, int, double>
,但不能实现它,因为在main.cpp中,只看到了调试器h的内容,其中模板方法SerialLog::printf()
被声明为,但是没有定义。因此编译器无法实现该方法的char const *, int, double
版本。
我建议按以下方式更改文件
-处女作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)
{
char buffer[256];
snprintf(buffer, 256, format, args...);
return String(buffer);
}
SerialLog& print(const char * format);
template <typename ...Args>
SerialLog& printf(const char * format, Args ...args)
{
Serial.print(this->sprintf(format, args...));
return *this;
}
} /* END class SerialLog */
} /* END namespace Debug */
- debug.cpp
#include <debug.h>
namespace Debug
{
String SerialLog::sprint(const char * format)
{
return String(format);
}
SerialLog& SerialLog::print(const char * format)
{
Serial.print(format);
return *this;
}
} /* END namespace Debug */
-结束文件
这样,如果您用main.cpp编写
RT::Debug::SerialLog::printf("foo format: %s %i %lf", bar, 0, 1.1);
编译器知道需要一个RT::Debug::SerialLog::printf<char const*, int, double>
,并且可以实现它,因为可以从调试器h中看到SerialLog::printf()
的定义。
https://stackoverflow.com/questions/58227005
复制相似问题