1. C++ / Говнокод #11425

    +20

    1. 1
    2. 2
    3. 3
    4. 4
    5. 5
    6. 6
    7. 7
    8. 8
    9. 9
    void File::open(string file, bool readAccess, bool writeAccess, bool append)
    {
    	Close();
    	(string&)Name=file;
    	const char* const modes[2][2][2]={{{null, "rb"}, {"wb", "w+b"}}, {{null, null}, {"ab", "a+b"}}};
    	if(modes[append][writeAccess][readAccess]==null) return InternalError("Создание файла с неверными параметрами!");
    	handle=fopen(Name.CStr(), modes[append][writeAccess][readAccess]);
    	if(handle==null) throw NotFoundException(string::Format("Файл <^> не найден!")(file));
    }

    Скорее всего, этот код вам покажется странным, поэтому я его выкладываю. Хотя я его говнокодом не считаю и он мне даже нравится.

    Запостил: gammaker, 16 Июля 2012

    Комментарии (90) RSS

    • Зачем так просто открывать файл, для этого есть системный вызов open,
      fdw = open(argv[2], O_RDWR|O_CREAT |O_SYNC|O_TRUNC, 0600);
      Ответить
      • Если не ошибаюсь, open описан в POSIX, а fopen описан в C89+. Поэтому fopen более кроссплатформенный.
        Ответить
    • Это в какой среде исполняется?
      Ответить
      • Я запускал на Windows, но старался писать кроссплатформенно.
        Ответить
        • >>запускал на Windows
          Там и оставайтесь. Портировать не обязательно.
          Ответить
          • Мне кажется, что Windows катится куда-то не туда. Небезопасно становится оставаться на нём.
            Ответить
            • Э не, её оттуда выкатывают выкатывают, выкатывают выкатывают...
              ИМХО: помимо 2k и ХPюши, про всё остальное не чокаясь.
              Ответить
        • Это для Windows XP Home Edition, Windows XP Professional, Windows Vista Basic, ... ?
          остальные редакции лень искать
          Ответить
          • С чего ты взял, что только для Windows? Ничего платформо-зависимого в моём коде нет.
            Ответить
    • 1) void но return InternalError
      2) std::stream(name, std::ios_base::out|std::ios_base::bin|st d::ios_base::app) vs fopen(name, "a+b") vs myfile.open(name, false, true, true) сравни читабельность
      3) modes - почему не статик?
      4) зачем нужно аргументу file добавлять еще одно имя Name
      5) я уж не говорю о своих классах строки, враппером над FILE * и т.д.
      Ответить
      • 1. InternalError ничего не возвращает. А я не люблю лишний раз ставить скобки.
        2. Эта функция используется внутри класса файла. Пользователь пользуется классами File::Reader и File::Writer, в которых всё читабельно.
        3. Да, наверное, надо сделать static. Только оптимизировать производительность здесь и на таких мелочах нет смысла.
        4. Name - это константный член класса, в котором хранится путь к файлу. (string&) - это приведение к неконстантной ссылке - способ изменения константы - такая хитрая реализация свойств только для чтения.
        5. defecate-STL. Своё мне больше нравится.
        Ответить
        • 1. ну так должно быть throw, скобки тут не причём
          2. раз пользователь пользуется чем то иным, вот это иное сюда и надо спокойно передать
          4. это говнокод

          и да, разницу в передаче аргументов по копии и по ссылке ведь понимаешь?
          Ответить
          • 1. Исключения для указания ошибок программиста я не использую. Я просто выдаю сообщение об ошибке и завершаю процесс. Не вижу смысла использовать тут исключение. А return я использую, чтобы сообщить компилятору (и программисту), что функция дальше выполняться не будет. А то при включённом анализе кода выдаётся предупреждение.
            2. В "ином" коде нет ничего интересного.
            4. Он запрятан внутрь, а снаружи он только улучшает код, позволяя избавиться от кучи геттеров.

            Мой класс string использует счётчик ссылок и копируется только при записи.
            Ответить
            • > улучшает код, позволяя избавиться от кучи геттеров
              если у тебя нет сеттера, тогда какая разница? выглядит плохо

              > Мой класс string использует счётчик ссылок
              т.е. на operator [] возвращает объект MyStringCharRef, как в кутэ?
              Ответить
              • >если у тебя нет сеттера, тогда какая разница? выглядит плохо
                Что лучше: использовать геттер engine->GetObjectCount() или обращаться напрямую engine->ObjectCount? ObjectCount объявлена как const int, но трюк с приведением к ссылке позволяет устанавливать его. Используется он только внутри класса Engine.

                >т.е. на operator [] возвращает объект MyStringCharRef, как в кутэ?
                Нет, он только для чтения. Устанавливает конкретный символ функцией SetChar.
                Ответить
              • А можно написать две версии [] - возвращающую ссылку на константу (просто ссылка) и возвращающая ссылку на не-константу (вызывает копирование), причём нужная версия выбирается в зависимости от того, в качестве rvalue или lvalue результат используется?
                Правка: это вопрос.
                Ответить
                • Где на gamedev'е это обсуждалось. Вроде бы это не по стандарту и будет по-разному работать с разными компиляторами.
                  Ответить
                • нет, не прокатит
                  компилятор с++ в случае
                  string a = "trololo";
                  f(a[10]);
                  будет юзать не константный operator []
                  потому в stl строки не страдают ерундой с COW, обеспечивая минимальный интерфейс общего назначения (ну и плюс в стандарте 98/03 никто не обговаривал средства обеспечения атомарности), а для QT, где объекты строк за свою жизнь тысячи раз куда то копируются из ядра на экран, не западло переписать класс строки, чтобы по operator [] возвращать специальный объект "символ", на модификации которого тот уже будет считать ссылки и копировать родительскую строку
                  Ответить
                  • Ага, и этот объект "символ" нельзя передавать в функция как char&?
                    Ответить
                    • Вряд ли это кому-то понадобится. А если и понадобится, то объявит временную переменную, и присвоит её значение символу в строке.
                      Ответить
                    • из меня херовый знаток qt
                      засуммонь борманда
                      всё, что я вижу в сорцах
                      class Q_CORE_EXPORT QCharRef {
                      ...
                          // all this is not documented: We just say "like QChar" and let it be.
                          inline operator QChar() const
                              { return i < s.d->size ? s.d->data[i] : 0; }
                      ...
                      };

                      т.е. это не даст передать как QChar & куда-нибудь
                      Ответить
                    • > Ага, и этот объект "символ" нельзя передавать в функция как char&?
                      Именно так.

                      Во-первых QChar юникодный и в char все равно не влезет, разве что в wchar_t.

                      Во-вторых, как правильно заметил defecate++, в QChar& он тоже не кастуется.

                      В третьих - а оно нужно? У QString удобный интерфейс и посимвольное обращение к строкам требуется редко.
                      Ответить
        • > (string&) - это приведение к неконстантной ссылке - способ изменения константы - такая хитрая реализация свойств только для чтения.
          No comments. Во время чтения этой строки у меня возник необъяснимый приступ агрессии.
          Ответить
          • пациент просто еще молод, собственные классы строки в анамнезе - это от избытка энтузиазма и свободного времени
            с опытом как-нибудь придёт - время лечит
            Ответить
            • Уже проходит. Я решил перейти на C#. Там эти классы выглядят как раз так, какими я их и хотел видеть. Создавая свои классы я хотел сделать C++ таким же удобным, как и C#, но нативным. К сожалению, это не всегда получалось, и занимало очень много времени.
              Ответить
              • > Создавая свои классы я хотел сделать C++ таким же удобным, как и C#, но нативным.
                Как сказал мудрый человек - "программист на фортране может писать на любом языке... как на фортране".

                Перенести все идиомы c# в c++ (да и в обратную сторону тоже) не получится - совершенно другая архитектура, и большая часть портированного кода будет смотреться как нечто инородное (если не как говно)...

                Если для ваших задач удобнее c#, то вы приняли совершенно правильное решение, перейдя на него.
                Ответить
                • >Как сказал мудрый человек - "программист на фортране может писать на любом языке... как на фортране".
                  Только я с C++ и начинал. От C# и D я потом всего этого нахватался и захотел перевести на C++.
                  Ответить
              • В крестовом СТЛ удобные классы, я считаю. Потоки вообще офигенны.
                Ответить
                • только медленные зело
                  в сравнении с выводом напрямую
                  Ответить
                  • Почему? Разве что-то может быть медленее такого говнища, как принтф?
                    Ответить
                    • Думаю, имеется ввиду хардкорный write
                      Ответить
                    • проверь перфоманс какого-то банального и небанального вывода через printf и cout

                      когда то давно пробегала ссылка на сравнение sscanf, atof, istream и boost::spirit::qi
                      не могу найти
                      Ответить
                      • очень странно, как cout, который сразу преобразует нужный тип к строке (или не преобразует, если изначально строка), может быть медленнее, чем printf с его говнопарсингом?
                        Ответить
                        • Походу из-за сложности кода std::stream'а он банально хуже оптимизируется. Посмотрел сейчас - вызовы стримооператоров gcc даже не заинлайнило... А printf это отлично оптимизированный конечный автомат.

                          P.S. Посравнивал на простеньком примере с числом, хекс числом выровненным под 8 знакомест, и строкой скорости вывода. fprintf самый быстрый. ofstream раз в 5 медленее. QTextStream еще в 4 раза медленее (что в принципе логично - у него все-таки кодек из юникода на выхлопе).
                          Ответить
                      • вот, нашел
                        http://bit.ly/LwiPpv
                        Ответить
                    • printf вообще в десятки раз быстрее выводит, чем cout. Только я сам не понимаю, откуда берётся такая разница?
                      Ответить
                      • не в десятки
                        в разы
                        Ответить
                        • Я говорю не о парсинге, а о выводе на консоль.
                          Да, stringstream медленнее sprintf в разы.
                          cout медленнее printf\puts при выводе готовой строки в десятки раз! По крайней мере, в студии. Возьмите строчку подлиннее и сами попробуйте.
                          Ответить
                          • Ну вот оценил в gcc сырую производительность вывода, без форматирования:

                            std::cout << bigString << std::endl;
                            3.80с на вывод миллиона строк

                            printf("%s\n", bigString);
                            1.02c на вывод миллиона строк

                            Парой постов выше тестировал на более-менее сложном примере - число, шестнадцатеричное число, и строка - iostream был раз в 5 медленее fprintf.

                            Ну т.е не десятки раз это, а разы.
                            Ответить
                            • В студии попробуй. Я в ней проверял. Сейчас ещё раз попробую, чтобы время замерить...
                              Ответить
                              • > В студии попробуй.
                                Виртуалку поднимать, экспресс эдишн качать... лень короче.

                                > Сейчас ещё раз попробую, чтобы время замерить...
                                Оки, скинь тогда код, на котором будет проведено тестирование. Откомпилю его в gcc для сравнения.
                                Ответить
                                • получил очень забавные результаты:

                                  without locale
                                  cout: 545 milliseconds
                                  printf: 8 milliseconds

                                  with locale
                                  cout: 566 milliseconds
                                  printf: 461 milliseconds

                                  код:
                                  http://pastebin.com/6wsFawWy
                                  Ответить
                                • #include <iostream>
                                  #include <string>
                                  #include <windows.h>
                                  using namespace std;
                                  
                                  int main()
                                  {
                                  	string str="Hello, World! ";
                                  	for(int i=0; i<10; i++) str+=str;
                                  	LARGE_INTEGER freq, time1, time2;
                                  	QueryPerformanceFrequency(&freq);
                                  	QueryPerformanceCounter(&time1);
                                  	cout << str.c_str(); //(1)
                                  	//printf("%s", str.c_str()); //(2)
                                  	QueryPerformanceCounter(&time2);
                                  	cout << endl << (time2.QuadPart-time1.QuadPart)*1000/freq.QuadPart << " ms" << endl;
                                  }

                                  У меня этот код даёт в среднем 6100 мс. Если раскомментировать (2) и закомментировать (1), то - 61 мс. Разница в 100 раз!
                                  Ответить
                                  • Да, только тут таймер на WinAPI. Для Linux'а придётся заменить таймер.
                                    Ответить
                                    • Перетестируйте с выводом в файл. А то получается, что мы меримся скоростями вывода местных терминальных эмуляторов...
                                      Ответить
                                  • Да у вас же виндоконсолепроблемы! 6 секунд писать сраные 14 килобайт... (В файл у вас должно сливаться гораздо быстрее, чем в консоль).

                                    (1) 0.000000835
                                    (2) 0.000000882

                                    http://ideone.com/sf3FK
                                    Ответить
                                    • Тьфу, я мудак. В tv_usec же микросекунды.
                                      (1) 0.000835
                                      (2) 0.000882
                                      Вот такой будет правильный результат.
                                      Ответить
                                      • ну ideone же не в терминал выводит
                                        Ответить
                                        • > ну ideone же не в терминал выводит
                                          Ну оно и не выводит 14 килобайт в течении 6100мс ;)
                                          Ответить
                                          • развивая свой тест с локалью на обеих системах получил следующее:
                                            results:
                                            --------------
                                            win7 msvc 9.0 sp1 /O2, invoking from cmd.exe:
                                            without locale
                                            cout : 4278 milliseconds
                                            printf : 162 milliseconds

                                            after locale
                                            cout : 4711 milliseconds
                                            printf : 3541 milliseconds

                                            --------------
                                            linux 2.6.9 g++ 3.4.6 -O2, invoking from native console
                                            (linux is guest OS virt. by oracle virtualbox on host machine win7)
                                            without locale
                                            cout : 548 milliseconds
                                            printf : 580 milliseconds

                                            after locale
                                            cout : 489 milliseconds
                                            printf : 512 milliseconds

                                            код: http://pastebin.com/qgX1QDZi

                                            зы:
                                            используя putty в качестве терминала на том же линуксе получается вообще
                                            without locale
                                            cout : 18 milliseconds
                                            printf : 25 milliseconds
                                            after locale
                                            cout : 27 milliseconds
                                            printf : 25 milliseconds

                                            так что оставляет лишь вопросы реализация микрософтом printf без установки глобальной локали
                                            ну и даже под виртуалкой (хоть и с современной виртуализацией), родной линуксовый терминал в 10 раз быстрее cmd.exe
                                            Ответить
                                            • > развивая свой тест с локалью на обеих системах получил следующее:
                                              А можно еще для полноты картины замерить вывод с перенаправлением в файл (ну или в NUL, /dev/null) на тех же машинах?
                                              Ответить
                                              • хм, интересные результаты
                                                string length 10M
                                                ---------
                                                win32, output to NUL
                                                without locale
                                                ofstream: 69436 microseconds
                                                fprintf : 65772 microseconds

                                                after locale
                                                ofstream: 69005 microseconds
                                                fprintf : 64979 microseconds
                                                ---------
                                                linux, output to real file
                                                without locale
                                                ofstream: 33588 microseconds
                                                fprintf : 68873 microseconds

                                                after locale
                                                ofstream: 50673 microseconds
                                                fprintf : 50930 microseconds
                                                ---------
                                                и самое необычное:
                                                linux, output to /dev/null

                                                without locale
                                                ofstream: 122 microseconds
                                                fprintf : 4700 microseconds

                                                after locale
                                                ofstream: 23 microseconds
                                                fprintf : 4961 microseconds

                                                подозрительно похоже, что в /dev/null поток сам оптимизирует и ничего не делает при выводе в него, в отличие от fprintf
                                                потому что на строке 1G вышло
                                                ofstream: 67 microseconds
                                                fprintf : 626281 microseconds
                                                что неправильно

                                                тест с /dev/null онлайн бесплатно - см http://liveworkspace.org/code/a379ba5525d7117d9da974fe98c934fc
                                                для него уменьшил размер строки до 100М
                                                Ответить
                                                • > ofstream: 67 microseconds
                                                  Читеры... Интересно как они отличают /dev/null от настоящего файла. Не по имени же?
                                                  Ответить
          • >>Во время чтения этой строки у меня возник необъяснимый приступ агрессии.

            http://mosobzor.com/moswar/news/49d9dc30788acd0742d9cbf859ebe794.jpg
            Ответить
          • А когда я изобрёл этот способ, у меня начался объяснимый приступ энтузиазма переписать весь свой движок на него, избавившись от методов доступа. Код стал намного красивее и короче без них.
            Ответить
            • Не самое приятное решение:

              1) Разрывает шаблон с++никам, привыкшим, что const поля никто не меняет.

              2) Неоднородность. В случае с акцессорами есть простые правила. К примеру в Qt: w->text() - получить текст, w->setText() установить текст. Это правило нарушают разве что в случае, когда работают с POD структурами. В вашем же случае часть полей будет read-only, и их будут использовать как w->text, часть полей будет read-write, к которым будут лезть напрямую, а часть полей все-равно потребует нетривиальных геттеров и\или сеттеров. Чтобы определить, какой из вариантов нужен для обращения к конкретному свойству придется читать документацию (или подсказку в IDE).

              3) Самое печальное - если вдруг понадобится превратить поле в акцессор или даже сменить его тип - придется рефакторить все точки, в которых используется поле. В пределах мелкого проекта - терпимо. А в большом, состоящем из нескольких программ и библиотек... ну вы поняли ;)
              Ответить
              • >1) Разрывает шаблон с++никам, привыкшим, что const поля никто не меняет.
                Это как бы не мои проблемы. Специально для них можно использовать readonly, определив его как #define readonly const.

                >а часть полей все-равно потребует нетривиальных геттеров и\или сеттеров
                Их я пытался минимизировать. Например, делал их доступными, но создавал private копии, по которым можно определить, изменилось ли значение. Если это не получается, можно написать свой класс свойств, но на C++ это получится решение с костылями и будет работать не всегда правильно. Поэтому я не стал пытаться это делать.

                3. Движок занимает чуть меньше 10000 строк. При переходе на C# он станет гораздо меньше, потому что велосипеды можно будет выкинуть. Хотя там и не надо будет так мудрить, там есть нормальные свойства. Вот это будет облегчение!
                Ответить
                • > Это как бы не мои проблемы.
                  Хочется взять и... ну вы поняли.

                  > Хотя там и не надо будет так мудрить, там есть нормальные свойства.
                  Верно. Раз задача небольшая, удобно переписывается на c#, и идеи, заложенные в c# вам ближе - пишите на c#, никто вас за это не осудит.
                  Ответить
                  • >Раз задача небольшая, удобно переписывается на c#, и идеи, заложенные в c# вам ближе - пишите на c#, никто вас за это не осудит.
                    Ну да, пока задача маленькая - можно писать на крестах, хотя все равно дохрена времени потратишь, а если большая, то на крестах проект просто развалится, так что сразу нужно было на C# переходить.
                    Ответить
    • Нужно иметь экземпляр File для того, чтобы открыть его? o_O
      Если уж писать велосипед, то по мне лучше сделать open статической функцией-членом, возвращающей объект File.
      То, что у файла может сменится хэндл, тоже не радует. Ну и упомянутые уже флажки - sucks.
      Ответить
      • Файл открывается в конструкторе. Просто open - это общий метод для открытия любого файла, которым пользуются File::Reader и File::Writer, которые я здесь не выкладывал.

        >То, что у файла может сменится хэндл, тоже не радует.
        Он может только обнулиться при закрытии файла, а меняться не может.
        Ответить
        • > может обнулиться ..., а меняться не может

          Поделил на ноль.
          Ответить
          • Я имею в виду меняться на другой дескриптор. А закрывать файлы бывает нужно до того, как они выйдут за пределы области видимости и вызовется деструктор.

            А про деление на ноль я не понял.
            Ответить
        • >Файл открывается в конструкторе.

          Зачем закрывать ещё не открыты файл? О_о
          Ответить
          • Точнее, была задумка сделать переоткрытие одного и того же файла, но я её так и не реализовал. А закрытие осталось. Там всё равно проверка стоит, поэтому если файл не открыт, попытки его закрыть не будет.
            Ответить
            • Открытие файла вызывает функцию закрытия, которая проверяет, не открыт ли файл а чтобы это узнать, нужно проверить, не закрыт ли он...
              Ответить
              • Ну да, у меня всё так запутано. Ну и что?

                P.S. почему когда я пишу ответ, у меня иногда спрашивают проверочный код с картинки, а иногда нет?
                Ответить
                • На всех кодов не завезли, выдают один в руки через раз.
                  Ответить
                  • Только не пойму, зачем они вообще нужны? Ещё понятно, если бы я был не зарегистрирован...
                    Ответить
    • Во всём плюсы виноваты, разве не ясно?
      Ответить
    • (string::Format("Файл <^> не найден!")(file))
      Вообще лисп какой-то...
      Ответить
      • Интересно было бы увидеть вызов велосиформата с несколькими параметрами. Например аналог sprintf(buf, "%3d %3d\n", a, b).
        Ответить
        • Держи: http://govnokod.ru/7674
          Ответить
          • Спасибо ^_^
            Ответить
            • Ты ещё моих массивов с новым оператором $ не видел:
              http://govnokod.ru/7688
              Ответить
              • jQuery пожирает c++ изнутри
                Ответить
              • Как ты $ реализовал?
                Ответить
                • Лень читать тред?
                  http://govnokod.ru/7688#comment104503
                  Ответить
                • Там всё написано. Класс с перегруженными операторами, который хранит относительную позицию в массиве. Число 0 обозначает нулевой элемент, UINT_MAX - последний элемент массива. $ - экземпляр этого класса.
                  Ответить
                  • >UINT_MAX - последний элемент
                    (UINT_MAX-1) - это какой элемент? Предпоследний?
                    Ответить
                    • Нет, это число с фиксированной запятой, которое задаёт относительную позицию. UINT_MAX/2 - середина массива. Ещё есть отдельное поле, хранящее абсолютное смещение. Предпоследний элемент - это когда отн. позиция = UINT_MAX и смещение = -1.
                      Ответить
                  • > класс перегружен операторами
                    Ответить

    Добавить комментарий