gpt4 book ai didi

c++ - 在Arduino的C++中使用可变参数模板编写自定义[s] printf

转载 作者:塔克拉玛干 更新时间:2023-11-03 07:01:55 28 4
gpt4 key购买 nike

通常,我想实现一个SerialLog类,该类包含使用printf -style API格式化字符串的方法的集合,并使用基本Serial.print()库中包含的arduino方法将编译后的消息输出到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);

从上面的示例中可以看出,我的意图是为带有镜像 normal C++ printf function的参数的串行控制台输出创建一组类方法。

我已经尝试使用简单的可变参数定义(例如 printf)来实现这种 myVariadicPrintfFunction(const char * format, ...)样式的API,但是这样的函数定义似乎要求所有参数的类型均为 const char *。这不是我想要的行为。因此,我当前的实现使用模板来启用任何类型的参数(不过,该类型最终必须最终为C++核心 printf函数所接受)。

我的实现在 SerialLog类中包含以下公共(public)方法:
  • 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()将字符串输出到串行控制台,而不是简单地返回它。

  • normal C++ printf function一样, SerialLog::sprintfSerialLog::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."

    我无法使用以下代码实现所描述的行为:

    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扩展)可以作为ist位于 gist.github.com/robfrawley/7ccbdeffa064ee522a18512b77d7f6f9上。此外,可以在 github.com/src-run/raspetub-arduino-app引用整个项目代码库,与此问题相关的项目位于 lib/Debug/Debug.hlib/Debug/Debug.cpp

    最后,虽然我精通Python,PHP,Ruby和其他许多其他语言,但这是第一个C++项目!我正在通过该应用程序的实现来学习C++语言,并且意识到代码库中存在许多次优的选择。随着我对C++的了解,本应用程序的各个方面都将进行修改和改进。因此,对于有关实现方面的缺陷的评论或解释我对C++的缺点的详细意见,我并不特别感兴趣。 请继续将讨论集中在上述单个问题上。

    感谢您抽出宝贵时间阅读整个问题,非常感谢您提供的任何帮助!

    最佳答案

    不确定(没有完整的示例很困难),但是我想问题是您只在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中,仅看到debug.h的内容,其中模板方法 SerialLog::printf()声明为 ,但 尚未定义。因此,编译器无法实现该方法的 char const *, int, double版本。

    我建议如下更改文件

    -调试
    #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>并可以实现它,因为可以从debug.h中看到 SerialLog::printf()定义

    关于c++ - 在Arduino的C++中使用可变参数模板编写自定义[s] printf,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58227005/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com