LINUX.ORG.RU

Макрос TYPENAME не отличает wchar_t от int

 , , ,


0

3

Всех приветствую!

Используемый язык - C.

wchar_t* w;
int*     q;
printf("%s\n", TYPENAME(w));
printf("%s\n", TYPENAME(q));

вывод

wchar_t*
wchar_t*

Сам макрос

#define TYPENAME(x)                           \
  _Generic( (x),                              \
    size_t           : "size_t",              \
    size_t*          : "size_t*",             \
    wchar_t          : "wchar_t",             \
    wchar_t*         : "wchar_t*",            \
    default          : TYPENAME1(x)           \
  )
    
#define TYPENAME1(x)                          \
  _Generic( (x),                              \
    _Bool             : "_Bool",              \
    char              : "char",               \
    signed char       : "signed char",        \
    unsigned char     : "unsigned char",      \
    short             : "short",              \
    short*            : "short*",             \
    int               : "int",                \
    int*              : "int*",               \
    long              : "long",               \
    long*             : "long*",              \
    long long         : "long long",          \
    unsigned short    : "unsigned short",     \
    unsigned int      : "unsigned int",       \
    unsigned int*     : "unsigned int*",      \
    unsigned long     : "unsigned long",      \
    unsigned long long: "unsigned long long", \
    float             : "float",              \
    double            : "double",             \
    long double       : "long double",        \
    char*             : "char*",              \
    char const*       : "char const*",        \
    void*             : "void*",              \
    void const*       : "void const*",        \
    default           : "unknown"             \
  )

Вопрос :как подправить макрос чтобы от отличал указанные выше типы?

Или это принципиально неразрешимая задача в языке C?

Благодарю заранее за ответы по существу.


Попробуйте вот так

#define TYPENAME(x)                           \
  _Generic( (x),                              \
    wchar_t          : "wchar_t",             \
    wchar_t*         : "wchar_t*",            \
    size_t           : "size_t",              \
    size_t*          : "size_t*",             \
    default          : TYPENAME1(x)           \
  )

Какой результат?

anonymous
()

Не знаю как решается эта задача, но совместимые типы можно различать, нужно заменить _Generic на цепочку вложенных __builtin_choose_expr, различать типы можно с помощью аттрибутов: https://godbolt.org/z/hhTWfP1Ya

visibility не советую использовать, нужно подобрать аттрибут без эффектов.

Или это принципиально неразрешимая задача в языке C?

Если без расширений то да. Проверка типов по совместимости еще приводит к проблемам с enum, он просто работает как int.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 3)
Ответ на: комментарий от Gyros

Аттрибуты нужны что бы различать типы, вместо типов аттрибуты, и switch по ним, такая идея. Это кроссплатформенно (главное использовать gcc) и работает на c89. Но конечно огромный костыль, как я говорил в предыдущих тредах, лучше по возможности не отклоняться от типичного кода на С.

MOPKOBKA ★★★★★
()
Ответ на: комментарий от MOPKOBKA

И как быть?

Мне это нужно для своих generic-контейнеров.

У каждого такого контейнера есть ф-ция вывода (исп. чаще всего для отладки), в которой

fprintf(output, "List of <%s>: length=%zu \n", TYPENAME(list->head->item), list->length);\
fprintf(output, "------------------------------------------------------------\n");\
fprintf(output, "|       index        |               value                 |\n");\
fprintf(output, "------------------------------------------------------------\n");\

Все норм работало пока не наткнулся на этот вышеописанный случай.

Забить?

Gyros
() автор топика
Последнее исправление: Gyros (всего исправлений: 1)
Ответ на: комментарий от Gyros

Можно в функцию передавать аргументом enum container_element_type, или хранить в контейнере. Лучше всего посмотреть как сделано в проекте который тебе нравится (linux? glib?) и повторить.

Можно еще сделать тип variant_t, который enum+union и может хранить в себе любой базовый элемент. И для контейнеров сделать итератор который возвращает такой variant_t, соответственно функция печати будет всего одна, ей главное уметь распечатать этот variant_t продясь с помощью итератора по любой коллекции.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 4)
Ответ на: комментарий от zurg

Кого-то из них завернуть в struct, скорее wchar*, заодно и строковый тип будет. А по существу - хватит уже выкапывать эту стюардессу, ну не умеет сишка в типы, и врядли когда научится

Rust тоже пора, там такая же проблема:

type my_int = i32;
type my_int2 = i32;

trait MyTypeName {
    fn print_my_name(self);
}

impl MyTypeName for my_int {
    fn print_my_name(self) {
        println!("my_int");
    }
}

impl MyTypeName for my_int2 {
    fn print_my_name(self) {
        println!("my_int2");
    }
}

MOPKOBKA ★★★★★
()
Ответ на: комментарий от MOPKOBKA

Скорее всего забью.

Неохота что-то менять из-за одного частного случая, который вряд ли у меня будет.

Контейнер <wchar_t*> - еще нормально. Но контейнер <int*> вряд ли будет нужен.

Gyros
() автор топика
Ответ на: комментарий от Gyros

В Erlang нету строкового типа, перед выводом print(int[]) просто проверяется, все ли числа из int[] входят в диапазон букв, если да то int[] печатается как строка, если нет то как массив чисел. В С можно запустить iswprint, если хоть один элемент не печатный, то можно вывести данные как массив чисел.

Еще можно напечатать два представления, строковое и в виде массива.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 5)
Ответ на: комментарий от Gyros

