我在C++环境中使用了一些C函数。我收到以下警告,因为C++不允许将字符串文字分配给char *
类型。
C++11 不允许从字符串文字转换为字符 *
我的代码:
void sys_vgui(char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
//do something
va_end(ap);
}
void open(t_buf *x)
{
sys_vgui("wm deiconify .x%lxn", x);
sys_vgui("raise .x%lxn", x);
sys_vgui("focus .x%lx.textn", x);
}
我可以通过 const 强制转换字符串文字来删除这些警告,如下所示。
sys_vgui(const_cast<char *>("wm deiconify .x%lxn"), x);
sys_vgui(const_cast<char *>("raise .x%lxn"), x);
sys_vgui(const_cast<char *>("focus .x%lx.textn"), x);
但我不确定它是否真的安全,因为我看到很多人说不要直接将字符串文字转换为char *
。
所以我想出了以下似乎更安全的解决方案。
char str1[] = "wm deiconify .x%lxn";
sys_vgui(str1, x);
char str2[] = "raise .x%lxn";
sys_vgui(str2, x);
char str3[] = "focus .x%lx.textn";
sys_vgui(str3, x);
但它使我的代码变得肮脏且难以维护,因为每当我使用该函数时,我都必须使用不同的名称(例如 str1、str2、str3...)创建多个变量。
所以我的问题是:
1)在我的情况下使用const_cast<char *>
真的不安全吗?
2)有什么解决方案可以使用char
数组编写干净的代码,而不必使用不同的名称创建多个变量?
只要sys_vgui
不修改字符串,它就是安全的,大概不是,因为修改字符串文字在 C 中具有未定义的行为,无论是否const
。所以如果你有类似的东西
sys_vgui("wm deiconify .x%lxn", x);
在 C 中,带有const_cast
的 C++ 版本同样安全。
但是,为了消除C++代码的丑陋,我可能会编写一个包装函数:
template<typename ...Ts>
void xsys_vgui(const char *fmt, Ts ...args) {
sys_vgui(const_cast<char *>(fmt), args ...);
}
现在
xsys_vgui("wm deiconify .x%lxn", x);
应该在C++"正常工作"。
不要滥用const_cast
意图(即使没有滥用它,也要首先尝试想办法避免它)。
这是相当可怕的,但可以做你想要的:你想要的API的模板版本,旨在制造必要的可变数组,并在之后适当地转发它。对于实际提供非 const 数组或指针的调用方,它应该直接调用 api。
#include <iostream>
#include <algorithm>
void do_function(char *ptr)
{
std::cout << __PRETTY_FUNCTION__ << 'n';
}
template<size_t N>
void do_function(const char (&ar)[N])
{
std::cout << __PRETTY_FUNCTION__ << 'n';
char inside[N];
std::copy(ar, ar+N, inside);
do_function(inside+0);
}
int main()
{
char msg[] = "array test";
do_function(msg);
do_function("Something const");
do_function("Nothing");
}
输出
void do_function(char *)
void do_function(const char (&)[N]) [N = 16]
void do_function(char *)
void do_function(const char (&)[N]) [N = 8]
void do_function(char *)
注意:我还没有真正通过拧干机,但它可能会实现您想要的。最大的好处是您无需更改任何先前的调用(除了删除那些const_cast
误称)。传递字符串文本的原始调用将开始工作。您需要做的就是挂起模板版本,让编译器整理其余部分。
如果你真的知道sys_vgui
不使用传递的指针来修改某些东西,这在实践中很好,虽然很脏,因为这个假设只是在你的脑海中。
在 c++17 中,您可以使用std::string::data
:
sys_vgui(std::string{"wm deiconify .x%lxn"}.data(), x);
在此之前(在 c++11 和 c++14 中),您必须手动指定大小以std::array
(包括 null 终止符,但如果它是错误的,编译器可能会抱怨)
sys_vgui(std::array<char, 20>{"wm deiconify .x%lxn"}.data(), x);
如果你不想改变sys_vgui
那么你可以做一个包装器:
template<typename... Args>
void sys_vgui_c(char const *fmt, Args&&... args)
{
sys_vgui( const_cast<char *>(fmt), args... );
}
然后用字符串文本调用它:sys_vgui_c("wm deiconify .x%lxn", x);
可变参数宏怎么样:
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#define my_vgui(fmt_,...)
do {
char myfmt[strlen(fmt_) + 1];
strcpy(myfmt,fmt_);
sys_vgui(myfmt,##__VA_ARGS__);
} while (0)
void
sys_vgui(char *fmt,...)
{
va_list ap;
va_start(ap,fmt);
fmt[0] |= 0;
vprintf(fmt,ap);
va_end(ap);
}
int
main(void)
{
my_vgui("hellon");
my_vgui("hello %sn","world");
return 0;
}
请注意,可能有更干净的方法来执行宏,因此请参阅:https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html
所以我最近才开始使用一些不完全支持 C++ STL 库的嵌入式设备,并且不得不依赖使用一些库,这些库可能会也可能不会试图符合旧的 C 标准,我相信没有定义关键字const
,因此接受字符串文字的函数必须处理char*
,我认为你应该"信任",该方法不会写入您的"字符串"。
我也不喜欢在任何地方看到const_cast<char*>("abc")
否则我只会写"abc"
所以就我的目的而言,定义一个简单的宏很容易:
#define S_(x) const_cast<char*>(x)
并按如下方式使用它:
print(S_("abc"));
它更加简洁,假设您的命名空间还没有很多大写字母宏,例如我选择使用的宏。