繁体   English   中英

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

[英]Can I determine if an argument is string literal?

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

例如,

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

is_string_literal("hello") == true;
const char * p = "hello";
is_string_literal(p) == false;

或者

bool is_string_literal(const char * s);

is_string_literal("hello") == true;
const char * p = "hello";
is_string_literal(p) == false;

谢谢。

是的! (感谢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;
}

这将在将参数传递给函数之前对其进行字符串化,因此如果参数是字符串文字,则传递给我们函数的参数将被引号字符包围。

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

const char *p = "string";
// should is_literal(p) be true or false?

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

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

enum string_type { LITERAL, ARRAY, POINTER };

void string_func(/*const? */char *c, enum string_type t);

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

#define string_func(s) \
    (string_func)(s, is_literal(s)  ? LITERAL :
        (void *)s == (void *)&s ? ARRAY : POINTER)

我无法想象它为什么会有所作为,除非在纯 C 中文字不是const并且由于某种原因您不想/不能将函数编写为采用const char *而不是char 但是有各种各样的理由想要做某事。 有一天,你也可能觉得有必要诉诸可怕的黑客攻击。

使用以下技术在编译时了解(如问题所述)。 您可以确定给定参数是否为字符串文字。 如果它是一些数组或指针,如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

[注意:我之前的回答被专家否决,在编辑后尚未被接受或投赞成票。 我正在提出另一个具有相同内容的答案。]

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

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

const char x[] = "Hello, World!";

如果 expr 是字符串文字,则 Clang 和 gcc 的__builtin_constant_p(expr)返回 1,否则返回 0。使用 clang 3.5+ 和 gcc 4.6+ 进行测试。

由于 clang 预先定义了GCC ,您可以简单地

#ifdef __GCC__
  ... use __builtin_constant_p(expr)
#else
  ... use fallback
#endif

https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html

是的; 在 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)]>{x}; }; }())

也就是说,字符串文字是对 const char 数组的类型引用,可用于初始化相同大小的 char 数组。

测试用例:

#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()));
}

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

试试这个:

#define is_string_literal(s) \
  (memcmp(#s, "\"", 1) == 0)

根据 C/C++ 变量命名约定,变量名必须以“_”或字母开头。

也许不是操作想要的,但我使用

#define literal(a) "" s
#define strcpy(a, b ) _strcpy(a, literal(b))

这使得

char buf[32];
char buf2[32];
const char *p = "hello,";

strcpy(buf, "hello"); // legal
strcpy,(buf, p);  // illegal
strcpy(buf, buf2); // illegal

这是一种适用于 C 和 C++ 的简单可移植方法,在编译时完全评估,用于测试简单的字符串文字,即:没有反斜杠的单字符串文字:

#define STR(s)  #s
#define is_string_literal(s)  (sizeof(STR(#s)) == sizeof(#s) + 4 \
                               && #s[0] == '"' && #s[sizeof(#s)-2] == '"')

它只是测试字符串转换是否以"开头和结尾,并且两次应用字符串转换只会将字符串的长度增加 4,仅转义初始和尾随"

这是一个测试程序:

#include <stdio.h>

#define STR(s)  #s
#define is_simple_string(s)  (sizeof(STR(#s)) == sizeof(#s) + 4 \
                              && #s[0] == '"' && #s[sizeof(#s)-2] == '"')

int main() {
#define TEST(s)  printf("%16s -> %d\n", #s, is_simple_string(s))
    char buf[4] = "abc";
    const char cbuf[4] = "def";
    char *p = buf;
    const char *cp = cbuf;

#define S "abc"
    TEST(1);
    TEST('x');
    TEST(1.0);
    TEST(1LL);
    TEST(main);
    TEST(main());
    TEST(S);
    TEST("");
    TEST("abc");
    TEST("abc\n");
    TEST("abc\"def");
    TEST("abc" "");
    TEST("abc"[0]);
    TEST("abc"+1);
    TEST("abc"-*"def");
    TEST(""+*"");
    TEST("a"+*"");
    TEST("ab"+*"");
    TEST("abc"+*"");
    TEST(1+"abc");
    TEST(buf);
    TEST(buf + 1);
    TEST(cbuf);
    TEST(cbuf + 1);
    TEST(p);
    TEST(cp);
    TEST(p + 1);
    TEST(cp + 1);
    TEST(&p);
    TEST(&cp);
    TEST(&buf);
    TEST(&cbuf);

    return *cp - *p - 3;
}

它只为TEST(S)TEST("")TEST("abc")输出1

我有一个类似的问题:我想说

MY_MACRO("compile-time string")

是合法的,而且

char buffer[200]="a string";
MY_MACRO(buffer)

是合法的,但不允许

MY_MACRO(szArbitraryDynamicString)

我使用了 GCC 的 __builtin_types_compatible_p 和 MSVC 的 _countof,它们似乎以拒绝短字符串文字为代价正常工作。

因为 C++ 中的字符串文字可以有不同的前缀,所以没有必要检查开头的引号: https ://en.cppreference.com/w/cpp/language/string_literal

最好检查结束报价:

  • C++11
  • msvc2015u3,gcc5.4,clang3.8.0

     #include <type_traits> #define UTILITY_CONST_EXPR_VALUE(exp) ::utility::const_expr_value<decltype(exp), exp>::value // hint: operator* applies to character literals, but not to double-quoted literals #define UTILITY_LITERAL_CHAR_(c_str, char_type) UTILITY_CONST_EXPR_VALUE(((void)(c_str * 0), ::utility::literal_char_caster<typename ::utility::remove_cvref<char_type>::type>::cast_from(c_str, L ## c_str, u ## c_str, U ## c_str))) #define UTILITY_LITERAL_CHAR(c_str, char_type) UTILITY_LITERAL_CHAR_(c_str, char_type) #define UTILITY_IS_LITERAL_STRING(c_str) UTILITY_CONST_EXPR_VALUE((sizeof(#c_str) > 1) ? #c_str [sizeof(#c_str) - 2] == UTILITY_LITERAL_CHAR('\"', decltype(c_str[0])) : false) #define UTILITY_IS_LITERAL_STRING_A(c_str) UTILITY_CONST_EXPR_VALUE((sizeof(#c_str) > 1) ? #c_str [sizeof(#c_str) - 2] == '\"' : false) #define UTILITY_IS_LITERAL_STRING_WITH_PREFIX(c_str, prefix) UTILITY_CONST_EXPR_VALUE((sizeof(#c_str) > 1) ? #c_str [sizeof(#c_str) - 2] == prefix ## '\"' : false) namespace utility { template <typename T, T v> struct const_expr_value { static constexpr const T value = v; }; // remove_reference + remove_cv template <typename T> struct remove_cvref { using type = typename std::remove_cv<typename std::remove_reference<T>::type>::type; }; //// literal_char_caster, literal_string_caster // template class to replace partial function specialization and avoid overload over different return types template <typename CharT> struct literal_char_caster; template <> struct literal_char_caster<char> { static inline constexpr char cast_from( char ach, wchar_t wch, char16_t char16ch, char32_t char32ch) { return ach; } }; template <> struct literal_char_caster<wchar_t> { static inline constexpr wchar_t cast_from( char ach, wchar_t wch, char16_t char16ch, char32_t char32ch) { return wch; } }; template <> struct literal_char_caster<char16_t> { static inline constexpr char16_t cast_from( char ach, wchar_t wch, char16_t char16ch, char32_t char32ch) { return char16ch; } }; template <> struct literal_char_caster<char32_t> { static inline constexpr char32_t cast_from( char ach, wchar_t wch, char16_t char16ch, char32_t char32ch) { return char32ch; } }; } const char * a = "123"; const char b[] = "345"; int main() { static_assert(UTILITY_IS_LITERAL_STRING_A(a) == 0, "Aa"); static_assert(UTILITY_IS_LITERAL_STRING(a) == 0, "a"); static_assert(UTILITY_IS_LITERAL_STRING_A(b) == 0, "Ab"); static_assert(UTILITY_IS_LITERAL_STRING(b) == 0, "b"); static_assert(UTILITY_IS_LITERAL_STRING_A("123") == 1, "A123"); static_assert(UTILITY_IS_LITERAL_STRING_WITH_PREFIX(L"123", L) == 1, "L123"); static_assert(UTILITY_IS_LITERAL_STRING_WITH_PREFIX(u"123", u) == 1, "u123"); static_assert(UTILITY_IS_LITERAL_STRING_WITH_PREFIX(U"123", U) == 1, "U123"); static_assert(UTILITY_IS_LITERAL_STRING("123") == 1, "123"); static_assert(UTILITY_IS_LITERAL_STRING(L"123") == 1, "L123"); static_assert(UTILITY_IS_LITERAL_STRING(u"123") == 1, "u123"); static_assert(UTILITY_IS_LITERAL_STRING(U"123") == 1, "U123"); }

https://godbolt.org/z/UXIRY6

需要UTILITY_CONST_EXPR_VALUE宏来强制编译器生成仅编译时的代码。

这个回复可能有点晚了,但希望对其他人有所帮助,我的解决方案如下:

#define _s(x) #x
#define IS_LITERAL(expr) (_s(expr)[0] == '"')

此解决方案的优点是可以在编译时评估此代码,这将允许在其他地方进行有用的优化。 此宏也可以使用任何编译器编译,此处不使用编译器特定的功能。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM