LINUX.ORG.RU
Ответ на: комментарий от CatsCantFly

Аргумент f — это будет указатель на C-шную функцию формата: char * fn(char *a), которая при этом модифицирует значение своего аргумента. Соответственно, пихнуть в нее результат basic_string::data() просто так нельзя, т.к. возвращается const char *. А делать const_cast к этому указателю — это провоцировать UB. тыц

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

Во-первых, я сформулировал условие, которое выдвинул сам смайлик, см. здесь: Почему я выбираю C++ (комментарий)

Во-вторых, вы же сами смайлику уже задавали точно такой же вопрос: Почему я выбираю C++ (комментарий) Зачем же его повторять мне?

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

ну и когда в Java подвезут полноценное множественное наследование? а перегрузка операторов там уже есть?

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

О да, C++ настолько хорош,

Еще раз: речь шла о том, что вменяемых альтернатив нет. «Хорош» и «Нет альтернатив» — это разные вещи.

что Mozilla изобрели новый язык чтобы его заменить.

Mozilla стала вкладываться в Rust, когда будущее C++0x было не совсем понятно. На данный момент ситуация несколько иная.

К тому же, о производительности Chrome и Firefox ходят легенды.

Так где же альтернативы?

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

Мне нужно было обернуть лишь 2 функции - basename, dirname в цепепешный вариант :-) Мой шаблон имеет смысл только для этих 2-х ф-ий - отсюда и ограничение, которое было возложено на компилятор

Вообще это все можно было бы сделать без шаблонов и с ограничением доступа к деталям реализации гораздо проще.

class path_utils {
public :
  static std::string basename(const std::string & path) {
    return ::basename( make_tmp_c_string(path).data() );
  }
  static std::string dirname(const std::string & path) {
    return ::dirname( make_tmp_c_string(path).data() );
  }
private :
  static auto make_tmp_c_string(const std::string & path) {
    const auto s = path.size() + 1;
    std::vector< std::string::value_type > tmp;
    tmp.reserve(s);
    tmp.insert(tmp.begin(), path.c_str(), path.c_str() + s);
    return tmp;
  }
};
И главная проблема здесь — это манипуляции с временным буфером для хранения мутабельной C-шной строки. А не ограничение для возможности вызова dirname или basename. Если не сильно заморачиваться на производительность, то создание этого самого временного буфера можно было бы записать вообще вот так:
  static auto make_tmp_c_string(const std::string & path) {
    return std::vector< std::string::value_type >(
      path.c_str(), path.c_str() + path.size() + 1);
  }
Но будет ли это вариант настолько же эффективен как вариант с предварительным reserve, да еще и на разных реализациях STL, это вопрос.

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

Интересный код на цепепе

string path_basename(const string& path) {
  return basename( (char*) string( path ).data() );
}

string path_dirname(const string& path) {
  return dirname( (char*) string( path ).data() );
}
Но он является показательным примером, как даже знаток таких тонкостей цепепе, как время жизни временного объекта угодил в ловушку :-) Ты использовал метод basic_string<>::data, который возвращает указатель на данные без терминирующего нуля :-) Но ведь в мане по basename(3) говорится: «The functions dirname() and basename() break a null-terminated pathname string into directory and filename components.» :-) Т.е. слажался первый раз :-) Теперь посмотрим что там в стандарте про basic_string<>:data - раздел 21.4.7.1 Requires: The program shall not alter any of the values stored in the character array. :-) Но т.к. basename/dirname таки модифицируют передаваемую им C-строку, то слажался второй раз :-) Ну и в-третьих, это повезло, что бойлерплэйт тут можно упихнуть в одну строку, и не так очевидна необходимость в шаблоне :-) А вообще-то сама идея - использовать *шаблон*, дабы свести на нет бойлерплэйт, но такой шаблон, который имеет смысл только с определёнными аргументами :-) И в цепепе это приводит к километрам простыней :-)

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

PS. Т.к. пример показывает как на замечательном цепепе можно написать с виду короткий код, но который совершенно неприемлем в продакшене :-) Так что please, don't use C++ :-)

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

Теперь внимание вопрос: какой в этом смысл?

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

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

