如何在 C++ 中编写独立于平台的包装函数 [重复]

Posted

技术标签:

【中文标题】如何在 C++ 中编写独立于平台的包装函数 [重复]【英文标题】:How to write a platform independent wrapper function in C++ [duplicate] 【发布时间】: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 中添加的。在此之前,它的实施并不标准。在编写可移植代码时,经常会看到配置检查以查看它是否以当前语言定义(因为它并不总是存在)。 【参考方案1】:

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

参考:

http://www.cplusplus.com/reference/cstdio/vsnprintf/ https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/vsnprintf-vsnprintf-vsnprintf-l-vsnwprintf-vsnwprintf-l?view=vs-2019

【讨论】:

【参考方案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++ 代码(例如 Qt 或 POCO 或 Boost 或 GTKmm)在您想要的所有平台之上提供抽象目标。

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

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

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

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

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

也考虑一些meta-programming 方法:

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

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

所以启用与可移植性相关的警告,如果可从您的 C++ 编译器获得。对于GCC,请阅读this。考虑在 2020 年底使用一些静态分析器(例如 Clang static analyzer、Frama-C++、Coverity、BugSeng 或 Bismon;请参阅 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 并使用网络浏览器作为程序的界面怎么样? 请改进您的问题

以上是关于如何在 C++ 中编写独立于平台的包装函数 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

最易于使用、轻量级、独立于平台的 C++ 图形库

如何制作 Qt 线程包装器

如何围绕 C++ 代码编写 C 包装器以公开类方法

如何在适用于 Android 和 iOS 的 C++ 跨平台库中正确链接 OpenCV?

C++ - 如何以独立于平台、线程安全的方式以用户首选的日期/时间语言环境格式格式化文件的最后修改日期和时间

如何在 Linux 平台上创建用于用 C++ 编写的临时文件?