О тонкостях, точках и запятых

March 26th, 2010 Begemot Posted in Использование

Есть у меня модуль для проверки наличия обновлений. Скачивает с сайта тхт файлик, парсит и радует юзверя наличием информации о новой версии.

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

Начал ковыряться, оказалось фаерволы не причем, вернее не всегда причем. Оказалось что “2.8” это не всегда 2.8, банально правда?:) Вроде бы известно, банально, но пока гром не грянет…

В общем решение вот:

bool wxString::ToCDouble(double * val)  const
Variant of ToDouble() always working in "C" locale.
Works like ToDouble() but unlike it this function expects the floating point number to be formatted always with the rules dictated by the "C" locale (in particular, the decimal point must be a dot), independently from the current application-wide locale (see wxLocale).

Related:

28 Responses to “О тонкостях, точках и запятых”

  1. …плюс человеческий фактор!
    У меня юзеры, бывало, писали “рупь двадцать” как “1-20”. Тут никакая локаль не поможет 😉

  2. ну ты даёшь! сравнивать версии с помощью double-ов – это ещё тот изврат…
    ведь код такого типа:
    double x = 2.8;
    if (x == 2.8) {…}
    не всегда будет работать (зависит от округления).
    Поэтому я бы тебе советовал бы использовать разбиение версии на целочисленные токены при помощи wxStringTokenizer.
    Будет и более точно и всегда так, как ты ожидаешь.
    Ну и даст возможность проверять версии, там где больше двух чисел, например, 2.8.10.1

  3. Полностью поддерживаю Norfolc! Бегемот, ты извращенец!!! 🙂

  4. че-то меня сильно много критиковать начали в последнее время:)

    А по делу, как только у меня хоть раз случится что 2.8 не равно 2.8 или появятся версии типа 2.8.10.2 – так сразу и усовершенствую систему:)

  5. не-а, мы тебе даем дельные советы 🙂 потому как сами тоже наступали на такие же грабли, и прекрасно помним время, проведенное в отладчике в поисках мистических ошибок.
    А что касается double, то для них строго не рекомендуют использовать конструкции вида
    if ( x == 2.8 )
    а только сравнение больше или меньше. Ну ежели очень хочется, то вышеуказанное можно попытаться заменить на
    if ( abs( x – 2.8 ) < 0.001 )
    с желательной точностью

  6. хм, честно говоря не знал что с Double все таки плохо. То есть она получается путем деления\умножения – то еще ладно, тут я знаю что может быть не совсем .00, но что код типа

    double x1 = 2.6;
    double x2 = 2.7;
    if (x1 != x2)

    может не всегда работать для меня сюрприз:)

    Посмотрел в код у меня там – if (version > curVersion), так что на подсознательном уровне получается я пишу правильно:)

  7. эй, не передергивай 🙂 я говорил про
    double x1 = 2.6;
    if (x1 == 2.6)
    ну а в твоем гипотетическом примере
    double x1 = 2.6;
    if (x1 != 2.6)
    вполне может оказаться, что х1 не равен 2.6

  8. эээ видимо я чего-то не понял 🙂

  9. да и вообще, даешь целочисленную арифметику!

  10. Тогда уж – даешь ООП, как учат отцы и старшие:
    [code]
    #include <wx/tokenzr.h>
    #include <vector>

    class Version
    {
    public:
    Version(wxString versionString, wxString delimiter = wxT(".")) {
    wxArrayString AS = ::wxStringTokenize(versionString, delimiter);
    long L;
    for(size_t n = 0; n < AS.size(); ++n) {
    AS[n].ToLong(&L);
    ver.push_back(L);
    }
    }
    bool operator < (const Version& v) const {
    for(size_t n = 0; (n < ver.size()) || (n < v.size()); ++n) {
    if(getPart(n) != v.getPart(n)) {
    return getPart(n) < v.getPart(n);
    }
    }
    return false;
    }
    inline long getPart(size_t n) const {
    return (n < ver.size())? ver[n] : 0;
    }
    inline size_t size() const {
    return ver.size();
    }
    protected:
    std::vector< long > ver;
    };
    [/code]

  11. Суровые русские программисты?:)
    А меня и флоат устраивает выше крышы, к чему такие сложности?:)

  12. Ну, ты же сомневался, читать “Рефакторинг” Фаулера или погодить?
    Я прочитал – и вот… 😉

  13. оо, круто, ты прочитал реракторинг и зарефакторил мою 1 строчку, в целый класс? при этом не добавив функциональности 🙂

    Хорошо наверное что я все-таки не купил Фаулера:)

  14. Теперь вы добились своей цели: наконец то нихрена непонятно что весь этот код делает 🙂

  15. Это-то как раз понятно: весь этот код лежит в .h-файле в личной библиотечке и ждет, когда вам в шестой раз понадобится работать с версиями программ в виде строки. (Написан он должен быть после того, как это понадобилось в пятый раз. Раньше смысла нет).

    Между прочим, у Фаулера была бы единственная претензия к моему классу: “код должен быть самотестирующимся”.
    То есть туда еще надо добавить функцию, которую можно будет запустить в серии тестов после каждой компиляции и убедиться, что класс не сломался…
    Рефакторинг же – наука о том, как перелопатить жуткий унаследованный код так, чтобы в нем можно было разобраться, поддерживать и добавлять функционал. Исследование таких вещей оставляет мало шансов сохранить здоровую обывательскую психику 😉

  16. У меня есть еще одна притензия, где проверки на ошибки ? где вообще хоть какие-то проверки? 🙂

  17. Да, по этому поводу Фаулер бы тоже высказался 😉
    Но я, к сожалению, не параноик и не смог найти здесь ни одного места для assert’a. Опыта, видимо, не хватает… 🙁

  18. не ассертами едиными 🙂
    что будет если я передам пустую строку, или “хрень” ? как вызывающему коду узнать об ошибке ?

  19. Нечисловая хрень или пустая строка превратится в версию 0.0…
    Как предусмотреть, что в этом случае нужно вызывающему коду?
    Можно создать еще один метод:
    [code]bool isOK() { return (size() > 1) || (getPart(0) != 0); }[/code]
    , но это можно сделать тогда, когда он понадобится 😉

  20. А что будет в случае 10.хрень.2.213 – такая строка возникнет в случае ошибока парсинга\форматирования исходных данных (у меня такая ситуация была), мой код из 1 строчеки сразу кричит про ошибку, а твой?:)

  21. Вот кстати вчера ссылка проскакивала в тему – http://p.umputun.com/14926638, для всех кто увлекается Фаулером:)))

  22. А что будет в случае 10.хрень.2.213 – такая строка возникнет в случае ошибока парсинга\форматирования исходных данных (у меня такая ситуация была), мой код из 1 строчеки сразу кричит про ошибку, а твой?:)

    надо цикл переделать так:

            for(size_t n = 0; n < AS.size(); ++n) {
                long L = 0;
                AS[n].ToLong(&L);
                ver.push_back(L);
            }

    иначе для неверных значений исходной строки значение L будет неопределённым.

  23. это не отвечает на мой вопрос…

  24. это не отвечает на мой вопрос…

    будет ноль на месте хрени… а если нужн тебе ассерты, то добавь их:

    wxASSERT(AS[n].ToLong(&L));
  25. Мне не нужны ассерты, мне нужно что-то типа того как у меня в вызывающем коде
    if (!str2.ToCDouble(&version))
    {
    HaveError = true;
    return -1;
    }

    Ошибка? отлично, говорим пользователю про ошибку – и выходим из процедуры проверки обновлений – на самый вверх.\

    p.s. Вот под маком хорошо – там походу проверка на обновления, делается парой кликов мышки, а функциональность уже в системе встроенная (если я правильно понял)

  26. #include
    #include

    class Version
    {
        bool isOK;
    public:
    Version(wxString versionString, wxString delimiter = wxT(".")): isOK(true)
     {
            wxArrayString AS = ::wxStringTokenize(versionString, delimiter);
            for(size_t n = 0; n < AS.size(); ++n) {
                long L = 0;
                isOK = isOK && AS[n].ToLong(&L);
                ver.push_back(L);
            }
        }
        bool IsOK() { return isOK; }
        bool operator < (const Version& v) const {
            for(size_t n = 0; (n < ver.size()) || (n < v.size()); ++n) {
                if(getPart(n) != v.getPart(n)) {
                    return getPart(n) < v.getPart(n);
                }
            }
            return false;
        }
        inline long getPart(size_t n) const {
            return (n < ver.size())? ver[n] : 0;
        }
        inline size_t size() const {
            return ver.size();
        }
    protected:
        std::vector ver;
    };

    вот после создания объекта вызывай метод IsOK и проверяй всё ли в порядке…

  27. мда, так действительно лучше, но согласитесь менять 1 строчку на целый класс, это изврат… ссылку выше читали?:)

  28. Ссылка выше немного о другом…
    Я согласен, что менять одну строчку на любой самый прекрасный класс неразумно. Собственно, и говорил, что такой класс может появиться, когда ты хочешь одну часто используемую фигню вылизать один раз, а потом использовать как библиотечную.

    Кстати, о фанатизме: второй день обсуждается класс, написанный за 10 минут. Как-то это по-фаулеровски 😉