Ты использовал метод basic_string<>::data, который возвращает указатель на данные без терминирующего нуля

Смайлик, хватит строить из себя эксперта по C++! Начиная с C++11 data() и c_str() возвращают один и тот же указатель на буфер с терминирующим нулем.

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

Ну почему, почему люди не любят KISS :( Какие исключения, какие шаблоны, какие unique_ptr, какие вектора, - задача, блин, в одну строку.

Ну хорошо, я тебе поверю :-) Продемонстрируй это на цепепе :-)

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

Смайлик, хватит строить из себя эксперта по C++!

Бомбит? :-) Поминаю :-) Смирись :-)

Начиная с C++11 data() и c_str() возвращают один и тот же указатель на буфер с терминирующим нулем.

Но судя по приведённой тобой же ссылке http://en.cppreference.com/w/cpp/string/basic_string/data это не так :-) Сравни с http://en.cppreference.com/w/cpp/string/basic_string/c_str :-) Ибо нефиг ссылаться на старьё, или таки data и c_str - суть разные значения возвращают :-)

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

Но судя по приведённой тобой же ссылке http://en.cppreference.com/w/cpp/string/basic_string/data это не так :-)

Возьми словарь или хотя бы Google Translate и переведи, идиот:

The returned array is null-terminated, that is, data() and c_str() perform the same function.

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

Сначала было

Самый простой — определить process_path внутри пространства имен impl или details и объявить это пространство имен деталями реализации.

Теперь простыня с private/public, векторами с резервами :-) Ясно же, что простые вещи на цепепе делаются слишком сложно :-) А уж с вовлечением шаблонов и подавно :-)

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

Возьми словарь или хотя бы Google Translate и переведи, идиот:

Эта ремарка про C++11 должна была быть первой строкой :-) А так первые строки таковы:

http://en.cppreference.com/w/cpp/string/basic_string/data «Returns pointer to the underlying array serving as character storage.»

http://en.cppreference.com/w/cpp/string/basic_string/c_str «Returns a pointer to a null-terminated character array with data equivalent to those stored in the string.»

Т.е. демонстрация качества документации цепепе :-) Приятного кодинга :-)

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

Теперь простыня с private/public

Вы и считать не умеете? Сравните со своим вариантом, в котором еще и ошибка сидит: C++ vs Java (в реализации ООП) (комментарий)

Так где простыня?

Ясно же, что простые вещи на цепепе делаются слишком сложно

Это следствие вашей идиотии, не более того.

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

Вы и считать не умеете? Сравните со своим вариантом, в котором еще и ошибка сидит:

Какая ошибка? :-) Бугага :-) Я то знаю, к чему ты клонишь - strdup типа не проверил на NULL :-) Наивно полагать, что после истощения памяти можно восстановиться, ибо финита ля комендия, game over :-)

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

Это следствие вашей идиотии, не более того.

Нет, в следствии этого глаза красные после цепепе :-)

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

Наивно полагать, что после истощения памяти можно восстановиться, ибо финита ля комендия, game over :-)

Решение об этом не должен принимать разработчик библиотечной функции path_dirname. Нет памяти для работы — выброси наверх std::bad_alloc, пусть у пользователя вашей библиотеки голова болит. Это всяко лучше, чем крах всего приложения по сегфолту из-за разыменования нулевого указателя.

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

Нет, в следствии этого глаза красные после цепепе :-)

Слово «нет» в этой фразе лишнее: в следствии вашей идиотии получается чрезмерно переусложненный код, в следствии чего у вас красные глаза после цепепе.

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

Нет памяти для работы — выброси наверх std::bad_alloc, пусть у пользователя вашей библиотеки голова болит.

Единственным пользователем этого кода является его автор, который не очень верит в полезность std::bad_alloc :-)

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

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

Ну так язык провоцирует писать такую муть, где либо простыни, либо вектора с нулями вручную, либо неопределённое поведение, либо инкапсуляция ф-й в структуру с применением директив доступа :-) А всего-то надо от языка - проверить строчки в компайл-тайме :-) Лол :-)

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

Это очень и очень хорошо.

