LINUX.ORG.RU

Стандартная библиотека C++: слишком много типов


0

0

В стандартной библиотеке С++ массово используются typedef-ы. Буквально везде. Речь идёт о всяких там streampos, streamoff, streamsize, size_type и т.п.

Лично я просто впадаю в ступор от такого обилия потенциально несовместимых типов. И чего с этим делать, непонятно. Либо получается куча приведений типов, либо приходится использовать "правильные" типы везде (что малореально, тупо, отнимает кучу времени и рушит модульность).

anonymous

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

Какое отношение имеет ООП к системе типов С++?

Если бы компилятор C++ считал typedef объявлением нового типа с запретом неявного приведения (возможно, опциональным запретом), вопрос был бы частично снят. То что сейчас наблюдается, тихий ужас. Вся тяжесть контроля типов возложена на плечи программиста.

Разработчики стандартной библиотеки вроде бы должны были это понимать, когда плодили миллион typedef-ов. Возможно, они руководствовались какими-то высшими побуждениями. Мне похрен, поскольку на практике получилось крайне неудобно.

anonymous
()

Всё правильно сделано. Чтобы программисты не путали х... с пальцем, образно говоря. Работа ведётся с разными "сущностями"

Грубо, чтобы поняли:

int rubles = 100;
int kilo = 16;
int sum = rubles + kilo;

с точки зрения логики ужас, но приемлимо для компилятора. Используя typedef вы получите предупреждения от компилятора.

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

>Используя typedef вы получите предупреждения от компилятора.

Не будет никаких предупреждений, в чём и заключается подстава. Вернее, предупреждения будут только в том случае, если они были бы и без typedef.

Соответственно, утверждение

>Всё правильно сделано. Чтобы программисты не путали х... с пальцем, образно говоря.

- неверно, т.к. контроль за х.. и пальцем целиком на плечах программистов. Об этом выше в теме писал, кстати.

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

> Если бы компилятор C++ считал typedef объявлением нового типа с запретом неявного приведения (возможно, опциональным запретом), вопрос был бы частично снят. То что сейчас наблюдается, тихий ужас. Вся тяжесть контроля типов возложена на плечи программиста.

а вы рассматривайте объявление типа как алиас, не более. в конечном итоге стало бы легче от того, если бы вместо typedef был бы явно указан, скажем, простой тип a'la unsigned long? ну был бы и что с того? наличие или отсутствие псевдонима не отменяет сам факт необходимости преобразования и если типы не совпадают - оно будет. независимо от того, пользуетесь вы typedef или нет.

> Разработчики стандартной библиотеки вроде бы должны были это понимать, когда плодили миллион typedef-ов. Возможно, они руководствовались какими-то высшими побуждениями. Мне похрен, поскольку на практике получилось крайне неудобно.

в зависимости от платформы, к примеру, size_t может быть unsigned int а может и unsigned long int. попробуйте объявить ::memset() или std::string::size() однообразно для различных платформ не используя typedef скажем в простых типах без потери значащих битов и в то же время рационально по ресурсам.

лично мне тот-же size_t & K доставлял изрядный геморой лишь в случае жесткой привязки типов в коде. printf() и последователи тому характерный пример. это действительно жопа, причем не зависимо от того, C это или C++. в последнем с этим, в отличие от C, в силу наличия перегрузки операций все решается существенно проще aka можно пойти по пути streams, можно по пути boost::format и так далее.

// wbr

klalafuda ★☆☆
()

Ну и пишите гуи и прочие обращения к БД на языках с динамической типизацией, а если надо что-то быстро считать, то делайте библиотеку на C и обвязку к ней

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

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

>You can use typedef declarations to construct shorter or more meaningful names for types already defined by the language or for types that you have declared. Typedef names allow you to encapsulate implementation details that may change.

>In contrast to the class, struct, union, and enum declarations, typedef declarations do not introduce new types — they introduce new names for existing types.

