Подтвердить что ты не робот

Обеспечьте сильную проверку типов в C (строгость строгости для typedefs)

Есть ли способ принудительного применения явного приведения для typedef одного типа? Я должен иметь дело с utf8, и иногда меня путают с индексами для количества символов и количества байтов. Поэтому было бы неплохо иметь некоторые typedefs:

typedef unsigned int char_idx_t;
typedef unsigned int byte_idx_t;

С добавлением, что вам нужно явное литье между ними:

char_idx_t a = 0;
byte_idx_t b;

b = a; // compile warning
b = (byte_idx_t) a; // ok

Я знаю, что такая функция не существует в C, но, возможно, вы знаете трюк или расширение компилятора (предпочтительный gcc), который делает это.


ИЗМЕНИТЬ Мне все еще не очень нравится венгерская нотация в целом. Я не мог использовать его для этой проблемы из-за соглашений о кодировании проекта, но теперь я использовал его в другом подобном случае, где также типы одинаковы, а значения очень похожи. И я должен признать: это помогает. Я никогда не стал бы объявлять каждое целое число со стартовым "i", но, как в примере Joel для перекрывающихся типов, это может быть спасение жизни.

4b9b3361

Ответ 1

Вы можете сделать что-то вроде:

typedef struct {
    unsigned int c_idx;
} char_idx;

typedef struct {
    unsigned int b_idx;
} byte_idx;

Затем вы увидите, когда будете использовать каждый из них:

char_idx a;
byte_idx b;

b.b_idx = a.c_idx;  

Теперь более понятно, что они разные, но все еще компилируются.

Ответ 2

Для типов "handle" (непрозрачные указатели) Microsoft использует трюк объявления структур, а затем typedef'ing указатель на структуру:

#define DECLARE_HANDLE(name) struct name##__ { int unused; }; \
                             typedef struct name##__ *name

Тогда вместо

typedef void* FOOHANDLE;
typedef void* BARHANDLE;

Они делают:

DECLARE_HANDLE(FOOHANDLE);
DECLARE_HANDLE(BARHANDLE);

Итак, теперь это работает:

FOOHANDLE make_foo();
BARHANDLE make_bar();
void do_bar(BARHANDLE);

FOOHANDLE foo = make_foo();  /* ok */
BARHANDLE bar = foo;         /* won't work! */
do_bar(foo);                 /* won't work! */   

Ответ 3

То, что вы хотите, называется "сильный typedef" или "strict typedef".

Некоторые языки программирования [Rust, D, Haskell, Ada,...] дают некоторую поддержку этому на уровне языка, C [++] этого не делает. Было предложено включить его в язык с названием "непрозрачный typedef", но не был принят.

Отсутствие поддержки языка действительно не проблема. Просто оберните тип, который будет сглажен, в новый класс, имеющий ровно 1 элемент данных типа T. Большая часть повторения может быть учтена шаблонами и макросами. Этот простой метод так же удобен, как и на языках программирования с прямой поддержкой.

Ответ 4

Используйте lint. См. Splint: Types и проверка сильного типа.

Сильная проверка типов часто показывает ошибки программирования. Шина может проверять примитивные типы С более строго и гибко, чем типичные компиляторы (4.1) и поддерживает булевский тип (4.2). Кроме того, пользователи могут определить абстрактные типы, которые обеспечивают скрытие информации (0).

Ответ 5

В C единственное различие между определяемыми пользователем типами , выполняемое компилятором, - это различие между структурами. Любой typedef, включающий различные структуры, будет работать. Ваш главный вопрос дизайна - , если разные типы структур используют одни и те же имена участников? Если это так, вы можете имитировать некоторый полиморфный код с использованием макросов и других трюков. Если нет, вы действительно привержены двум различным представлениям. Например, хотите ли вы иметь возможность

#define INCREMENT(s, k) ((s).n += (k))

и используйте INCREMENT как для byte_idx, так и для char_idx? Затем назовите поля одинаково.

Ответ 6

Вы спрашивали о расширениях. Jeff Foster CQual очень приятный, и я думаю, что он мог бы выполнить нужную работу.

Ответ 7

Если вы писали С++, вы могли бы создать два одинаково определенных класса с разными именами, которые были обертками вокруг unsigned int. Я не знаю трюк, чтобы делать то, что вы хотите в C.

Ответ 8

Используйте сильное typedef, как определено в BOOST_STRONG_TYPEDEF

Ответ 9

С С++ 11 вы можете использовать класс enum, например

enum class char_idx_t : unsigned int {};
enum class byte_idx_t : unsigned int {};

Компилятор будет принудительно применять явное литье между двумя типами; это похоже на тонкий класс обертки. К сожалению, у вас не будет перегрузки оператора, например. если вы хотите добавить два char_idx_t вместе, вам нужно будет отнести их к unsigned int.