Ну что лицемерить? :-) Давай по-честному :-) Я признаю, что не проверил strdup на NULL :-) Mea culpa :-) Но кто-нибудь из присутствующих когда-нибудь проверяет на предмет std::bad_alloc? Кто-нибудь выполняет в обработчике код восстановления в ситуации тотального исчерпания виртуальной памяти? Только по-честному :-)

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

демонстрируя, что в других языках это решается с помощью элементарнейших макр? :-)

И много таких языков? Особенно, если не учитывать динамику.

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

С++ частыми местами неудобный, криво сшитый, многокостыльный шубниггурат от семейства сиподобных. Но разруха не в клозетах, она в головах! (с) Ф.Ф. Преображенский. А производительность такого прожорливого софта изначально вопрос архитектурный.

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

Но кто-нибудь из присутствующих когда-нибудь проверяет на предмет std::bad_alloc?

В подавляющем случае это не нужно. bad_alloc мало чем отличается от других типов исключений.

Если программист озадачивается понятиями exception safety guarantees, то он пишет код, которые обеспечивает хотя бы базовую гарантию безопасности. Что означает активное использование RAII и автоматический откат не выполненных до конца операций (ну или хотя бы оставление внутренностей в таком состоянии, которое позволяет спокойно завершить работу). При таком подходе, не суть важно, выскакивает ли bad_alloc или invalid_argument.

Кто-нибудь выполняет в обработчике код восстановления в ситуации тотального исчерпания виртуальной памяти?

Не понятно, что вы понимаете под восстановлением.

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

Другое дело, если при этих операциях исключения могут возникать вновь. Но это уже, скорее всего будет либо исключение из деструктора при раскрутке стека из-за предшествующего исключения (что сразу ведет к std::terminate), либо же будет исключение где-то в блоке catch на самом верху. И там уже вполне уместно дергать std::abort(), т.к. возможностей для отката к какой-то исходной точки нет.

Но это все гораздо лучше, что сегфолт из-за разыменования нулевого указателя.

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

В твоём же говнокоде в process_path можно передать любую ф-ю, а не только удовлетворяющую семантике basename/dirname :-)

А зачем ту функцию выставлять наружу? Почему в качестве интерфейса не может торчать просто path_basename и path_dirname? Тогда и не надо будет решать несуществующую проблему.

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

О да, C++ настолько хорош, что Mozilla изобрели новый язык чтобы его заменить.

Дык, именно что «изобрели новый», а не взяли один из существующих. Tак как ими заменить не особо получается.

К тому же, о производительности Chrome и Firefox ходят легенды.

Этот аргумент имеет смысл при наличии других, заметно более быстрых, браузеров. Такие есть? И написаны не на С++?

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

А зачем ту функцию выставлять наружу? Почему в качестве интерфейса не может торчать просто path_basename и path_dirname?

Можно :-) Но мне нравятся проверки компайл-тайма :-)

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

Почему в качестве интерфейса не может торчать просто path_basename и path_dirname?

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

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

В подавляющем случае это не нужно. bad_alloc мало чем отличается от других типов исключений.

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

При таком подходе, не суть важно, выскакивает ли bad_alloc или invalid_argument.

Это 2 совершенно разные проблемы - логическая ошибка и отсутствие памяти :-) Логическая ошибка - это то же, что нарушение assert - т.е. ошибка, которая по определению может быть обнаружена ещё *до* запуска программы :-) bad_alloc - это ошибка времени выполнения, причём ошибка, которая говорит, что ничего уже сделать нельзя :-) А в Linux это уже как предзнаменование того, что процесс будет уничтожен OOM-killer :-)

Не понятно, что вы понимаете под восстановлением.

Чего не понятного? :-) Что делать то в случае bad_alloc, или, что тоже, какой толк проверять на NULL в strdup()/malloc()? (разве что для правильности с т.з. программирования)

Можно грохнуть проблемного актора, в процессе чего часть ресурсов будет возвращена системе.

Можно *попытаться* грохнуть :-) Только нет гарантии, что получится - на Linux твой процесс может стать жертвой OOM-killer :-)

Но это все гораздо лучше, что сегфолт из-за разыменования нулевого указателя.

С т.з. дурного тона таки лучше :-) А на практике - что при сегфолте, что при bad_alloc - исход почти неизбежно один :-)

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

