gpt4 book ai didi

c++ - snprintf 的交叉编译版本在字符串缓冲区中分配适量的内存

转载 作者:行者123 更新时间:2023-12-04 19:08:56 25 4
gpt4 key购买 nike

在我从事的项目(一个 Windows 和 Ubuntu 应用程序)中,我定义了一个与日志库交互的宏。本质上,这个宏接受类似 printf 的输入,转换为字符串,并将其发送到日志库。我附上一个使用 std::cout 的宏示例而不是日志库:

#include <stdio.h>
#include <iostream>

#ifdef _WIN32
#define custom_sprintf sprintf_s
#else
#define custom_sprintf sprintf
#endif
#define LOG_PF(...) \
do \
{ \
char buffer[500]; \
custom_sprintf(buffer, __VA_ARGS__); \
std::cout << buffer << std::endl; \
} while (false)

int main()
{
int a = 3, b = 5;
LOG_PF("%i + %i = %i", a, b, a + b);
return EXIT_SUCCESS;
}
custom_sprintf部分是避免以下窗口警告:

C4996: 'sprintf': This function or variable may be unsafe. Consider using sprintf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.


除非您需要超过 500 个字符来分配正在记录的消息,否则此宏可以完美运行并完成其工作。如果内存不足,Windows 会产生段错误,而 Ubuntu 会打印所需的输出,但运行时会出现编译警告和错误消息:

*** stack smashing detected ***: terminated


为了能够分配适当大小的缓冲区,我尝试了三种方法,但均未成功。
在第一个中,我尝试将大小设为 const size_t所以我可以分配一个正确大小的缓冲区:
#include <stdio.h>
#include <iostream>

#ifdef _WIN32
#define custom_sprintf sprintf_s
#else
#define custom_sprintf sprintf
#endif
#define LOG_PF(...) \
do \
{ \
const size_t neded = snprintf(nullptr, 0, __VA_ARGS__); \
char buffer[neded]; \
custom_sprintf(buffer, __VA_ARGS__); \
std::cout << buffer << std::endl; \
} while (false)

int main()
{
int a = 3, b = 5;
LOG_PF("%i + %i = %i", a, b, a + b);
return EXIT_SUCCESS;
}
该解决方案在 Ubuntu 上完美运行。但是,它不能在 Windows 中编译说

Error C2131 expression did not evaluate to a constant

Error C2664 'int sprintf_s(char *const ,const size_t,const char *const,...)': cannot convert argument 2 from 'const char [13]' to 'const size_t'


在第二个中,我尝试定义两个不同的宏,这显然不是维护的最佳选择:
#include <stdio.h>
#include <iostream>

#ifdef _WIN32
#define LOG_PF(...) \
do \
{ \
int n = snprintf(nullptr, 0, __VA_ARGS__); \
char* buffer = new char[n]; \
sprintf_s(buffer,n, __VA_ARGS__); \
std::cout << buffer << std::endl; \
delete[] buffer; \
} while (false)
#else
#define LOG_PF(...) \
do \
{ \
int n = snprintf(nullptr, 0, __VA_ARGS__); \
char* buffer = new char[n]; \
sprintf(buffer, __VA_ARGS__); \
std::cout << buffer << std::endl; \
delete[] buffer; \
} while (false)
#endif

int main()
{
int a = 3, b = 5;
LOG_PF("%i + %i = %i", a, b, a + b);
return EXIT_SUCCESS;
}
我必须定义两个宏,尽管只有一个不同的行,因为在宏中我无法合并 #ifdef _WIN32部分。在这种情况下,虽然 Linux 可以完美运行,但 Windows 也会产生段错误。
第三种方法使用具有可变长度输入参数的函数而不是宏,并且基于此 answer :
/* sprintf example */
#include <stdarg.h>
#include <stdio.h>

#include <iostream>

#ifdef _WIN32
#define custom_sprintf sprintf_s
#else
#define custom_sprintf sprintf
#endif

void log_pf(const char* format, ...)
{
va_list args;
va_start(args, format);
int result = vsnprintf(NULL, 0, format, args) + 1;
char* buffer = new char[result];
#ifdef _WIN32
custom_sprintf(buffer, result, format, args);
#else
custom_sprintf(buffer, format, args);
#endif
std::cout << buffer << std::endl;
va_end(args);
}

int main()
{
int a = 3, b = 5;
log_pf("%i + %i = %i", a, b, a + b);
return EXIT_SUCCESS;
}
这,代码在这两种情况下都可以编译,但是,在 Windows 中它会产生段错误,而在 Ubuntu 中它不会崩溃,但它没有输出所需的消息:

-1592530864 + -296641163


您能帮我解决这两种方法中的问题,以便我可以拥有上述功能吗?从我的角度来看,最好的方法是类似函数的方法,尤其是在维护方面,但是,只要它有效,它实际上并不重要。
更改为 boost::format如解释 here不被视为一个选项,因为此宏在库中使用了数千次,因此需要大量工作。
使用的编译器是 Ubuntu 中的 g++ (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0 和 Windows 中的 MSVC 19.25.28614.0。

最佳答案

char buffer[neded];是 VLA 扩展,因此 C++ 无效。

您确实必须使用分配(您可以用 std::stringstd::vector<char> 包装)。

那么你可以使用 snprintf既要知道大小又要格式化:
注意 snprintf 返回的值不包括最终的 nul 字符,所以你必须加 1。

#define LOG_PF(...)                                      \
do \
{ \
const int n = 1 + snprintf(nullptr, 0, __VA_ARGS__); \
std::vector<char> buffer(n); \
snprintf(buffer.data(), n, __VA_ARGS__); \
std::cout << buffer.data() << std::endl; \
} while (false)

Demo

关于c++ - snprintf 的交叉编译版本在字符串缓冲区中分配适量的内存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61886176/

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