Могу ли я определить, является ли аргумент строковым литералом?
Можно ли определить, является ли аргумент, переданный в макро или функции, строковым литералом во время компиляции или времени выполнения?
Например,
#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;
Спасибо.
Ответы
Ответ 1
ДА! (Спасибо Джеймс Макнеллис и 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
. Но есть все основания, чтобы хотеть что-то сделать. Когда-нибудь вы тоже почувствуете необходимость прибегать к ужасному взлому.
Ответ 2
Знание во время компиляции (как упоминалось в вопросе), со следующей методикой. Вы можете определить, является ли данный аргумент строковым литералом или нет. Если это какой-то массив или указатель вроде 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
[Примечание: мой предыдущий ответ был отклонен экспертами, и он еще не принят или проголосовал после внесенных изменений. Я помещаю другой ответ с тем же контентом.]
Ответ 3
Нет. Строковый литерал - это всего лишь массив char
(в C) или const char
(в С++).
Вы не можете различать строковый литерал и другой массив char
, как этот (в С++):
const char x[] = "Hello, World!";
Ответ 4
Попробуйте следующее:
#define is_string_literal(s) \
(memcmp(#s, "\"", 1) == 0)
В соответствии с соглашением об именовании переменных C/С++ имя переменной должно начинаться с "_" или алфавита.
Ответ 5
У меня был аналогичный вопрос: я хотел сказать, что
MY_MACRO("compile-time string")
был законным и что
char buffer[200]="a string";
MY_MACRO(buffer)
является законным, но не позволяет
MY_MACRO(szArbitraryDynamicString)
Я использовал GCC __builtin_types_compatible_p и MSVC _countof, которые, похоже, работают правильно за счет отказа от коротких строковых литералов.
Ответ 6
Поскольку строковый литерал в 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
требуется, чтобы заставить компилятор генерировать код только во время компиляции.