нехватку памяти стоит ловить хотя бы для сохранения данных о состоянии софта. все ресурсы. которые понадобятся для такого сохранения выделяются заранее.

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

нехватку памяти стоит ловить хотя бы для сохранения данных о состоянии софта. все ресурсы. которые понадобятся для такого сохранения выделяются заранее.

Так, так, и ты делаешь это в своём софте? И твои обработчики bad_alloc гарантированно не требуют памяти для освобождения ресурсов? И ты, конечно, не на Linux? :-)

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

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

Специально для идиотов: нет смысла ловить именно bad_alloc. В местах, где программа может предпринять какие-то действия, имеет смысл ловить просто std::exception.

Чего не понятного?

Например, почему я пытаюсь объяснить что-то идиоту.

Что делать то в случае bad_alloc, или, что тоже, какой толк проверять на NULL в strdup()/malloc()?

В каком именно приложении? HTTP-сервер — это одно, Word из Office — другое, видеоконвертер третье. В каждом из этих приложений будет своя система обработки проблемных ситуаций и свои точки, в которых возможно что-то сделать.

Поэтому разговор о возможности восстановления без явного сужения предметной области не имеет смысла. И мне не понятно, почему вам это не понятно.

на Linux твой процесс может стать жертвой OOM-killer

К счастью, даже мир Unix-ов не ограничивается одним только Linux-ом.

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

обработчики бэдаллоков и нуллов от маллоков гарантированно не просят выделит память в обработчике нахватки памяти. а ты не пишешь гарантированный код?

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

В местах, где программа может предпринять какие-то действия, имеет смысл ловить просто std::exception.

«Какие-то действия» :-) Т.е. при возникновении std::exception, программа может делать «какие-то действия» :-) Грохнуть поток, например, при возникновении неважно какого, но исключения :-) Лол :-)

Поэтому разговор о возможности восстановления без явного сужения предметной области не имеет смысла. И мне не понятно, почему вам это не понятно.

Мне не понятно что делать в случае с bad_alloc на Linux :-) Вот и всё :-)

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

обработчики бэдаллоков и нуллов от маллоков гарантированно не просят выделит память в обработчике нахватки памяти. а ты не пишешь гарантированный код?

Ну если твой обработчик нехватки памяти будет располагать каким-то заранее отведённым резервом, то флаг в руки :-) Может быть не только в log запись перед завершением сумеешь сделать :-) Вопрос в другом изначально был - кто-нибудь так делает? :-)

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

Метод basic_string::data() возвращает константный указатель, а модификация значения по этому указателю — это UB. тыц

const charT* data() const noexcept; Returns: A pointer p such that p + i == &operator[](i) for each i in [0,size()].

Это стандарт. Сможешь реализовать так, чтоб все сломалось при модификации? Так что это такой же UB, как и то, что до С++11 все прекрасно знали, что вектор не имеет «дырок».

// captcha: ARCH OF CHRIST

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

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

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

К счастью, даже мир Unix-ов не ограничивается одним только Linux-ом.

Да слышал я эти сказки про множественность реализаций :-) От тех же лисперов, которые любят рассказывать про кроссплатформенность и существование разных рантаймов, ага :-) Только вот пишут в основном на SBCL :-) Спрашивается, зачем было израсходовано столько человеко-лет на около десятка реализаций, если в ходу, фактически, осталась одна? :-) Потраченное зря время, которое вместо доказывания чего-то друг перед другом можно было потратить на создание одной суперской реализации :-) Так и в мире Unix-like :-) Только в мире Unix-like сообщество значительно больше :-) А суть та же - бесполезная трата времени для одного и того же различными командами :-)

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

const charT* data() const noexcept; Returns: A pointer p such that p + i == &operator[](i) for each i in [0,size()].
Это стандарт.

Если вы уж взялись ссылаться на стандарт, то не останавливайтесь на полдороги:

Requires: The program shall not alter any of the values stored in the character array.

Так что UB здесь будет. Но вам никто не запрещает надеяться, что это никогда боком не выйдет (хотя сильно зависит от версии GCC, т.к. не все сидят на последних версиях).

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