Использование typedef обоснованно, когда получаются слишком монструозные конструкции и в случаях, когда один тип в дальнейшем может быть подменён другим прозрачным способом.

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

>а вы рассматривайте объявление типа как алиас

Всё бы хорошо, да только стандарте языке не оговаривается, алиасом каких именно типов являются вышеупомянутые streampos, streamoff, streamsize, size_type и т.п. Зависит от реализации. Воспринимать эти typedefs как алиасы для кого-то конкретного типа == писать непереносимый код.

>в конечном итоге стало бы легче от того, если бы вместо typedef был бы явно указан, скажем, простой тип a'la unsigned long?

Да, стало бы. Если было бы что-то типа typedef unsigned long ulong и использовался этот псевдоним -- тоже стало бы. Мой наезд исключительно на миллион typedef-ов, зависящих от реализации. Надеюсь, мысль ясна.

>в зависимости от платформы, к примеру, size_t может быть unsigned int а может и unsigned long int.

size_t -- тот редкий случай, когда это действительно нужно. Во всяком случае, к существованию size_t у меня предъяв нету :) Однако, в стандартной библиотеке С++ довели этот подход до абсурда: чуть ли не каждый класс объявляет свои implementation-defined typedef-ы.

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

> size_t -- тот редкий случай, когда это действительно нужно. Во всяком случае, к существованию size_t у меня предъяв нету :)

ssize_t? off_t? gid_t? uid_t? и так далее и тому подобное. это механизм абстракции, который позволяет разработчику интерфейса заявить, что мол pid - это целочисленное значение, которое определяет уникальный идентификатор процесса. главное, это - число. а вот его разрядность на конкретной платформе или архитектуре может меняться.

> Однако, в стандартной библиотеке С++ довели этот подход до абсурда: чуть ли не каждый класс объявляет свои implementation-defined typedef-ы.

ну грануляция типов до уровня определения foo::size_type в каждом foo - это лишь подход. в конце-концов, почему нет? учитывайте, что интерфейс того же STL проектировался с расчетом максимальной гибкости. может быть STLport захотел бы сделать list::size_type как unsigned int а для string::size_type как size_t (первый - это обычный счетчик а второй уже может зависеть от платформы). если бы был скажем глобальный std::size_type, так сделать бы не получилось.

// wbr

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

> Всё бы хорошо, да только стандарте языке не оговаривается, алиасом каких именно типов являются вышеупомянутые streampos, streamoff, streamsize, size_type и т.п. Зависит от реализации. Воспринимать эти typedefs как алиасы для кого-то конкретного типа == писать непереносимый код.

ну так не воспринимайте их как алиас какого-то конкретного типа и пишите переносимый код, какие проблемы то :)

// wbr

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

>ну так не воспринимайте их как алиас какого-то конкретного типа и пишите переносимый код, какие проблемы то :)

Э... как бы, это есть предмет недовольства :) Библиотека существует не в сферическом вакууме. Если библиотека провоцирует слишком частое использование приведения типов, или заставляет тащить её внутренние типы в мой интерфейс, это хреновая библиотека.

В принципе, даже это было бы простительно, если бы компилятор ругался на неявные приведения типов. Т.е. считал бы typеdef объявлением нового типа с запретом неявного приведения (опционально). Об этом уже говорил выше.

Этого в С++ нет, и единственной гарантией портируемости кода может быть внимательность программиста. Присвоил unsigned int size_t, на x86-32 всё ОК, на x86-64 прога глюкнула. Поменял реализацию STL -- словил кучу глюков. И т.п.

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

> Э... как бы, это есть предмет недовольства :) Библиотека существует не в сферическом вакууме. Если библиотека провоцирует слишком частое использование приведения типов, или заставляет тащить её внутренние типы в мой интерфейс, это хреновая библиотека.

ну во-первых, сама по себе библиотека - интерфейс, стандарт на STL - к преобразованию типов не призывает и это уже удел конкретной реализации. стандарт лишь дает возможность объявить size_type разными путями скажем для std::list и std::map в целях гибкости, но не более того. как следствие, будет ли преобразование типов или нет [скорее всего нет] лучше спрашивать не стандарт а конкретного производителя STL.

> В принципе, даже это было бы простительно, если бы компилятор ругался на неявные приведения типов. Т.е. считал бы typеdef объявлением нового типа с запретом неявного приведения (опционально). Об этом уже говорил выше.

это был бы явный overkill и такой библиотекой было действительно весьма муторно пользоваться. typedef unsigned int a, typedef unsigned int b, a a, b b, a = b -> type cast error если нет явного оператора преобразования b в a, а таких типов может быть множество и причем не только и не столько в самой STL - это была бы действительно жопа и праздник копипаста.

> Этого в С++ нет, и единственной гарантией портируемости кода может быть внимательность программиста.

компилятор все-таки неплохо помогает в этом деле. к примеру, умный компилятор ловит операции над signed/unsigned integer и выдает предупреждение [см. пост выше]. если же тип комплексный, то тут все и так понятно - нужен явный каст.

> Присвоил unsigned int size_t, на x86-32 всё ОК, на x86-64 прога глюкнула. Поменял реализацию STL -- словил кучу глюков. И т.п.

ну это общее свойство и C и C++ бо и там и там в определенной ситуации можно потерять значимые биты. вывод: не присваивайте size_t в unsigned int.

// wbr

klalafuda ★☆☆
()

У меня сложилось впечатление, что автор темы что-то недоосилил..

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

>Частые приведения типов - признак кривого дизайна/рук

+1. Всегда подозревал, что стандартная библиотека плюсов криво спроектирована криворукими программистами :)

Задача:

Спроектировать интерфейс для работы с двоичным файлом (только чтение, произвольный доступ), реализовать этот интерфейс при помощи std::istream.

Функции интерфейса: 1. Чтение блока данных (read) 2. Позиционирование указателя чтения (seek) 3. Получение указателя чтения (peek)

Интерфейс должен быть максимально изолированным от реализации. Вперед, с пестней, мой некриворукий друк, будем считать приведения типов (в т.ч. неявные).

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

#include <cstddef>

class BinaryFile {
        virtual void read(char* buffer, std::size_t size) = 0;

        virtual void setPosition(std::size_t position) = 0;

        virtual std::size_t getPosition() = 0;
};

#include <fstream>

class BinaryFileImpl : public BinaryFile {
public:

        BinaryFileImpl(const char* fileName) : file(fileName, std::ios_base::in | std::ios_base::binary) { }

        virtual void read(char* buffer, std::size_t size) {
                file.read(buffer, size);
        }

        virtual void setPosition(std::size_t position) {
                file.seekg(position);
        }

        virtual std::size_t getPosition() {
                return file.tellg();
        }

private:
        std::ifstream file;
};

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

class BinaryFile {
        virtual void read(char* buffer, std::size_t size) = 0;
        virtual void setPosition(std::size_t position) = 0;
        virtual std::size_t getPosition() = 0;
};

ОК, к интерфейсу претензий нет. Ну может только тип позиции
не очень логично делать size_t, скорее long или unsigned long.

Теперь считаем приведения типов в реализации:

1. file.read(buffer, size);
Неявное приведение типа size_t к std::streamsize.

2. file.seekg(position);
Неявное приведение типа size_t к std::streamoff.

3. return file.tellg();
Неявное приведение типа std::streampos к size_t.

Все эти приведения типов потенциально некорректны.
Нет никаких гарантий, что streamsize, streamoff и streampos
могут быть без проблем приведены к size_t, и наоборот.

Ы?

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

>Не ищите проблем там где их нет.

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

anonymous
()

К сожалению разработкичи всего, что доступно в std::, не сочли нужным вас спросить, как им лучше сделать. Иначе бы вы их определенно проконсультировали бы, как лучше стоит реализовать стандартные библиотеки.

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

Когда подрастешь немного, поймешь, что идеализировать "Разработчиков Стандартной Библиотеки" наивно и глупо. А мой интерес чисто практический -- что со всем этим делать.

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

> мой интерес чисто практический -- что со всем этим делать.

Соблюдать "гигиену" разработки.

1. Избавиться от warning'ов (в нашем проектике на 50-100 kSLOC ни одного warning'a), собирать с -pedantic -Wall -Wextra.

2. Положиться на компилятор. Там, где типы совместимы, незачем забивать себе голову явными приведениями, там где потенциально несовместимы (несовместимость signed/unsigned, потеря разрядности и т.п.), будет warning (при соблюдении п.1 они не потеряются среди сотен), и в каждом таком случае надо уже аккуратно разбираться -- возможно, одним явным приведением тут не отделаться. Например, присваивание беззнакового целого знаковому означает, что беззнаковое может превысить максимальное значение знакового. Возможно, в каком-то случае стоит кинуть exception, а не преобразовать число, превышающее диапазон, в отрицательное значение.

3. Портирование на другую платформу/компилятор -- это всегда особая задача. Можно лишь некоторым образом уменьшить объем работы, необходимый для портирования. autotools и им подобные не зря придумали, а именно потому, что в языке нет возможностей писать абсолютно портируемый код Точнее, в C++ они, конечно, есть, до некоторой степени, но это настолько геморройно, что проще по старинке -- препроцессором.

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

>там где потенциально несовместимы (несовместимость signed/unsigned,
потеря разрядности и т.п.), будет warning

Если бы. Мне так и не удалось добиться ни одного warning-а
при компиляции этой программы на 32-битной платформе x86:

-----8<------------------------

#include <stdio.h>

unsigned ulong2u(unsigned long v) { return v; }

void print_uint(unsigned v) { printf("%u\n", v); }

int main(void) {
    volatile unsigned long v = 1UL;
    printf("%u\n", ulong2u(v));
    print_uint(v);
    return 0;
}

----->8------------------------

(проверялось с MSVC7, MSVC8, gcc 3.2.3)

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

Чисто для контраста:

public class Test {
    public static void main(String [] args) {
        long x = 1;
        int y = x;
    }
}

Test.java:4: possible loss of precision
found   : long
required: int
        int y = x;
                ^
1 error

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

А какой вам warning тут нужен? На 32bit x86 sizeof(unsigned) == sizeof(unsigned int) == sizeof(unsigned long), поэтому все правильно, никаких warning'ов не надо.

При портировании на архитектуру, где это не выполняется, warning'и появятся.

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

>поэтому все правильно, никаких warning'ов не надо.

У меня категорически противоположное мнение на этот счёт. Отсутствие предупреждений в данном случае вылетает в жесткий геморой при портировании на другую платформу, которого можно было элементарно, подчеркиваю, элементарно избежать.

Не вижу никакой логики в том, чтобы считать типы с потенциально разной размерностью идентичными.

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

> Когда подрастешь немного, поймешь, что идеализировать "Разработчиков Стандартной Библиотеки" наивно и глупо.

Как подрастешь, поймешь, что причин выбора того или иного решения может быть очень много, и не надо сразу кричать "фугавно", смотрите сами не обделайтесь.

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

Задолбали идиоты, которым нечего сказать по делу. У разработчиков COBOL и GW BASIC тоже была какая-то мотивация их решений. Что с того?

Давно хочу понять мотивацию разработчиков STL, или, ещё лучше, почитать внятную дискуссию на данную тему (почему сделано так, и как предполагалось с этим работать).

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

> Не вижу никакой логики в том, чтобы считать типы с потенциально разной размерностью идентичными.

Это для ЯВУ. C++ -- это ассемблер с шаблонами.

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

> Задолбали идиоты, которым нечего сказать по делу.

А что с вами говорить по делу, если вы ведете себя как малолетка, сразу грязью STL поливаете.

fmj
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.