Обеспечить строгую проверку типов в C (строгость типов для typedefs) - PullRequest
35 голосов
/ 18 декабря 2008

Есть ли способ применить явное приведение для typedefs того же типа? Мне приходится иметь дело с 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), которое делает это.


EDIT Я до сих пор не очень люблю венгерские обозначения в целом. Я не мог использовать это для этой проблемы из-за соглашений о кодировании проекта, но я использовал это теперь в другом подобном случае, где также типы одинаковы, и значения очень похожи. И я должен признать: это помогает. Я бы никогда не объявил каждое целое число начальным «i», но, как в примере Джоэля для перекрывающихся типов, это может спасти жизнь.

Ответы [ 9 ]

19 голосов
/ 18 декабря 2008

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

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;  

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

18 голосов
/ 18 декабря 2008

Для «дескрипторных» типов (непрозрачных указателей) Microsoft использует способ объявления структур, а затем вводит определение указателя на структуру:

#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! */   
14 голосов
/ 23 октября 2009

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

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

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

7 голосов
/ 18 декабря 2008

Используйте пух. См. Шина: Типы и Строгая проверка типа .

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

5 голосов
/ 18 декабря 2008

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

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

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

3 голосов
/ 26 февраля 2009

Вы спрашивали о расширениях. CQual Джеффа Фостера очень хороша, и я думаю, что он может сделать то, что вы хотите.

2 голосов
/ 24 октября 2013

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

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

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

2 голосов
/ 02 мая 2010

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

2 голосов
/ 18 декабря 2008

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

...