【问题标题】:How to write a platform independent wrapper function in C++ [duplicate]如何在 C++ 中编写独立于平台的包装函数 [重复]
【发布时间】:2020-05-31 09:18:00
【问题描述】:

我正在使用 snprintf 将输出发送到缓冲区。使用 C++ 11 并用于桌面应用程序。

到目前为止,我只为 Windows 做这件事。但从现在开始,它必须支持不同的平台(Windows、Linux 和 Mac)

为了支持多平台,我打算写一个带有#if标签的包装函数。

但在这里我面临的挑战是,当从项目的不同位置调用 WrapperSprintf 时,参数的数量是不同的。

如何编写一个通用的包装器,可以在不同的地方使用不同的参数传递给 WrapperSprintf 函数?

我尝试了如下所示的包装函数。请帮助我如何继续:

void WrapperSprintf( char buffer, const char *format, ... )
{
#if defined(_WIN32)
    _snprintf_s(buffer, sizeof(buffer), format,...);
#else
    snprintf(buffer, sizeof(buffer), format, ...);
#endif
}

调用 WrapperSprintf 函数1:

char m_systemTime[20];

char* CUPSManager ::getSystemTime()
{
    time_t rawtime;
    struct tm * timeinfo;
    time ( &rawtime );
    timeinfo = localtime ( &rawtime );

    WrapperSprintf(m_systemTime,"%d-%d-%d :%d:%d:%d" , timeinfo ->tm_year +1900,
             timeinfo ->tm_mon +1,
             timeinfo->tm_mday,
             timeinfo->tm_hour,
             timeinfo->tm_min,
             timeinfo->tm_sec);
    return m_systemTime;
}

调用 WrapperSprintf 函数2:

void getDevicePath()
{
    wstring strDevPath;
    strDevPath = (LPCWSTR)cDevicePath;
    char cDevPath[2048];
    WrapperSprintf(cDevPath,"%ls", strDevPath.c_str());
    int nPathLength = strlen(cDevPath);
...
}

【问题讨论】:

  • 为什么需要一个包装器呢? MSVC 似乎支持 snprintf 而没有 _。 (并且_snprintf 不太安全;如果缓冲区不够长,它不会以空值终止字符串。)
  • 查看va_list,这是 C 捕获省略号的方式 (...)。但是,为什么不使用 C++?
  • snprintf 是标准 C++ 函数。为什么你需要一个包装器?
  • 这看起来像 C 代码。不是惯用的 C++ 代码。
  • @bolov snprintf()(带有 n)是在 C++11 和/或 C99 中添加的。在此之前,它的实施并不标准。在编写可移植代码时,经常会看到配置检查以查看它是否以当前语言定义(因为它并不总是存在)。

标签: c++ c++11 c++14


【解决方案1】:

您可以在WrapperSprintf 中分别使用vsnprintf_vsnprintf 代替snprintf_snprintf。这些函数使用可变参数列表。

参考:

【讨论】:

    【解决方案2】:

    我不会这样写的:

    void WrapperSprintf( char buffer, const char *format, ... )
    {
    #if defined(_WIN32)
        _snprintf_s(buffer, sizeof(buffer), format,...);
    #else
        snprintf(buffer, sizeof(buffer), format, ...);
    #endif
    }
    

    这里不需要函数。您想要的是选择正确的功能。
    而是这样做:

    #if defined(_WIN32)
    #define WrapperSprintf    _snprintf_s
    #else
    #define WrapperSprintf    snprintf
    #endif
    

    如果你想变得复杂并有能力重新排列参数,那么我们可以更进一步:

    #if defined(_WIN32)
    #define WrapperSprintf(buffer, size, ...)    _snprintf_s(buffer, size, __VA_ARGS__)
    #elif defined(_WIERDYSTEM)
    #define WrapperSprintf(buffer, size, ...)    wierdsnprintf(size, buffer, __VA_ARGS__)
    #else
    #define WrapperSprintf(buffer, size, ...)    snprintf(buffer, size, __VA_ARGS__)
    #endif
    

    注意:__VA_ARGS__ 必须至少匹配一个参数(即它不能匹配零)。因此,如果格式字符串中没有 % 标记,则将 if 用于“格式和格式参数”,因为可能存在零个“格式参数”。

    【讨论】:

    • 嗨@Martin York,感谢您提供详细信息。如果我像你提到的那样使用,它是否适用于 WrapperSprintf 中的不同参数。因为我从一个地方调用带有 7 个参数的 WrapperSprintf 函数,而从另一个地方调用带有 3 个参数的函数,就像我在问题中提到的那样。
    • @JohnPaulCoder 是的,...(省略号)将匹配 1 个或多个参数,__VA_ARGS__ 是如何将 ... 放置在输出端。
    • 好的,谢谢。我会试试这个。但是,正如我建议的那样,我可以将您提供的代码放在一个公共文件中吗?这样我就可以从项目中的任何地方访问该功能?
    • @JohnPaulCoder 是的。只需将上述内容放在标题中即可。
    【解决方案3】:

    如何在 C++ 中编写独立于平台的包装函数?

    一种方法阅读 C++ 标准(例如,n3337 用于 C++11),或者选择更新的标准 such as C++17并坚持那里提到的功能

    另一种方法是在库之上编写 C++ 代码(例如 QtPOCOBoostGTKmm在您想要的所有平台之上提供抽象目标。

    对于独立的 C++ 代码 cross-compiled 然后在嵌入式平台上运行(例如,FreeNOSRaspBerry Pi 4 的某些端口,或最近的 AndroidAutoSar)这可能是一个技术挑战。

    仔细阅读你的 C++编译器的文档

    选择足够好的build automation 工具(例如GNU makeNinja 或其他)来运行您的C++ 编译器(和其他程序,可能是您的元程序)。

    还可以考虑 C++ 程序的 Web 界面。

    您会想到WtLibonion 之类的工具包(或只是cpp-httplib)。当今大多数桌面操作系统都有一些网络浏览器(例如 Firefox,它本身是用 C++ 编码的)。

    也考虑一些meta-programming 方法:

    编写您的小程序,生成有助于可移植性的 C++ 代码。

    SWIG 生成器可能有用或鼓舞人心(另请参见 ANTLR 解析器生成器、Qt mocRefPerSys 项目、FLTK 工具包以获取灵感或搜索Quine 用 C++ 编写的程序)。 GPPGNU m4 预处理器也很有用。您生成 C++ 代码的元程序可能使用 PythonLuaGuile 或仅使用 C++ 等编写...

    所以启用与可移植性相关的警告,如果可从您的 C++ 编译器获得。对于GCC,请阅读this。考虑在 2020 年底使用一些静态分析器(例如 Clang static analyzerFrama-C++CoverityBugSengBismon;请参阅 this draft 报告)来帮助您。

    记住一句老话

    没有portable代码之类的东西,只有一些移植软件....

    署名忘记了,但我在上个世纪读过类似的东西

    【讨论】:

    • 嗨@Basile Starynkevitch,我想在为windows、linux服务的通用实用程序文件中编写一个通用函数,我们可以在整个项目中调用这个函数。我可以像我尝试的那样继续该功能(WrapperSprintf)吗?
    • 针对哪个 C++ 标准(C++11、C++14、C++17、C++20)和哪个 C++ 目标实现(Android、您自己的 C++ 操作系统内核、Windows、 Linux,FreeRTOS,......)以及使用哪个 C++ 编译器或交叉编译器? 细节很重要,所以edit你的问题请。
    • 适用于 C++ 标准 C++ 11,适用于桌面应用程序。
    • 为什么不能用Qt,或者POCO,或者Boost?哪个操作系统上的桌面(Linux不一样作为Windows或Android)? 请在您的问题中用书面英语解释这一点。如果您改进了您的问题,我可以投票重新提出问题。
    • 使用Wt 并使用网络浏览器作为程序的界面怎么样? 请改进您的问题
    猜你喜欢
    • 1970-01-01
    • 2010-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-19
    • 1970-01-01
    • 1970-01-01
    • 2013-09-13
    相关资源
    最近更新 更多