[英]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 McNellis和GMan的更正。更新以正确处理连接的文字,如"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
是的; 在 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()));
}
试试这个:
#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
最好检查结束报价:
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"); }
需要UTILITY_CONST_EXPR_VALUE
宏来强制编译器生成仅编译时的代码。
这个回复可能有点晚了,但希望对其他人有所帮助,我的解决方案如下:
#define _s(x) #x
#define IS_LITERAL(expr) (_s(expr)[0] == '"')
此解决方案的优点是可以在编译时评估此代码,这将允许在其他地方进行有用的优化。 此宏也可以使用任何编译器编译,此处不使用编译器特定的功能。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.