Ну это от локали зависит, iswprint как раз проверяет печатный ли wchar_t. Если ты хочешь свою функцию, учти все пробелы, все числа, все знаки на английской и русской раскладке (#/№), и a-zA-ZА-ЯЁа-яё.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 2)
Ответ на: комментарий от Gyros

Так ведь можно, вместо «int*» подставь func_detect_int_or_wchar(x). Ах да, размер то не узнать без расширений.

Кстати, если сложные структуры печатаешь, то можно печатать их в простом формате .dot, и потом запускать graphviz для получения изображения, так делают некоторые проекты включая gcc:

https://github.com/gcc-mirror/gcc/blob/master/gcc/graphviz.cc

https://user-images.githubusercontent.com/22757058/113173490-17405e00-9252-11...

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 4)
Ответ на: комментарий от MOPKOBKA

у меня проверяется

TYPENAME(list->head->item)

т.е. первый элемент списка.

Если <wchar_t*>, то там м.б. «123Вяся»; если <int*> - 123

Со строкой понятно, я должен бежать до ‘\0’ по каждому символу и проверять iswprint.

А с int что делать? Рассм. как как послед-ть wint_t?

Gyros
() автор топика
Последнее исправление: Gyros (всего исправлений: 2)
Ответ на: комментарий от Gyros
void print_int_array(const int *s, int n)
{
    int is_string, i;

    // Определить строка ли это    
    for (is_string = 1, i = 0; i < n; ++i) {
        if (!iswprint(s[i])) {
            is_string = 0;
            break;
        }
    }
    
    // Если строка то напечатать как строку, если нет то каждый элемент как число
    if (is_string) {
        wprintf("%.*ls", n, (const wchar_t*)s);
    } else {
        for (i = 0; i < n; ++i) {
            printf("%d ", s[i]);
        }
    }
}

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от Gyros

А как распечатывать массив int* если размер неизвестен? Просто указатели что ли? Без знания длинны функция смысла не имеет.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от MOPKOBKA

Элемент контейнера <int*>

int* integer = malloc(sizeof(int));
*integer = 123;
...
listPINT_push(lpint, integer);

Элемент контейнера <wchar_t*>

listPWCHAR_push(lpwch, wstr_dup(L"123Вася"));

И вот по этим item-ам я должен определить что у меня List_of<int*> или List_of<wchar_t*>

Gyros
() автор топика
Последнее исправление: Gyros (всего исправлений: 2)
Ответ на: комментарий от Gyros

Нужно размер массива указывать, по другому никак. Если делать свой malloc где можно будет узнать размер блока, то это сломается на массивах внутри структур.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от MOPKOBKA

Размер массива чего?

Вы надеюсь, не размер контейнера имеете в виду, наверное.

У строки L"123Вася" можем узнать размер (wcslen).

А у числа (как массива wint_t) 123 не можем?

Я правильно понимаю.

Gyros
() автор топика
Последнее исправление: Gyros (всего исправлений: 3)
Ответ на: комментарий от Gyros

Если у тебя возможен List<int*> то предложенная мной функция взятая из Erlang работать не будет, потому что размер массива int* невозможно определить, а значит нельзя проверить, является ли он строкой.

MOPKOBKA ★★★★★
()
Ответ на: комментарий от MOPKOBKA

как распечатывать массив int* если размер неизвестен? Просто указатели что ли? Без знания длинны функция смысла не имеет.

Экспромтом.

Разработчики компиляторов могли бы помочь программистам в этом.

Как?

Перед скажем vector добавлять метаданные о объекте к которым программа могла использовать для алгоритмов.

Вообщем-то это довольно стандартный приём.
Вспомним как устроена архитектура CString, указатели на адреса памяти, …

anonymous
()
Ответ на: комментарий от vazgen05

Так ли оно нужно в низкоуровневом языке? Программист и сам какой хочет может построить уровень абстракции над сырыми адресами.

Всё зависит от потребностей алгоритмов.

anonymous
()
Ответ на: комментарий от anonymous

Гм., а мыслишка неплохая.
Надеюсь когда нибудь её реализую.
Это будет не отладочная информация о функциях, а неки урезанные мета данные, которые смогут использовать алгоритмы.
Для этого и разработчиков компиляторов беспокоить не придётся.

anonymous
()
Ответ на: комментарий от anonymous

Это алиасы типов и на уровне языка неразличимы.

Подброшу ка я дровишек: а ещё есть long который на самом деле distinct type и участвует в overloads resolution (в плюсах очевидно), но при этом для всех практических целей (sizeof() и всё остальное) такой же как int в 32 битах и long long в 64, а ещё есть новомодные изобретения типа std::byte… Жить с каждым днём становится всё веселей и веселей :)

bugfixer ★★★★★
()
Ответ на: комментарий от MOPKOBKA

Rust тоже пора, там такая же проблема:

type не создает новый тип это просто псевдоним, то есть твой код это тупо:

trait MyTypeName {
    fn print_my_name(self);
}

impl MyTypeName for i32 {
    fn print_my_name(self) {
        println!("my_int");
    }
}

impl MyTypeName for i32 {
    fn print_my_name(self) {
        println!("my_int2");
    }
}
anonymous
()
Ответ на: комментарий от MOPKOBKA

Тут ты по своей воле сел в лужу, а у ТСа безысходность, за него авторы сишечки решили что символы не достойны иметь свой тип и наалиасили как попало. В расте char и соответствующий ему u32 - разные типы, как и в большинстве нормально типизированных языков, и при чём тут сразу раст. Типы в сишке мало того что убоги, но и то что есть просто сломано нахрен, так что и в плюсах икается, которые унаследовали эту срань.

zurg
()