Да слышал я эти сказки про множественность реализаций

Блин, идиот, да просто по сторонам посмотрите. MacOS — это же Unix, но не Linux, там ядро из FreeBSD.

Да тот же Linux для разных нужд переконфигурируют так, что мало не кажется.

Ну и приплетать в качестве примера кроссплатформенности давно никому не нужный Lisp — это признак большого ума, да.

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

Если это inline-функции, определенные в публичном заголовочном файле...

А зачем этим функциям быть целиком объявленными в заголовочном файле? Я бы не стал придумывать дополнительные условия к изначально дурацкой задаче.

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

использовал метод basic_string<>::data, который возвращает указатель на данные без терминирующего нуля :-)

Нет, в С++11 data и c_str возвращают одинаковое значение - указатель на данные с нулем.

Т.е. слажался первый раз :-)

Нет, это ты продолжаешь лажать раз за разом.

The program shall not alter any of the values stored in the character array.

Ответил выше, стандарт выписан так, что data() вернет абсолютно тот же указатель, что и &[0]. Кроме пустых строк, где data() не UB в отличие от. Так что это вполне допустимый трюк, тем более нам эта строка больше не будет не нужна, так что пусть там расставляются нули где угодно. Ну и если говорить про стандарт, то твоих strdup, basename и dirname вообще там нет.

в-третьих, это повезло, что бойлерплэйт тут можно упихнуть в одну строку

А тебе не повезло, да?

вообще-то сама идея - использовать *шаблон*, дабы свести на нет бойлерплэйт

template <class F>
string func_c_string( F f, string s ) {
  return f( (char*) s.data() );
}

int main()
{
  const char* s = "/please/dont/use/c++";  
    
  cout << func_c_string( dirname, s ) << '\n';
  cout << func_c_string( basename, s ) << '\n';
}

И все.

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

Блин, идиот, да просто по сторонам посмотрите.

Посмотрел :-) Вижу много лысых теоретиков, склонных к менторству :-)

MacOS — это же Unix, но не Linux, там ядро из FreeBSD.

Вау, и что? :-) Много ли тех, кто использует MacOS на сервере? :-) У меня нет столько времени, чтобы свой код портировать на все платформы в мире лишь потому, что они в мире есть, и что, быть может, 1 человек когда-то запустит мой код, в котором, быть может, в 21 веке, имея 16 Гб оперативы нарвётся на своём Маке на bad_alloc, и вот тут обработчик георически упасёт счастливчика от завершения приложения, использующего мою доморощенную библиотеку (что вряд-ли) :-) Лол :-)

Ну и приплетать в качестве примера кроссплатформенности давно никому не нужный Lisp — это признак большого ума, да.

Ты хоть бы выучил тот самый Lisp :-) Для ума полезно :-)

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

Да ладно, ладно :-) Это ты тут KISS демонстрируешь, я уже понял откуда ноги растут :-) Хороший код, компактный, но в стандарте ведь сказано, как я уже говорил, что не можно программе модифицировать данные по basic_string<>::data :-) Не могу себе позволить нарушать требования стандарта :-) И пусть даже data возвращает массив без дырок с \0 в конце - стандарт не велит его менять, вот и всё :-)

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

Так что UB здесь будет.

Расскажи каким образом, ты получил свой указатель через &[0], я через data(), по стандарту они равны с точностью до бита. Как ты получишь тут UB? Я вижу только один случай, где будет разница - пустые строки, тут очевидно реализация может из data() вернуть константу, и под это и выписано это предупреждение. Но пустую строку и модифицировать не получится.

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

Я вижу только один случай

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

Посему лучше сейчас лишний раз присесть, чем потом отгрести.

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

Ну и если говорить про стандарт, то твоих strdup, basename и dirname вообще там нет.

Нет, ну ты ваще :-)

И все.

Так ведь не всё :-) Я тоже так изначально и сделал (за исключением того, что не стал связываться с data()) :-) Но потом возникла идея продемонстрировать как сложно в цепепе заставлять делать компилятор то, что хочется :-)

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

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

Ну понятно, вы говнякаете какую-то никому не нужную хрень. А все проблемы в C++.

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