我可以确定参数是否是字符串文字?

abs*_*urd 18 c c++

是否可以确定在宏或函数中传递的参数在编译时或运行时是否是字符串文字?

例如,

#define is_string_literal(X)
...
...   

is_string_literal("hello") == true;
const char * p = "hello";
is_string_literal(p) == false;
Run Code Online (Sandbox Code Playgroud)

要么

bool is_string_literal(const char * s);

is_string_literal("hello") == true;
const char * p = "hello";
is_string_literal(p) == false;
Run Code Online (Sandbox Code Playgroud)

谢谢.

Chr*_*utz 25

是!(感谢James McNellisGMan进行更正.更新以正确处理连接文字,例如"Hello, " "World!"在连接之前进行字符串化.)

#define is_literal_(x) is_literal_f(#x, sizeof(#x) - 1)
#define is_literal(x) is_literal_(x)

bool is_literal_f(const char *s, size_t l)
{
    const char *e = s + l;
    if(s[0] == 'L') s++;
    if(s[0] != '"') return false;
    for(; s != e; s = strchr(s + 1, '"'))
      {
        if(s == NULL) return false;
        s++;
        while(isspace(*s)) s++;
        if(*s != '"') return false;
      }
    return true;
}
Run Code Online (Sandbox Code Playgroud)

这将在将参数传递给函数之前对其进行字符串化,因此如果参数是字符串文字,则传递给函数的参数将被引号括起来.

如果你认为这是一个字符串文字:

const char *p = "string";
// should is_literal(p) be true or false?
Run Code Online (Sandbox Code Playgroud)

我不能帮你.您可以使用某些实现定义(或*shudder*undefined)行为来测试字符串是否存储在只读内存中,但是p可以修改某些(可能是较旧的)系统.

对于那些质疑使用这种功能的人,请考虑:

enum string_type { LITERAL, ARRAY, POINTER };

void string_func(/*const? */char *c, enum string_type t);
Run Code Online (Sandbox Code Playgroud)

而不是string_function在每次调用时显式指定第二个参数,而是is_literal允许我们用宏包装它:

#define string_func(s) \
    (string_func)(s, is_literal(s)  ? LITERAL :
        (void *)s == (void *)&s ? ARRAY : POINTER)
Run Code Online (Sandbox Code Playgroud)

我无法想象为什么它会产生影响,除了在普通的C中,文字不是,const并且由于某种原因你不想/不能把这个函数写成const char *代替a char.但是有各种各样的理由要做某事.总有一天,你也可能觉得有必要采取可怕的黑客行为.

  • 即使它有效,我也会在代码审查中立即拒绝它.这是一个如此庞大的WTF. (10认同)
  • @In silico - 任何依赖于了解函数是否传递字符串文字或更可变的字符串的代码可能已经无法通过代码审查。 (3认同)
  • +1非常好的开箱即用的例子. (2认同)

iam*_*ind 5

在编译时知道(如所提到的),使用以下技术。您可以确定给定的参数是否为字符串文字。如果是像这样的数组或指针const char x[], *p;它将引发编译器错误。

#define is_string_literal(X) _is_string_literal("" X)
bool _is_string_literal (const char *str) { return true; } // practically not needed
Run Code Online (Sandbox Code Playgroud)

[注意:我以前的回答是专家否决的意见,但在编辑后尚未被接受或赞成。我要再回答一个内容相同的问题。]


eca*_*mur 5

是的; 在 C++20 中:

#include <type_traits>
#define IS_SL(x) ([&]<class T = char>() { \
    return std::is_same_v<decltype(x), T const (&)[sizeof(x)]> and \
    requires { std::type_identity_t<T[sizeof(x) + 1]>{x}; }; }())
Run Code Online (Sandbox Code Playgroud)

也就是说,字符串文字是对 const char 数组进行类型引用的内容,可用于初始化相同大小或更大大小的 char 数组。(最后一点是防止可能采用P1997 放宽对数组的限制,这将允许char[6]从 a初始化 a char const[6],但不允许从 a初始化char const[5]。)

测试用例:

#include <source_location>
int main(int argc, char* argv[]) {
    static_assert(IS_SL("hello"));
    static_assert(IS_SL("hello" "world"));
    static_assert(IS_SL(R"(hello)"));
    static_assert(not IS_SL(0));
    static_assert(not IS_SL(argc));
    static_assert(not IS_SL(argv[0]));
    char const s[] = "hello";
    static_assert(not IS_SL(s));
    constexpr char const cs[] = "hello";
    static_assert(not IS_SL(cs));
    constexpr char const* sp = "hello";
    static_assert(not IS_SL(sp));
    static_assert(IS_SL(__FILE__));
    static_assert(not IS_SL(std::source_location::current().file_name()));
}
Run Code Online (Sandbox Code Playgroud)

演示: https: //godbolt.org/z/TMG5r9ffz


Jam*_*lis 4

不。字符串文字只是char(在 C 中)或const char(在 C++ 中)的数组。

您无法区分字符串文字和char像这样的其他数组(在 C++ 中):

const char x[] = "Hello, World!";
Run Code Online (Sandbox Code Playgroud)

  • @iammilind:我发现有趣的是,您对这个答案投了反对票,但您所谓的优雅“解决方案”甚至不适用于我在答案中提出的微不足道的案例。 (3认同)