Страницы

Поиск по вопросам

понедельник, 2 декабря 2019 г.

Чем плох перехват всех исключений разом?

#c_sharp #исключения


Является ли антипаттерном  перехват всех исключений разом? 

Чем плох такой подход:

try {
    foo();
} catch (Exception e) {
    // handle <-- разумеется, тут реальная обработка, а не подавление исключений
}


Не указывая какие конкретно исключения я хочу обработать.

Допустим, у меня в try...catch обёрнут код работающий с файловой системой, для конкретики
- копирование файла.

В документации написано аж целых 8 вариантов исключений, которые может выбросить
данный метод -- должен ли хороший программист тщательно выписывать все возможные эксепшены
и периодически проверять, что майкрософт не добавит новые?

Я слышал, что можно обобщить эксепшены и не писать каждый раз PathTooLongException,
DirectoryNotFoundException и DirectoryNotFoundException, а один раз обработать IOException,
от которого они все наследуются. Это помогает сократить количество блоков catch, но
не сильно (в примере с File.Copy - можно сократить на 4, всего 8). Правильный ли это
подход или тут тоже есть свои нюансы?
    


Ответы

Ответ 1



Вы описываете какое то совсем малюсенькое правило и возводите его в абсолют. К счастью, в природе нет таких жестких правил, и, при разработке, программисту как в данном случае, так и во всех других случаях, следует пользоваться также головой. Например, первый вопрос при обработке исключения, который может возникнуть - а сделал ли вызываемый метод свою работу? Или, если вы по таймеру вызываете сторонний сервис, то куда бы выше вы бы хотели пробросить исключение? Интересует ли вызывающий код вообще проблемы вызванной функции? Например, если вы по таймеру делаете одно и то же, просто сторонний сервис то работает, то не работает, то имеет смысл факт проблем с ним писать в лог и, возможно, кого то оповестить о проблемах, не важно каких, сеть отвалилась или сервер возвращает 500 - таймеру это не интересно. Но если вы копируете файлы, то, наверно, вызывающему коду будет интересно узнать о результате операции. В таком случае, если и ловить все исключения, то только для того, чтобы залогировать и оправить исключение дальше, выше по стеку. По поводу того, какие типы перехватывать исключений - все просто. Если вам есть разница между FileNotFoundException и DirectoryNotFoundException в вашей логике, то перехватывайте их отдельными обработчиками. Если разницы нет, и вас не интересует специфика проблемы, только её наличие, то перехватывайте одним.

Ответ 2



Перехватывают исключения по типам когда ты хочешь запустить отдельный сценарий для конкретного исключения. Допустим если у тебя FileNotFound exception и только в этом случае ты хочешь вернуть FileNotFoundResult, в все остальные случаи ты хочешь просто сделать лог: try { foo(); } catch (FileNotFound exception) { return new FileNotFoundResult() } catch(Exception ex){ logger.Log(ex.Message) } Так как все исключения наследуются от родительского класса Exception, то его catch statement нужно указывать в самом низу

Void как параметр

#cpp


Чем отличается объявление функции 

int main(){}


От 

int main(void){}


?

Вроде все то же самое, но часто попадается в исходниках последнее. Если нет разницы,
зачем писать лишнее?
    


Ответы

Ответ 1



void foo(void); Это правильный путь объявления функции без параметров в С, С++. С другой стороны: void foo(); Имеет разное значение в С и С++! В С это означает "может принимать любое количество параметров неизвестных типов", в С++ имеет тот же смысл что и foo(void). Функции с переменным количеством аргументов по своей сути являются небезопасными, и их следует избегать, когда это только возможно. Source

Ответ 2



Предлагаю разобраться с подобным синтаксисом. В языке C функция может быть объявлена как 6.7.6.3 Function declarators (including prototypes If, in the declaration "T D1",D1 has the form D(parameter-type-list) or D(identifier-list_opt) Как видим, у нас имеется два варианта объявления: В скобках parameter-type-list В скобках identifier-list При этом parameter-type-list является обязательным, а identifier-list является опциональным (_opt). Теперь взглянем на то, что же такое identifier-list: An identifier list declares only the identifiers of the parameters of the function. An empty list in a function declarator that is part of a definition of that function specifies that the function has no parameters. The empty list in a function declarator that is not part of a definition of that function specifies that no information about the number or types of the parameters is supplied. То есть функция с identifier-list это вот такое объявление: void foo(a, b, c) int a, b, c;//Функция объявлена с identifier-list При этом такое объявление не накладывает ограничений на тип и количество параметров при вызове. Также, такой список является опциональным, т.е. может отсутствовать. void foo();//Функция с пустым identifier-list А это значит, что при вызове такой функции можно передавать параметры разных типов в разном количестве, т.е. вызовы foo() или foo(1, 2) являются законными. Перейдем к parameter-type-list: A parameter type list specifies the types of, and may declare identifiers for, the parameters of the function. это список типов параметров, и, возможно, идентификаторов: void foo(char, int, float x);//Функция объявлена с parameter-type-list Смотрим стандарт далее: The special case of an unnamed parameter of type void as the only item in the list specifies that the function has no parameters. То есть имеется специальный случай для объявления функции не принимающей параметров - один параметр типа void без имени: void foo(void);//Функция без параметров Для такой функции вызов foo(1, 2) уже не является правильным, в отличии от ситуации с identifier-list. Теперь, что касается C++. В C++ имеется один вариант объявления функции: In a declaration T D where D has the form D1 ( parameter-declaration-clause ) cv-qualifier-seq_opt ref-qualifier_opt noexcept-specifier_opt attribute-specifier-seq_opt trailing-return-type При этом имеется правило: ... If the parameter-declaration-clause is empty, the function takes no arguments. A parameter list consisting of a single unnamed parameter of non-dependent type void is equivalent to an empty parameter list. То есть для C++ объявления void foo(); void foo(void); полностью эквивалентны. И немного истории. Выдержка из книги Бьёрна Страуструпа "Дизайн и эволюция C++": В C with Classes введена нотация f(void) для функции f(), не принимающей аргументов, вместо нотации f() в C, обозначающей функцию с любым числом аргументов без проверки типов. Однако вскоре пользователи убедили меня, что нотация f(void) неизящна, а объявление с помощью f() функции, которая может принимать аргументы, далеко от интуитивно очевидного. Поэтому после некоторых экспериментов было решено оставить нотацию f() для функции, не принимающей никаких аргументов, ибо именно этого ожидает неискушенный пользователь. Чтобы отважиться на такой разрыв с традициями C, мне потребовалась поддержка со стороны Дуга Макилроя и Денниса Ритчи. Только после того как они охарактеризовали нотацию f(void) словом "гадость", я решил придать f() очевидную семантику. Однако и по сей день правила контроля типов в C гораздо слабее чем в C++, а комитет по стандартизации ANSI C одобрил "отвратительную f(void)", впервые появившуюся в C with Classes.

Ответ 3



Чтобы компилятор не ругался, по стандарту, должны быть описаны параметры принимаемые функцией, если их нет то void - пусто/отсутствие параметров. Попробуйте собрать код без void с ключами -Wall -Wextra -pedantic, для C языка полезен ключ -Wstrict-prototypes (gcc или clang) и увидите реакцию компилятора. Вероятно будет полезно ознакомиться с ключами компилятора, чтоб получать больше информации о собственном коде. например важные ключи для анализа собственного кода: -Wall Включите все предупреждения о конструкциях, которые некоторые пользователи считают сомнительными, и которые легко избежать (или изменить для предотвращения предупреждения), даже в сочетании с макросами, другими словами, некоторый набор предупреждений. -Wpedantic Выдать все предупреждения, требуемые строгими ISO C и ISO C ++; отклонить все программы, которые используют запрещенные расширения, и некоторые другие программы, которые не соответствуют ISO C и ISO C ++. -Wextra более полный тест собираемого кода, включены максимально возможные предупреждения, но некоторые предупреждения задаются только индивидуально. -Weverything специфический флаг компилятора clang, после его включения иногда возникает желание удалить написанное :) -Werror -pedantic-errors -Werror выдает ошибку, когда обнаружено предупреждение, переводит warnings в errors, -pedantic-errors соответственно делает тоже самое, только для предупреждений доступных в -pedantic mode. Историческая справка: Первоначально C-функции не имели прототипов, поскольку C эволюционировал из B, беспристрастного языка :) . Когда были добавлены прототипы, оригинальные декларации без текста были оставлены на языке для обратной совместимости.

Исключения и странное наследование

#java


Всем привет! Заметил такую вещь:


Как все мы прекрасно знаем существует 2 вида Exception:


Checked
Unchecked


Вопрос: Почему RuntimeException (unchecked), наследуется от класса Exception, который
является (checked)?

UPGRADE:   Исключения необходимо явно объявлять с помощью оператора throws,  объявляются
все исключения, кроме RuntimeException и Error потому что они UNCHECKED? Как понять
явно объявлять?
    


Ответы

Ответ 1



Короткий ответ Для того, чтобы конструкция: catch(Exception e) { ... } отлавливала и RuntimeException. Длинный ответ @Grundy уже объяснил, что RuntimeException явно прописан в спецификации и то, что данная иерархия была выбрана авторами Java и только они смогут достоверно объяснить, почему был выбран именно такой вариант, а не какой-либо другой. Я попробую объяснить почему выбранная иерархия имеет смысл. Error и Exception Начнем издалека. Есть базовый класс Throwable. Все методы, связанные с выбрасыванием исключений, определены в нем. Его наследники, что Error, что Exception, не объявляют никаких специфичных методов. По сути это один и тот же класс продублированный с разными названиями. Такая иерархия явно отделяет фатальные системные ошибки (Error) от нефатальных исключений (Exception). В теории, разработчик должен обрабатывать только исключения, а при возникновении ошибок, программа должна прекращать работу. Например, такой блок try: try { //какой-то код } catch(Exception e) { //обрабатываем исключение } Нормально обработает NullPointerException и продолжит работу. Но если возникнет OutOfMemoryError, то исполнение прекратится и ошибка будет выброшена на уровень выше. Проверяемые исключения Разработчики Java приняли решение, довольно спорное, что код должен явно обрабатывать все исключения, которые могут возникнуть. Т.е. если код обращается к методу, который может выбросить исключение, то его нужно либо явно обработать с помощью блока try-catch: try { //метод, который может выбросить IOException throwingMethod(); } catch(IOException e) { //явно обрабатывается } либо явно объявить в вызывающем методе с помощью throws: //Так мы объявляем всему миру, что наш метод опасный //и может выбросить исключение. //Любой код, который вызовет метод, должен будет либо //обработать исключение, либо передать дальше. void myMethod() throws IOException { throwingMethod(); } Если не сделать ни того, ни другого, то код не скомпилируется. Error — не проверяется, т.к., во-первых, Error не имеет смысла обрабатывать в большинстве случаев, и, во-вторых, ошибки обычно не относятся к какому-то конкретному методу (переполнение памяти может возникнуть в любой момент). Непроверяемые исключения Отдельные виды исключений потенциально очень широко распространены. Например, NullPointerException потенциально может возникнуть при вызове почти любого нестатичного метода: void method(MyClass obj) { //здесь может быть NPE obj.method(); //и здесь может быть NPE obj.getField().method(); //и здесь showMessage(obj.toString()); } Явная обработка NullPointerException привела бы к очень неудобному коду, с огромным количеством try catch и throws. Поэтому для таких исключений пришли к компромису и объявили класс RuntimeException. (Вопрос о том, какие исключения должны наследоваться от RuntimeException — довольно спорный. Есть рекомендации использовать непроверяемые исключения для программных ошибок, но на практике выбор может оказаться сложным) RuntimeException наследуется от Exception потому-что непроверяемое исключение это все еще исключение, а не ошибка: его можно обработать и оно всегда зависит от кода, а не от сторонних, системных, факторов. И если разработчик решит обрабатывать все исключения: catch(Exception e) { //обработка } , то должны обрабатываться и возникшие RuntimeException. Можно ли было сделать по-другому? Да, но есть нюансы. Рассмотрим варианты. Вариант 1. Разные классы-наследники для проверяемых и непроверяемых исключений. Можно было бы создать отдельные базовые классы для проверяемых и непроверяемых исключений: Exception RuntimeException CheckedException Но в этом случае возникает вопрос: должен ли быть проверяемым Exception и его наследники? Если да, то класс CheckedException теряет смысл и получаем существующую иерархию. Если нет, то теряет смысл RuntimeException. Тогда получаем второй вариант Вариант 2. Проверяемые исключения наследуются от непроверяемых Exception (unchecked) CheckedException (checked) Такая иерархия вполне допустима, но разработчики Java решили, что базовый класс должен быть проверяемым. Это соответствует выбранной философии: исключения должны быть проверяемыми по-умолчанию и непроверяемыми только если их повсеместная проверка вызывает затруднения. С этой философией не все согласны, но имеем, что имеем. Итог Почему RuntimeException (unchecked), наследуется от класса Exception, который является (checked)? По следующим причинам: Проверяемость тех или иных классов явно прописана в спецификации и не связана с какими-либо свойствами классов-исключений. Наследование имеет чисто организационный/иллюстративный характер и не связано с наследованием свойств/методов. Проверяемое исключение — это исключение, а не ошибка, и должно обрабатываться как исключение. Разработчики Java приняли решение, что базовый класс исключений должен быть проверяемым.

Ответ 2



Это явно определено в спецификации, section 11.1.1: RuntimeException and all its subclasses are, collectively, the runtime exception classes. The unchecked exception classes are the runtime exception classes and the error classes. The checked exception classes are all exception classes other than the unchecked exception classes. That is, the checked exception classes are all subclasses of Throwable other than RuntimeException and its subclasses and Error and its subclasses. Выше в спецификации явно указывается, что checked exceptions – это все подклассы Throwable за исключением RuntimeException, Error и их подклассов. То есть RuntimeException и Error – это просто особые случаи для компилятора. Перевод ответа @JonSkeet

Ответ 3



RuntimeException и Error можно не проверять явно, т.е. они не должны быть обёрнуты в try-catch или методы не должны содержать throws для компиляции, в то время как обычные Exception обязаны обрабатываться с помощью try-catch или передаваться через throws. void neverCompiled(){ throw new Exception(); } void compiled(){ try{ throw new Exception(); } catch(Exception e){} } void compiled(){ throw new RuntimeException(); } class Complex { void compiled1() throws Exception { throw new Exception(); } void compiled2() { try{ compiled1(); } catch(Exception e){} } }

Как разобраться со всем разнообразием библиотек, framework'ов и т.д. для C++ на Windows

#windows #winapi #net #qt #cpp


Есть знания C++. Допустим, надо написать программу. Можно ее написать разными способами.
Можно с помощью Qt, можно через Win32, можно через MFC и т.д. Зависит от того, что
будет делать программа. Но для этого надо знать какие есть способы и что они могут.

Какие есть самые популярные способы? Я в этом всем запутался. 
.NET для C++. Как конкретно этот способ называется? C++/CLI? 
Эти способы обычно совмещаются? Допустим есть библиотека, которая решают что-то одно,
например, графические библиотеки GTK/FLTK/wxWidgets. А если этого не достаточно, то
чем то дополняют или используют тот способ, в котором есть все что нужно?
Можете описать задачи, которые выполняют эти способы(хотя бы сказать про самые популярные
типа Qt, Win32)? В описании этих способов все как-то расплывчато написано. Для меня
пока это не очень понятно.
Framework отличается от библиотеки тем, что он имеет более широкую функциональность? 
Мне Qt нравится тем, что там много чего есть, хотя я им не разу пользовался. Но я
не знаю востребован ли Qt на рынке труда. Не могу пока определиться, что лучше учить.
Qt, Win32 или еще что-то.
    


Ответы

Ответ 1



Что подразумевается под способами? Вы уже выбрали язык. Можете разве что выбрать ту среду, в которой будете работать, и компилятор, которым собирать, а еще библиотеки, с помощью которых уменьшите объем работы. Да, C++/CLI. Еще раз. Это не способы. Вы назвали библиотеки. Действительно, можно писать на голом С++, но это очень трудоемко. И программы будут консольные. Сейчас это уже для большинства задач неприменимо. Получается, что придется все равно использовать какие-то вызовы для конкретных платформ - Windows, Linux. Уже получается какая-то привязка к определенной ОСи и ее интерфейсам. Вот смотрите. В Windows существует минимум три интерфейса, с помощью которых можно что-либо сделать: Native API — самые низкоуровневые вызовы. Идут через библиотеку NTDLL.DLL в само ядро. Win32 API — обертка вокруг Native API для обеспечения работоспособности классических Windows-программ. Обеспечивает работу граф. интерфейса, сеть и пр. Работа Win32API обеспечивается подключением кучи библиотек, но основные — это KERNEL32.DLL, USER32DLL и GDI32.DLL MFC — библиотека, разработанная M$ для облегчения участи программистов, т.к. писать на голом WinAPI весьма грустно. Очень много вызовов, Win32 API не объектноориентирован в категориях С++ и пр. Читайте описание. Задачи разные. Native API разрабатывался для обеспечения работы самой операционной системы. Поэтому с его помощью можно творить такие вещи, которые с помощью других интерфейсов нереально сделать. Правда приходится расплачиваться двумя моментами: недокументированностью и сложностью использования. Никто не гарантирует, что в следующей версии ОС этот интерфейс кардинально не изменят. WinAPI — это сложившийся интерфейс для написания графических приложений под среду Windows. Qt и MFC — это еще более высокоуровневые библиотеки, в которых есть множество модулей, начиная от работы с графическим интерфейсом (причем это проще, чем работать напрямую через WinAPI), кончая работой с конкретными типами файлов (аудио-видео) и сетью. Понятно, что MFC под линукс нет. И если вы хотите кроссплатформенное приложение, то нужно использовать библиотеки, которые более широко распространены. Тот же Qt. Задача какая? Просто писать программу, не отвлекаясь на специфику каждой из платформ. Да и Qt сам по себе более мощен, чем MFC, но это на мой взгляд. Никто не мешает взять, да и самому произвести сравнительный анализ. Framework по отношению к .net подразумевает, что это не просто библиотека, а среда. Среда выполнения программ. И, в принципе, существуют совместимые с .net среды, вроде проекта mono. А библиотека - ну, она одна. Или их несколько. Они реализуют какой-то функционал. Но он не такой всеобъемлющий как в framework'е, который предоставляет функции практически на все случаи жизни. И есть более мелкие отличия — как-то, что фреймворк имеет свой формат исполняемых файлов, свой загрузчик таких файлов и пр. Хорошая блок-схема, разъясняющая взаимоотношения между разными интерфейсами:

B-TREE и HASH индексы

#sql #mysql


Привет. Интересует что такое B-TREE, HASH индексы? Как они влияют на ускорение выборки?
Каков их синтаксис?    


Ответы

Ответ 1



B-tree он же balanced tree индекс, это индекс сгруппированный по листьям сбалансированного дерева. Применяется для больших индексов, по сути это индекс индексов. Ну скажем индексы с величиной от 1 до 10 хранятся в одной ветке, от 11 до 20 в другой и т.д., когда приходит запрос на индекс с номером 35, идем к 3-й ветке и находим там 5-й элемент. В общем как то так. Подробнее здесь Hash индекс применяется для сравнения/построения индексов строчных и/или двоичных данных. Каждому значению индексируемого выражения сопоставляется значение определенной хэш функции отображающей исходное значение на целое число (иногда на строку). Подробнее здесь B-Tree индекс дает скорость выборки порядка log(N), hash дает линейную. В реальной жизни hash и B-Tree применяются совместно, то есть для вычисления значений B-Tree индекса все равно применяются хэши.

Ответ 2



Пусть у вас есть таблица goods ( id int primary key, catalog_id int, sales_from datetime, sales_till datetime ) Если у вас Engine InnoDb или MyISAM, то первичный индекс только BTREE, если тип таблицы MEMORY, то первичный индекс по умолчанию HASH, но можно назначить и BTREE. Для типа ndb - можно использовать как BTREE, так и HASH Если у вас частые запросы, типа: SELECT * FROM goods WHERE catalog_id = ... то имеет смысл сделать индекс HASH по полю catalog_id Если вы хотите найти товары, на которые в данный момент существуют скидки, т.е. условие: WHERE sales_from > NOW() AND sales_till < NOW() то тут однозначно надо использовать индекс BTREE

Как создать (добавить) директорию?

#git #bitbucket #git_add


Первый день работаю с GIT (BitBucket).

Возник вопрос как создать и закоммитить директорию.
Добавил в основную папку проекта (на локале) директорию. Потом в терминале написал:

git add *
git push


и в итоге ничего не изменилось. Подскажите, что я делаю не так?
    


Ответы

Ответ 1



Чтобы git сохранил директорию: Она должна быть непустой. Нужно добавить её содержимое и сделать коммит. 1. Директория должна быть непустой Добавить пустую директорию нельзя. Команда git add «видит» только файлы и пути, в которых они лежат. Чтобы сохранить пустую директорию, создайте в ней пустой файл .gitkeep и добавьте его в git. Почему git так работает? Потому что он хранит данные в виде следующих объектов: Blob, блоб (от Binary Large OBject) — бинарный архив файла. Tree, дерево — текстовый список содержимого директории: файлов (блобов) и других директорий (деревьев). Дерево не может быть пустым — by design, так устроен алгоритм. Поэтому пустую директорию нельзя добавить. Чтобы её наполнить, достаточно создать в ней файл. Обычно такой файл называют .gitkeep, но допустимо любое имя. Например, в директории есть файл и ещё пара директорий, одна из которых пуста. Вот что будет после команды git add .: на диске: в индексе git: . tree ├── dir1 tree │   └── file1 blob ├── dir2 (ничего) └── file2 blob Чтобы узнать про другие объекты и подробности, читайте Git from the bottom up. 2. Нужно сделать коммит Команда git add только добавляет файлы в индекс, после чего нужно сохранить их командой git commit. Для добавления используйте git add ., а не *.

Ответ 2



Добавьте файл .gitkeep (или любой другой) в Вашу директорию которая должна быть закомичена

Ответ 3



чтобы добавить подпапку (с файлами проекта) на контроль версий: git add folder_name/ далее что вам нужно, git commit. git push....

Двоеточие в определении конструктора

#cpp #конструктор


У меня возникла проблема с пониманием синтаксиса. Увидел вот такой конструктор: 

foo(char *msg) : msg(msg) { ..... }


Что значит двоеточие после аргумента в первых скобках?
    


Ответы

Ответ 1



Область кода за двоеточием и до начала тела конструктора называется инициализатором конструктора. Используется как для инициализации членов класса, так и для вызова конструктора базового/базовых классов, т.е. по сути, инициализации базовой составляющей. Также здесь может быть вызов другого конструктора текущего класса (делегирование конструкторов, начиная с c++11). Без инициализатора конструктора не обойтись, если в классе присутствует член ссылочного типа или константа, или член класса, у которого нет конструктора по умолчанию: struct S { S(int) {} // Конструктор с параметром. Не является конструктором по умолчанию. }; class B { public: B(int i) : i(i), r(i), s(i) { // this->i = i; // Ошибка. Нельзя присваивать константе. // r = i; // Ошибка. Не является инициализацией ссылки. // s = S(i); // Ошибка. `s` должен быть создан в инициализаторе конструктора. } private: const int i; int& r; S s; }; т.к. код, начинающийся за фигурной скобкой будет приводить уже не к инициализации члена, а к присваиванию ему значения, чего нельзя сделать для упомянутых ранее ссылок или констант. Дополнительно стоит заметить (как было упомянуто в комментарии Monah Tuk), что если член-класса не инициализирован явно в инициализаторе конструктора и при этом ему присваивается значение в теле конструктора, то он сначала будет инициализирован конструктором по умолчанию (или инициализацией в определении класса (c++11)): class D { int i = 42; // Инициализация члена в определении класса }; После чего выполнится присваивание в теле конструктора. Для сложных классов это может приводить к дополнительным расходам. Т.о. инициализацию членов стоит производить либо в определении класса, либо в инициализаторе конструктора. Не стоит пытаться сделать нечто подобное непосредственно внутри тела конструктора.

Ответ 2



class foo { public: foo(int a) : a(a) {} // эквивалентно foo(int a) { this->a = a; } private: int a; } Это вообще-то база синтаксиса определения конструкторов в плюсах. Еще может быть: class foo : public bar { public: foo(int a) : bar(a) { ... } } В этом случае, после двоеточия вызывается конструктор базового класса. ЕМНИП (могу ошибаться), в случае базового класса это единственный верный способ его вызова, т.е. написать вот так: foo(int a) { bar(a); ... } будет неверно. П.С. Думаю, ответ alexolut более полон, чем мой.

Ответ 3



Это использование списка инициализации (Initialization List) для конструктора. Эта запись эквивалентна строчке кода в начале конструктора this->msg = msg;

Как использовать несколько view в RecyclerView

#android #android_view #recyclerview


Мне нужно в зависимости от типа объекта(записи), заполнить RecyclerView разными view,
раньше когда я делал это с помощью ListView у меня получалось, а тут я даже не знаю
как это организовать, в onCreateViewHolder сразу создается view но без учета текущей
позиции, следовательно я не могу несколько разных view там создать.

Как это можно организовать?
    


Ответы

Ответ 1



Для создания айтемов разного вида необходимо переопределить метод адаптера getItemViewType(), который будет в зависимости от условия определять, айтем какого вида требуется в данной позиции. В RecyclerView этот механизм был улучшен и метод onCreateViewHolder() возвращает значение ViewType - какой тип айтема требуется в данной позиции: class MyAdapter extends RecyclerView.Adapter { private final int TYPE_ITEM1 = 0; private final int TYPE_ITEM2 = 1; public MyAdapter () { // конструктор адаптера } @Override public ItemHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v; switch (viewType) { // инфлейтим нужную разметку в зависимости от того, // какой тип айтема нужен в данной позиции case TYPE_ITEM1: v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item1, parent, false); break; case TYPE_ITEM2: v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item2, parent, false); } return new ItemHolder(v); } @Override public void onBindViewHolder( ItemHolder holder, int position) { // Получаем тип айтема в данной позиции для заполнения его данными int type = getItemViewType(position); switch (type) { case TYPE_ITEM1: holder.mText1.setText("биндим данные в айтем типа 1"); break; case TYPE_ITEM2: holder.mText2.setText("биндим данные в айтем типа 2"); break; } } @Override public int getItemViewType(int position) { // условие для определения айтем какого типа выводить в конкретной позиции if (position == <условие>) return TYPE_ITEM1; return TYPE_ITEM2; } public static class ItemHolder extends RecyclerView.ViewHolder{ TextView mText1; TextView mText2; public ItemHolder(View v) { super(v); mText1 = (TextView) v.findViewById(R.id.text1); mText2 = (TextView) v.findViewById(R.id.text2); } }

Ответ 2



Можно писать свой адаптер, как ответил @pavlofff, можно воспользоваться готовыми библиотеками: https://github.com/vivchar/RendererRecyclerViewAdapter https://github.com/sockeqwe/AdapterDelegates

Ответ 3



У кого остались вопросы по примеру, приведённому выше, (или кто-то хочет более детально разобраться), в дополнение к примеру ссылку Вам!

Может ли бот Telegram посылать оповещения при определённых событиях?

#telegram_bot


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


Ответы

Ответ 1



То, что Вам нужно - это обычный POST/GET запрос вида https://api.telegram.org/bot/sendMessage?chat_id=1234567&text=HelloBot где: - токен Вашего бота; chat_id - идентификатор того чата, куда вы хотите послать сообщение; text - текст самого сообщения. Вешаете на нужное событие этот запрос, передавая соответствующие chat_id и text, получаете профит. Важно понимать, что если бот заблокирован пользователем (попросту говоря - удалён), то запрос вернёт ошибку и пользователь сообщение не получит. При этом если у бота не реализована обработка исключений, то он упадёт.

Как правильно называть имена классов в Java?

#java #android #android_fragment


Как правильно называть имена классов в Java?

Есть у меня к примеру список классов. Удобнее читать так?

FragmentInternalCapitalization
FragmentWordsRunTogether
FragmentWordsStrungTogether
FragmentCamelHumpedWord
FragmentInternalCapitalization
FragmentWordsRunTogether
FragmentWordsStrungTogether
FragmentCamelHumpedWord


Или все так так?

InternalCapitalizationFragment
WordsRunTogetherFragment
WordsStrungTogetherFragment
CamelHumpedWordFragment
InternalCapitalizationFragment
WordsRunTogetherFragment
WordsStrungTogetherFragment
CamelHumpedWordFragment


Половина кода у меня так половина так)).  Это чем то похоже на 

Strings str[] и Strings []str

Конечно для коротких названий без разницы. А если названия будут из 3-5 слов.

Только не пишите как хочешь). А то ведь если так то я и так могу называть классы

OneTwoFragmentThreeFour =)

Просто в iOS к примеру там все названия классов идут с переди. Типо NSString NSlalal. 
    


Ответы

Ответ 1



Надо оттолкнуться от первоисточника, а именно Google Java Style Guide - именование Android'а следуют именно этому гайду. Про именование классов написано буквально следующее: Class names are written in UpperCamelCase. Class names are typically nouns or noun phrases. For example, Character or ImmutableList. Interface names may also be nouns or noun phrases (for example, List), but may sometimes be adjectives or adjective phrases instead (for example, Readable). Смысл этой белиберды состоит в том, что именование классов обычно состоит из существительных (иногда прилагательных) образующих или слова или фразу в виде CamelCase Возвращаясь к вопросу, ни один из представленных вариантов (на мой английский) не удовлетворяет критериям фразы, посему я бы таки рассмотрел такие варианты: FirstFragment SecondFragment ThirdFragment FourthFragment

Ответ 2



Стиль, как FragmentOne кажется удобнее тем, что в списке файлов классы будут сгруппированы по принадлежности, то есть сначала идут все активити, потом фрагменты и тд, так их удобнее искать в дереве проекта, если таких файлов достаточное количество. Однако, на самом деле, в поиске класса по имени не первостепенно важно, кто его родитель, более важно предназначение, которое выражается через собственное осмысленное имя. Если появляется необходимость в группировке по другому признаку, то обычно это делается через поддиректории в основной директории с кодом, как src/../service (то есть выделение в пакет), как в эталонном приложении Google I/O Sched Для расширяющих классов принято писать имя родительского класса в конце, как AppCompatActivity, ListFragment и тд. С точки зрения стиля Android правильным будет второй вариант

Ответ 3



Придерживаться единого стиля и смысловой нагрузки В Вашем случае FragmentOne - фрагмент один, OneFragment - один фрагмент, поэтому FragmentOne будет логичнее.

Как правильно скрыть часть текста?

#javascript #jquery


Здравствуйте. Помогите, пожалуйста, поправить ошибки в скрипте, который скрывает
часть длинного текста. А именно, если в тексте есть параграфы, то он их игнорирует
или скрывает после первого, или еще любым другим способом как ему угодно. Если текст
идет сплошной, без параграфов, то скрипт работает правильно. И еще один момент, как
научить скрипт, чтобы он скрывал текст после точки, а не в средине предложения? Спасибо!



(function($) {
  $.fn.truncate = function(options) {

    var defaults = {
      length: 100,
      minTrail: 10,
      moreText: "",
      lessText: "",
      ellipsisText: ""
    };

    var options = $.extend(defaults, options);

    return this.each(function() {
      obj = $(this);
      var body = obj.html();

      if (body.length > options.length + options.minTrail) {
        var splitLocation = body.indexOf(' ', options.length);
        if (splitLocation != -1) {

          var splitLocation = body.indexOf(' ', options.length);
          var str1 = body.substring(0, splitLocation);
          var str2 = body.substring(splitLocation, body.length - 1);
          obj.html(str1 + '' + options.ellipsisText +
            '' + '' + str2 + '');
          obj.find('.truncate_more').css("display", "none");

          obj.append(
            ''
          );

          var moreLink = $('.truncate_more_link', obj);
          var moreContent = $('.truncate_more', obj);
          var ellipsis = $('.truncate_ellipsis', obj);
          moreLink.click(function() {
            if (moreLink.text() == options.moreText) {
              moreContent.show('normal');
              moreLink.text(options.lessText);
              ellipsis.css("display", "none");
            } else {
              moreContent.hide('normal');
              moreLink.text(options.moreText);
              ellipsis.css("display", "inline");
            }
            return false;
          });
        }
      }

    });
  };
})(jQuery);

$().ready(function() {
  $('.story').truncate({
    length: 20,
    minTrail: 10,
    moreText: 'Подробнее',
    lessText: 'Скрыть',
    ellipsisText: "[...]"
  });
});
.story {
  margin: 0 0 20px 0;
}

В этом блоке нет параграфов. Однажды осенью матушка варила в гостиной медовое варенье, а я, облизываясь, смотрел на кипучие пенки. Батюшка у окна читал Придворный календарь, ежегодно им получаемый. Эта книга имела всегда сильное нљ него влияние: никогда не перечитывал он ее без особенного участия, и чтение это производило в нем всегда удивительное волнение желчи. Матушка, знавшая наизусть все его свычаи и обычаи, всегда старалась засунуть несчастную книгу как можно подалее, и таким образом Придворный календарь не попадался ему на глаза иногда по целым месяцам. Зато, когда он случайно его находил, то, бывало, но целым часам не выпускал уж из своих рук. Итак, батюшка читал Придворный календарь, изредка пожимая плечами и повторяя вполголоса: «Генерал-поручик!.. Он у меня в роте был сержантом!.. Обоих российских орденов кавалер!. А давно ли мы...» Наконец батюшка швырнул календарь на диван и погрузился в задумчивость, не предвещавшую ничего доброго.

В этом блоке есть параграфы.

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

Эта книга имела всегда сильное нљ него влияние: никогда не перечитывал он ее без особенного участия, и чтение это производило в нем всегда удивительное волнение желчи.

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

Итак, батюшка читал Придворный календарь, изредка пожимая плечами и повторяя вполголоса: «Генерал-поручик!.. Он у меня в роте был сержантом!.. Обоих российских орденов кавалер!. А давно ли мы...» Наконец батюшка швырнул календарь на диван и погрузился в задумчивость, не предвещавшую ничего доброго.


Ответы

Ответ 1



Чтобы текст скрывал после точки - надо и искать точку, а не пробел, как сейчас. Для того, чтобы использовать скрипт не только с текстом, но и с текстом включающим теги его нужно сильно модифицировать. не использовать .html();, который возвращает html в виде текста. В этом случае можно случайно разрезать тег пополам. можно рассматривать элемент с классом story и его дочерние элементы в виде дерева В таком виде можно разделить узел в котором находится искомая подстрока на то, что надо показать и то, что надо скрыть, попутно добавив служебный элемент с ..., а все соседние справа узлы поместить в элемент, который надо скрыть Примером реализации функции, которая определяет узел который надо разделять может стать следующая функция find function find(container, text, minLength) { var curIndex = 0; // обход дерева в глубину for (var nodes = Array.from(container.childNodes); nodes.length;) { var node = nodes.shift(); if (node.nodeType == Node.ELEMENT_NODE) { // если не дошли до нижнего уровня спускаемся дальше nodes.unshift(...node.childNodes); continue; } // определяем наличием искомой строки в текущем узле var index = -1; do { index = node.textContent.indexOf(text, index + 1); } while (index != -1 && curIndex + index < minLength); // если строка найдена и соблюдено условие минимальной показываемой длины if (index != -1) { curIndex += index; return [node, index]; // возвращаем найденный узел и индекс, по которому его надо разделить } else { curIndex += node.textContent.length; } } return [null, -1]; } После получения узла и точки разбиения, нужно разбить текстовое содержимое на две части: то что надо показать и то что надо скрыть. В самом узле оставить только часть которая видна. Добавить справа от текущего служебный узел с [...] Добавить справа узел со скрытым текстом. Подняться на уровень выше, и все соседние справа элементы поместить в узел отвечающий за скрытый текст повторять пункт 8 пока не поднимемся до самого контейнера - элемента с классом .story Пример конечной реализации: (function($) { $.fn.truncate = function(options) { var defaults = { length: 100, minTrail: 10, moreText: "", lessText: "", ellipsisText: "" }; var options = $.extend(defaults, options); function find(container, text, minLength) { var curIndex = 0; for (var nodes = Array.from(container.childNodes); nodes.length;) { var node = nodes.shift(); if (node.nodeType == Node.ELEMENT_NODE) { nodes.unshift(...node.childNodes); continue; } var index = -1; do { index = node.textContent.indexOf(text, index + 1); } while (index != -1 && curIndex + index < minLength); if (index != -1) { curIndex += index; return [node, index]; } else { curIndex += node.textContent.length; } } return [null, -1]; } return this.each(function() { var obj = $(this); var body = this.textContent; if (body.length > options.length + options.minTrail) { var textToFind = '.'; if (body.indexOf(textToFind, options.length) != -1) { var [node, startIndex] = find(this, textToFind, options.length); var splitLocation = startIndex + textToFind.length; var str1 = node.textContent.substring(0, splitLocation); var str2 = node.textContent.substring(splitLocation + 1); node.textContent = str1; if (str2.length) { $(node).after(`${str2}`); } $(node).after(`${options.ellipsisText}`); var oi = 0; while (node != this) { var span = $('').addClass('truncate_more'); for (var nextNode = node.nextSibling, savedNode; nextNode; nextNode = savedNode) { if (nextNode.classList && (nextNode.classList.contains('truncate_more') || nextNode.classList.contains('truncate_ellipsis'))) continue; savedNode = nextNode.nextSibling; span.append(nextNode); } node = node.parentNode; $(node).append(span); } obj.find('.truncate_more').css("display", "none"); obj.append( '' ); var moreLink = $('.truncate_more_link', obj); var moreContent = $('.truncate_more', obj); var ellipsis = $('.truncate_ellipsis', obj); moreLink.click(function() { if (moreLink.text() == options.moreText) { moreContent.show('normal'); moreLink.text(options.lessText); ellipsis.css("display", "none"); } else { moreContent.hide('normal'); moreLink.text(options.moreText); ellipsis.css("display", "inline"); } return false; }); } } }); }; })(jQuery); $().ready(function() { $('.story').truncate({ length: 20, minTrail: 10, moreText: 'Подробнее', lessText: 'Скрыть', ellipsisText: "[...]" }); }); .story { margin: 0 0 20px 0; }
В этом блоке нет параграфов. Однажды осенью матушка варила в гостиной медовое варенье, а я, облизываясь, смотрел на кипучие пенки. Батюшка у окна читал Придворный календарь, ежегодно им получаемый. Эта книга имела всегда сильное нљ него влияние: никогда не перечитывал он ее без особенного участия, и чтение это производило в нем всегда удивительное волнение желчи. Матушка, знавшая наизусть все его свычаи и обычаи, всегда старалась засунуть несчастную книгу как можно подалее, и таким образом Придворный календарь не попадался ему на глаза иногда по целым месяцам. Зато, когда он случайно его находил, то, бывало, но целым часам не выпускал уж из своих рук. Итак, батюшка читал Придворный календарь, изредка пожимая плечами и повторяя вполголоса: «Генерал-поручик!.. Он у меня в роте был сержантом!.. Обоих российских орденов кавалер!. А давно ли мы...» Наконец батюшка швырнул календарь на диван и погрузился в задумчивость, не предвещавшую ничего доброго.

В этом блоке есть параграфы.

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

Эта книга имела всегда сильное нљ него влияние: никогда не перечитывал он ее без особенного участия, и чтение это производило в нем всегда удивительное волнение желчи.

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

Итак, батюшка читал Придворный календарь, изредка пожимая плечами и повторяя вполголоса: «Генерал-поручик!.. Он у меня в роте был сержантом!.. Обоих российских орденов кавалер!. А давно ли мы...» Наконец батюшка швырнул календарь на диван и погрузился в задумчивость, не предвещавшую ничего доброго.



Зачем ставить (void) перед printf и другими функциями?

#c


Изучаю С на примере репозитория: https://github.com/ankushagarwal/nweb/blob/master/nweb23.c
И собственно вопрос - зачем ставить (void) перед printf и другими функциями?

(void)sprintf(...);

    


Ответы

Ответ 1



Функция printf не является void-функцией. Она возвращает в вызывающий код значение типа int. Таким способом - приведением возвращенного значения к типу void - автор кода дает понять компилятору (и читателю кода) то, что возвращаемое значение функции printf было проигнорировано им намеренно. Для некоторых функций игнорирование возвращаемого ими значения является нормальной ситуацией (например, printf или strcpy). Для других функций игнорирование их возвращаемого значения обычно свидетельствует о грубой ошибке в коде (например, malloc или fopen). Многие компиляторы предоставляют [нестандарные] средства для пометки функций как принадлежащих первой или второй группе. Если проигнорировать возвращаемое значение функции второй группы, то компилятор выдаст [нестандарное] диагностическое сообщение. Однако в некоторых группах разработчиков можно встретить "драконовскую" установку на то, что возвращаемые значения функций никогда нельзя просто неявно игнорировать (и компилятор конфигурируется на безусловную выдачу диагностических сообщений в таких случаях). Это довольно бессмысленное требование, но тем не менее оно встречается. И в такой ситуации, чтобы избавиться от диагностики, разработчику приходится вот таким способом явно сообщать компилятору, что значение проигнорировано намеренно.

Сборщик мусора c# не очищает память

#c_sharp


Есть статичный метод

private static bool GetXml(string date)
{
    try
    {
        //Подключаемся к сервису
        KBODataProviderClient client = new KBODataProviderClient();
        //Получаем данные
        string[] result = client.getKBOContracts("2018-12-25", null);

        //Если данных нет
        if (result == null || result.Length <= 0)
        {
            log.InfoFormat("[{0}]|Pbo return 0 rows", runGuid);
            return false;
        }

        if (File.Exists(filePath))
            File.Delete(filePath);

        //Сохраняем XML
        using (StreamWriter sw = new StreamWriter(filePath, true, Encoding.UTF8))
        {
            //пишем первую запись с заголовков
            sw.WriteLine("");
            sw.WriteLine("");
            for (int i = 0; i < result.Length; i++)
            {
                //удаляем заголовок
                string temp = result[i].Replace("", "");
                sw.WriteLine(temp);
            }
            sw.WriteLine("");
        }


        log.InfoFormat("[{0}]|Create XML success", runGuid);

        return true;

    }
    catch (Exception ex)
    {
        log.ErrorFormat("[{0}]|GetXml error {1}", runGuid, ex + Environment.NewLine
+ ex.StackTrace);
        return false;
    }
}


При вызове метода он подключается к WCF сервису, оттуда приходит массив строк (возвращает
string[]), который я потом пишу в файлик.
Проблема в том, что при получении данных разумеется съедается память, и после завершения
метода память не очищается.

Собственно, я знаю то, что переменная string[] result является временной переменной
и после завершения метода она должна собраться сборщиком мусора, но этого не происходит
(данные занимают около 400мб в памяти)

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

result = null;
GC.Collect();


и вызвать сборщик самому. Но и тут эффекта 0.
В чем проблема?

upd:

Использование метода идет так:

if (GetXml(date))
{
    //парсинг
}


сам метод дополнил так:

private static bool GetXml(string date)
{
    try
    {
        //Подключаемся к сервису
        KBODataProviderClient client = new KBODataProviderClient();
        //Получаем данные
        string[] result = client.getKBOContracts("2018-12-25", null);

        //Если данных нет
        if (result == null || result.Length <= 0)
        {
            log.InfoFormat("[{0}]|Pbo return 0 rows", runGuid);
            return false;
        }

        if (File.Exists(filePath))
            File.Delete(filePath);

        //Сохраняем XML
        using (StreamWriter sw = new StreamWriter(filePath, true, Encoding.UTF8))
        {
            //пишем первую запись с заголовков
            sw.WriteLine("");
            sw.WriteLine("");
            for (int i = 0; i < result.Length; i++)
            {
                //удаляем заголовок
                string temp = result[i].Replace("", "");
                sw.WriteLine(temp);
            }
            sw.WriteLine("");
        }

        log.InfoFormat("[{0}]|Create XML success", runGuid);

        client.Close();
        client = null;

        result = null;

        GC.Collect();

        return true;

    }
    catch (Exception ex)
    {
        log.ErrorFormat("[{0}]|GetXml error {1}", runGuid, ex + Environment.NewLine
+ ex.StackTrace);
        return false;
    }
}


Клиент сервиса я закрываю и на всякий даже null присваиваю. Далее присваиваю null
строке и вызываю сборщик.

В итоге при получении данных память увеличивается до 320мб(+- офк) и после завершения
падает до +-211 и продолжает работать.

Метод парсинга не занимает столько памяти, т.к без получения данных при парсинге
память остается на уровне +-60мб.


попробовал Array.Clear, так-же 0 эффекта (скрин выше)
    


Ответы

Ответ 1



Проблема решена. Я решил проверить все дело на левых проектах потихоньку перенося код и оказалось чудом, когда на новом проекте память очищалась нормально(требовалось лишь вызвать GC.Collect()) Первым делом я решил проверить версии .net, на проекте где все работало был 4.7.1, но изменение версии не помогло. Тогда я начал в буквальном смысле добавлять по строчке и сверять код. Причина оказалось в app.config, а именно в секции которую генерирует VS при добавлении ссылки на сервис (там указываются таймауты и прочее). Конкретно проблема была в том, что таймауты (CloseTimeout, OpenTimeout, ReceiveTimeout, SendTimeout) указывались в привязке (секция basicHttpBinding). Если я указывал эти таймауты в этой секции - память не очищалась (ни в основном, ни в каких других проектах), т.е если делал так: но если я указывал таймауты через код: client.Endpoint.Binding.CloseTimeout = new TimeSpan(0, 30, 0); client.Endpoint.Binding.OpenTimeout = new TimeSpan(0, 30, 0); client.Endpoint.Binding.ReceiveTimeout = new TimeSpan(0, 30, 0); client.Endpoint.Binding.SendTimeout = new TimeSpan(0, 30, 0); то сборщик мусора успешно собирал мусор. Чтобы сделать настройку времени из конфига я воспользовался ConfigurationManager тогда в коде я делаю так: //Получаем время, переводим его в TimeSpan (если указать их в конфиге, то память не будет очищена) TimeSpan time = new TimeSpan(TimeSpan.TicksPerMinute * int.Parse(ConfigurationManager.AppSettings.Get("timeout"))); client.Endpoint.Binding.CloseTimeout = time; client.Endpoint.Binding.OpenTimeout = time; client.Endpoint.Binding.ReceiveTimeout = time; client.Endpoint.Binding.SendTimeout = time; и сборщик мусора так-же успешно очищает память

Ответ 2



Все языки, использующие сборщик мусора, известны своей нелюбовью возвращать память системе. Проще всего с этим смириться: как правило, неиспользуемая память быстро оседает в файле подкачки и никому там не мешает. Проблемы начинаются только когда уже и файл подкачки заканчивается. Однако, есть несколько способов, позволяющих принудительно освободить память. Способ 1 - отдельный процесс. Можно вынести вызов GetXml в отдельную программу, которая сделает всю тяжелую по памяти работу и завершится. Это - самый медленный способ (он медленнее даже чем ничего не делать), но он позволяет быстро решить проблему с нехваткой памяти. Способ 2 - поточная обработка. Этот способ требует переписывания клиента так, чтобы он не возвращал не список строк, а работал с потоками ввода-вывода. Идея в том, чтобы таскать всюду за собой Stream (или PipeReader, см. библиотеку System.Threading.Pipelines), и никогда не хранить в памяти более, к примеру, четырех килобайтов данных. Достоинство этого способа - очень низкое потребление памяти если сделать всё правильно. Недостаток же - в том, что такие операции, как "разделить поток на две части" или "выкусить декларацию XML из начала документа", требуют сложных преобразований архитектуры. Так, к примеру, вполне возможно что вам не удастся получить список потоков от клиента, и вместо этого придется подписываться на событие. Это может выглядеть как-то так: using (StreamWriter sw = new StreamWriter(filePath, true, Encoding.UTF8)) { sw.WriteLine(""); sw.WriteLine(""); var client = new KBODataProviderClient(); client.ProcessResult += stream => { sw.Flush(); stream.Position += Encoding.UTF8.GetByteCount(""; stream.CopyTo(sw.BaseStream); }; client.getKBOContracts("2018-12-25", null); sw.WriteLine(""); } Способ 3 - неуправляемая память Этот способ также требует переписывания клиента. Он может оказаться как проще поточной обработки (за счет более простых алгоритмов), так и сложнее (за счет более сложного API). Недостатком этого способа является непереносимость, так как он завязывается на конкретное API платформы. Нужно выделить кусок неуправляемой памяти через P/Invoke (лучше всего использовать пару функций VirtualAlloc/VirtualFree - они уж точно вернут память системе), и (старых подход) либо обернуть ее в SafeBuffer и дальше в UnmanagedMemoryAccessor + UnmanagedMemoryStream, либо (новый подход) обернуть ее в MemoryManager из библиотеки System.Memory и далее работать с Memory

Ответ 3



Поведение сборщика мусора недетерминировано и как следствие он запускается тогда, когда считает нужным. Это сделано в целях оптимизации, так как сборка мусора- это ресурсоемкая операция: Надо найти объекты без ссылок Затем освободить память Выполнить сжатие(дефрагментацию) кучи В вашем же случае явного вызова GC.Collect(); память все таки должна очищаться, если вы работаете с управляемыми ресурсами и нету никаких утечек. Память может не очищаться из-за того, что программа запущена в режиме отладки и в этом режиме переменным проливается жизнь, что логично, ведь мусор может собираться еще до выхода из метода, если видно, что переменная не видна, но во время отладки мы можем наблюдать за переменными и как следствии было бы неудобно, если бы из Watch'ей переменные пропадали во время отладки. Попробуйте запустить в режиме релиза со всевозможными оптимизациями. Подробнее об этом написано тут. Так же, вроде, Рихетр писал, что сборщик мусора может не сразу отдавать память системе, так как может предполагать, что она понадобится в ближайшее время.

Почему inline-функции, определённые в заголовочных файлах не дублируются при линковке?

#cpp #функции #inline #линковка


Я прочёл такой факт насчёт "обычных" и inline- функций:


  В предыдущих главах мы не раз говорили, что вы не должны определять функции в заголовочных
файлах, так как если вы подключаете один заголовок с определением функции в несколько
файлов .cpp, то определение функции также будет скопировано несколько раз. Затем, при
соединении файлов, линкер выдаст ошибку, что вы определяете одну и ту же функцию больше
одного раза.
  
  Однако, встроенные функции освобождаются от этого правила, так как дублирования
в исходном коде не происходит — определение функции одно, и никакого конфликта при
соединении линкером файлов .cpp возникнуть не должно.


Почему нельзя определять функции в header'е, понятно: произойдёт "multiple definition"
при линковке нескольких модулей, включающих этот хэдер. Но из текста совсем не понятно,
почему это правило не работает для inline-функций. Я понял это так: "inline-функции
не дублируются, потому что они не дублируются". Бред.

Я, конечно, могу принять во внимание этот факт как должное, и всё, но всё таки хочется
понять, почему так происходит, а исчерпывающего объяснения этот текст не даёт.
    


Ответы

Ответ 1



Если inline-функция реально была заинлайнена, то её как бы и нету, поэтому она не может дублироваться в принципе. Если же она не была заинлайнена после оптимизаций, то она получает уникальное имя в каждой единице трансляции, в итоге мы имеем много функций, которые делают одно и то же, но имеют разные имена. Так как имена в итоге разные, multiple definition не происходит. Подробнее читать здесь: Стандарт C++11, 3.2.5 (One definition rule), 7.1.2 (Function specifiers). Где взять стандарт C++?

Ответ 2



Если спецификация языка говорит, что ошибки быть не должно, значит ошибки быть не должно. А дальше уже начинаются детали реализации. Почему вы решили, что они не дублируются? В классической реализации инлайновые функции с внешним связыванием для которых компилятор при компиляции нескольких единиц трансляции решил сгенерировать тела, разумеется, дублируются. В процессе компиляции каждая единица трансляции получает свою копию такой функции в своем объектном файле с одним и тем же именем. Однако такие функции в объектном файле помечены особым образом - так, чтобы при обнаружении множества копий одного и тот же внешнего символа при линковке линкер не выдавал ошибки, а наоборот молча удалял все копии, оставляя только одну. То есть компилятор C++ генерирует "свалку" одинаково именованных функций, раскиданных по разным объектным файлам, а линкер потом собирает всё вместе и занимается чисткой этой "свалки". В компиляторах семейства *nix эта пометка - это обозначение экспортируемого символа, как т.наз. "слабого" (weak) символа. В компиляторе MSVC++ существует аналогичная пометка selectany. Линкеры выдают ошибку множественного определения только если встретят два или более одинаковых "сильных" символа в процессе линковки. Если же "сильный" символ только один (а остальные "слабые"), то побеждает "сильный" символ, а "слабые" символы отбрасываются. Если "сильного" символа нет вообще, а есть только "слабые", то побеждает один (какой-то) из "слабых". Никакой ошибки при этом не рапортуется. Когда компилятор решает сгенерировать тело для inline-функции с внешним связыванием, он просто помечает соответствующий символ для линкера как "слабый" - и все. (На этом же механизме построена трансляция шаблонных функций, которые, как известно, тоже определяются в заголовочных файлах и тоже порождают свои копии во всех объектных файлах, которые потребовали их инстанцирования.) Например, скомпилировав вот такой простой исходник в объектный файл inline void bar() {} void (*foo())() { return bar; } и просмотрев содержимое этого объектного файла при помощи nm мы увидим 0000000000000000 W _Z3barv 0000000000000000 T _Z3foov Буковка W помечает "слабый" символ, а буковка T - "сильный" символ. Во всех объектных файлах, в которых сгенерировалось тело для такой inline функции, она будет фигурировать под одним и тем же именем _Z3barv с пометкой W. Обратите внимание, что ни о каком решении этой проблемы через генерацию множества функций с разными именами не может быть и речи: в всех остальных отношениях инлайновая функция с внешним связыванием должна вести себя так же как и любая другая функция с внешним связыванием, т.е., например, она обязана иметь один и тот же адрес во всех единицах трансляции. Побочным эффектом такого подхода является то, что "классический" подход к формированию объектного файла, в котором у функции нет начала и конца, а есть только точка входа, становится неприемлем. Для того, чтобы иметь возможность исключать функции из объектного файла, С++ компиляторы вынуждены формировать тела функций в объектном файле в компактном виде. Существуют исторические примеры альтернативных реализаций, которые пытались действовать по-другому. "Многопроходные" реализации вообще не порождали тел для инлайновых и шаблонных функций на первом проходе компиляции. Они выполняли предварительную линковку, на которой собирали информацию о том, каким функциям действительно нужны тела, затем снова вызывали компилятор и компилировали уникальные тела для таких функций, и затем уже выполняли финальную линковку. Но среди популярных компиляторов (GCC/Clang/MSVC) такой подход не прижился.

Python 3 и русские символы: print('Всем привет!') ведёт к UnicodeEncodeError: 'ascii' codec can't encode… ошибке

#python #linux #кодировка #python_3x


Пишу обычную программу print('Всем привет!') и запускаю python3.4 main.py, а в ответ:


  Traceback (most recent call last):
  File "main.py", line 1, in 
      print('\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442!')
  UnicodeEncodeError: 'ascii' codec
  can't encode characters in position
  0-3: ordinal not in range(128)


Система:


  webapp: ~/Applications $ uname -a
  Linux webapp 3.13.0-29-generic 
  
  53-Ubuntu SMP Wed Jun 4 21:00:20 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

    


Ответы

Ответ 1



В начале файла добавьте: #!/usr/bin/env python # -*- coding: utf-8 -*- # vim:fileencoding=utf-8

Ответ 2



Питон использует кодировку терминала для печати, которая не имеет никакого отношения к sys.getdefaultencoding(). Иногда переменные окружения, определяющие язык, такие как LANGUAGE, LC_ALL,LC_CTYPE, LANG могут быть не установлены, например, в окружении используемом ssh, upstart, Salt, mod_wsgi, crontab, etc. В этом случае используется C (POSIX) локаль, которая использует ascii кодировку, что приводит к UnicodeEncodeError ошибке, т.к. русские буквы не представимы в ascii. В Python 3.7, реализована PEP 540: Forced UTF-8 Runtime Mode: utf-8 режим включается по умолчанию для "C" локали. Серверные варианты Linux могут использовать C локаль по умолчанию. Настольные инсталяции дитрибутивов Linux обычно имеют utf-8 локаль. Ошибка в вопросе связана с Питон багом: Python 3 raises Unicode errors with the C locale. Разработчики решили следовать ascii кодировке из C локали, даже если это ошибка в подавляющем большинстве случаев, но в Python 3.7 ситуация может улучшится в поведении по умолчанию, см. PEP 538 -- Coercing the legacy C locale to a UTF-8 based locale. Чтобы временно изменить используемую кодировку можно определить PYTHONIOENCODING: $ PYTHONIOENCODING=utf-8 python your-script.py В качестве более постоянного решения, нужно убедиться что используется utf-8 локаль в окружении, которое запускает python. Не обязательно русскую локаль устанавливать, чтобы напечатать русский текст. В этом достоинство Юникода, что можно работать с многими языками одновременно. Например, существует C.UTF-8 локаль. Если locale -a не показывает ни одной utf-8 локали, то на Debian системах следует установить locales пакет и выбрать utf-8 локаль для использования: root# aptitude install locales root# dpkg-reconfigure locales Индивидульно, каждый пользователь может настроить переменные окружения (см. Не сохраняются переменные XUBUNTU ), например: export LANG=en_US.UTF-8 Используется первая доступная переменная из списка: LC_ALL, LC_CTYPE, LANG. Может потребоваться сконфигурировать для работы с utf-8 индивидуальные программы, такие как xterm, Gnome Terminal, screen, отдельно.

Отличия переопределения метода от перекрытия

#c_sharp #классы #функции #виртуальная_функция


Чем отличается перекрытие от переопределения метода?
    


Ответы

Ответ 1



Перекрытие (в ваших терминах) означает, что метод в производном классе скрывает метод с той же самой сигнатурой в базовом классе. Поэтому в C# рекомендуется для методов производных классов, которые перекрывают методы базовых классов, использовать ключевое слово new. Если вы забудете это сделать, то компилятор вас предупредит, что вы скрываете одноименную функцию с той же сигнатурой в базовом классе. Например, class Base { public void Hello() { Console.WriteLine( "I'm the base class" ); } } class Derived : Base { public new void Hello() { Console.WriteLine( "I'm the derived class" ); } } В этом случае для ссылок на базовый класс Base всегда будет вызываться метод Hello базового класса, а для ссылок на производный класс будет вызываться метод Hello производного класса. Например, Base b = new Base(); b.Hello(); // I'm the base class Derived d = new Derived(); d.Hello(); // I'm the derived class b = d; b.Hello(); // I'm the base class Переопределение применяется к виртуальным функциям. В производном классе виртуальная функция переопределяется. Для этого в базовом классе функция должна быть определена с ключевым словом virtual (или abstract), а в производном классе для ее переопределения необходимо указывать другое ключевое слово override. Например, class Base { public virtual void Hello() { Console.WriteLine( "I'm the base class" ); } } class Derived : Base { public override void Hello() { Console.WriteLine( "I'm the derived class" ); } } Различие с предыдущим примером состоит в том, что если ссылка базового класса указывает на объект производного класса, то в этом случае будет вызвана переопределенная функция производного класса. Base b = new Base(); b.Hello(); // I'm the base class Derived d = new Derived(); d.Hello(); // I'm the derived class b = d; b.Hello(); `// I'm the derived class` То есть различие состоит в этих двух предложениях. В первом случае для перекрытия b = d; b.Hello(); // I'm the base class ^^^^^^^^^^^^^^^^^^ а во втором случае при переопределении имеем b = d; b.Hello(); `// I'm the derived class` ^^^^^^^^^^^^^^^^^^^^^^^^ То есть в первом случае мы в каждом новом производном классе в иерархии классов объявляем новую функцию, которая скрывает функцию с той же сигнатурой в базовых классах. А во втором случае функция с тем же именем заново не объявляется, а переопределяется уже объявленная функция базового класса. Это позволяет динамически вызывать нужное определение одной и той же функции в зависимости от объекта, с которым функция вызывается.

Ответ 2



Переопределение виртуального метода базового класса — это создание метода в порождённом классе, имеющим точно такую же сигнатуру (и отмеченным специальным ключевым словом). При этом для связки виртуальный метод + его переопределение действует позднее связывание: вызов метода по ссылке на базовый класс может привести к вызову перегруженного метода. Переопределений невиртуальных методов не бывает. Перекрытие метода — это просто определение в классе или производном от него метода с таким же именем (и возможно отличающейся сигнатурой). При этом который из двух методов будет вызван, известно во время компиляции.

Как спрятать в Visual Studio окно свойств и панель элементов, когда активен редактор кода?

#visual_studio


Почему то в Visual Studio, применяется один макет окон, когда активно окно в режиме
конструктора и когда активно окно в режиме кода. Причины наверно есть, ведь можно и
одновременно открыть на части экрана и то и то... Но я такой возможностью не пользуюсь
и мне не удобно, что часть экрана когда я пишу код занимают окна свойств и панель элементов,
которые мне нужны, когда я пользуюсь конструктором. Возможно ли настроить студию так,
чтобы автоматически скрывать их, когда у меня активно окно с кодом, и автоматически
отображать, когда активно окно конструктора?

P.S. У меня версия 2008, ответ ищется для неё. Но прошу также оставлять ответы для
любой версии.
    


Ответы

Ответ 1



Вот такой ещё вариант: нажимать Shift+Alt+Enter, когда вы переходите в режим правки кода. Это стандартный студийный хоткей View.FullScreen. Под рукой студия 2015, проверил у коллег в 2012 и 2010 — работает. 2008 под рукой нет, но думаю, что тоже сработает.

Ответ 2



Но прошу также оставлять ответы для любой версии Начиная с версии Visual Studio “14” CTP 2: https://blogs.msdn.microsoft.com/visualstudio/2014/07/08/visual-studio-14-ctp-2-available/ появилась возможность сохранять разметку открытых окон в IDE. Как это сделать описано здесь: https://msdn.microsoft.com/en-us/library/4k7zyeba.aspx Если кратко, то расставляешь окна как тебе удобно и в верхнем меню выбираешь Window > Save Window Layout. Потом расставляешь окна по-другому как нужно и сохраняешь еще одну разметку. Переключение между разметками с помощью сочетания клавиш: CTRL + ALT + 1..0 Видео-инструкция: https://www.youtube.com/watch?v=IZ4LDZU7c_s Единственный минус - данная фича отсутствует в Visual Studio 2008

Ответ 3



В Visual Studio 2005/2008 скрывать и показывать окна и панели можно с помощью макросов. Смотрите этот ответ. К сожалению, в данный момент у меня нет доступа к предыдущим версиям VS, а в новых версиях макросы отсутствуют. Устанавливать ненужную мне версию Студии я не горю желанием, однако я установил расширение Visual Commander (доступно для версий 2010-2015; невозможно установить в Express-версии). Опробовал на VS2015 Community. После его установки и перезапуска Студии в меню появится пункт VCmd. Выбираем пункт Extensions, добавляем новое расширение, даём ему подходящее название (HideToolboxAndPropertiesPanes), выбираем язык C# v4.0 и вводим следующий код: using EnvDTE; using EnvDTE80; using Microsoft.VisualStudio.Shell; public class E : VisualCommanderExt.IExtension { public void SetSite(DTE2 DTE, Package package) { dte = DTE; windowEvents = DTE.Events.WindowEvents; windowEvents.WindowActivated += OnWindowActivated; } public void Close() { windowEvents.WindowActivated -= OnWindowActivated; } private void OnWindowActivated(Window gotFocus, Window lostFocus) { if (gotFocus.Caption.EndsWith(" [Design]")) { dte.Windows.Item(Constants.vsWindowKindProperties).AutoHides = false; dte.Windows.Item(Constants.vsWindowKindToolbox).AutoHides = false; } else { dte.Windows.Item(Constants.vsWindowKindProperties).AutoHides = true; dte.Windows.Item(Constants.vsWindowKindToolbox).AutoHides = true; } } private DTE2 dte; private WindowEvents windowEvents; } Щёлкаем по кнопкам Compile, Install. Готово! Наш макрос-расширение установлен. Теперь окна Properties и Toolbox автоматически показываются, когда активен документ, содержащий строку [Design] в названии и скрываются во всех остальных случаях.

Как правильно в коде описать отношения между картами в колоде?

#ruby #логика


Предположим, что каждое достоинство игральной карты описывается отдельным классом:

class Jack {
    ...
}

class Queen {
    ...
}


Также есть отдельный класс колоды:

class Deck {
    ...
}


Интересует, как правильно описать взаимоотношения старшинств карт. Например, что
в любой карточной игре (практически, но это неважно) Дама старше Валета, а двойка младше
Короля. Также не стоит забывать, что в некоторых играх есть козырь, соответственно
любая козырная карта будет старше любой не козырной.

Насколько я понимаю, это должно описываться в каждом классе (для каждого вида карты).
Но как? И придется ли перечислять все карты, старше (и младше) которой является данная?
И как это сделать, если я рассуждаю правильно, в языке Ruby? Или все-таки взаимоотношения
должны описываться на уровне всей колоды?
    


Ответы

Ответ 1



Я бы предложил унаследовать все типы карт от базового класса Card, в котором бы при помощи внутреннего массива, например, TYPES задал бы порядок следования "типов" карт. Индекс такого массива будет определять старшинство среди типов. В конструктор можно было бы передавать масть suit и признак, является ли карта козырем trump. Чтобы объекты класса можно было сравнивать друг с другом мы при помощи include модуль Comparable, а потом переопределяем оператор <=>. class Card include Comparable # Массив-константа типов карт в порядке убывания значения TYPES = %w(Jocker Ace King Queen Jack Ten Nine Eight Seven Six).freeze # Конструктор принимает масть suit и признак является ли карта козырем trump def initialize(suit, trump = false) @suit = suit @trump = trump end # Вес мастей относительно друг друга def weight TYPES.find_index(self.class.name) end # Козырь или нет? def trump @trump end # Масть def suit @suit end # Переопределение оператора сравнения <=> def <=>(other) if other.trump == @trump other.weight <=> weight else (other.trump && !@trump) ? -1 : 1 end end end После этого можно либо явно унаследовать классы карты от базового класса Card class Jocker < Card end class Ace < Card end class King < Card end class Queen < Card end class Jack < Card end ... class Six < Card end Либо создать их средствами метапрограммирования (все-равно они однотипные). В цикле обходим наш массив карт Card::TYPES и динамически создаем одноименные классы, унаследованные от класса Card Card::TYPES.each do |klass| Kernel.const_set(klass, Class.new(Card)) end В результате объекты эти классов можно сравнивать друг с другом. С учетом козырной масти, чтобы пометить карту козырной, передаем true в качестве второго аргумента конструктора. jack = Jack.new(:hearts) ace = Ace.new(:clubs) p ace < jack # false p ace > jack # true p ace == ace # true jack = Jack.new(:hearts, true) ace = Ace.new(:clubs) trump = Ace.new(:clubs, true) p ace < jack # true p ace > jack # false p ace == trump # false В результате, какую бы колоду вы не сформировали затем в Deck ее всегда можно будет корректно отсортировать и сравнить каждую карту с другой, учитывая текущую козырную масть.

Ответ 2



Не смотря на кажущуюся очевидность, считаю что наследование здесь ни к чему. Как минимум, получится много классов, каждый из которых надо не только определить, но и инициализировать. Конкретно для определения старшинства достаточно определить атрибут seniority (старшинство) и метод can_hit?. Этого будет достаточно и для определения старшинства, и для переопределения (тот самый пример, когда двойка может бить туза). class Card attr_reader :name, :seniority, :hitable_seniorities def initialize(name, seniority, hitable_seniorities) @name = name @seniority = seniority @hitable_seniorities = hitable_seniorities end def can_hit?(other_card) hitable_seniorities.include?(other_card.seniority) end end Например вот так мы определим что двойка может бить туза: Card.new("two", 2, [14]) Ну и осталось определить фабрику: def build_deck(cards_params) cards_params.map { |card_params| Card.new(*card_params) } end Пример использования: build_deck([ ["three", 3, [2]], ["seven", 7, [2, 3, 4, 5, 6]], ["ace", 14, [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]] ]) Я умышленно не учитывал в примерах масти т.к. про них не было ничего в вопросе. Для использования мастей (особенно с учётом таких карт, как джокер), скорее всего, понадобится использовать композиции (см. Practical Object-Oriented Design in Ruby, главу Combining Objects with Composition). Но общее направление мыслей, надеюсь, понятно. UPD: Сделал небольшой пример. Масти тоже различаются по старшинству, но для того, чтобы пример был более полным, сравниваются "традиционным" методом. class Suit attr_reader :name, :seniority def intialize(name, seniority) @name = name @seniority = seniority @trump = false end def trump! @trump = true end def trump? @trump end def same?(other_suit) seniority == other_suit.seniority end def highter?(other_suit) return false if same?(other_suit) return true if trump? seniority > other_suit.seniority end end class Card attr_reader :name, :seniority, :hitable_seniorities, :suit def initialize(name, seniority, hitable_seniorities, suit) @name = name @seniority = seniority @hitable_seniorities = hitable_seniorities @suit = suit end def can_hit?(other_card) #hitable_seniorities.include?(other_card.seniority) return true if suit_highter?(other_card) return false unless same_suit?(other_card) seniority_highter?(other_card) end private def suit_highter?(other_card) suit.highter?(other_card.suit) end def same_suit?(other_card) suit.same?(other_card.suit) end def seniority_highter?(other_card) hitable_seniorities.include?(other_card.seniority) end end class Game attr_reader :suits_factory, :deck_factory attr_reader :suits, :deck def initialize(suits_factory, deck_factory) @suits_factory = suits_factory @deck_factory = deck_factory end def new_game! build_deck! choose_tramp! end private def build_deck! @suits = suits_factory.build @deck = deck_factory.build(suits) end def choose_tramp! @suits.example.trump! end end class SuitsFactory attr_reader :suits_data def initialize(suits_data) # возможные масти в игре @suits_data = suits_data end def build suits_data.map do |suit_data| Suit.new(*suit_data) end end end class DesckFactory attr_reader :cards_data def initialize(cards_data) # возможные в игре достоинства карт @cards_data = cards_data end def build(suits) suits.map { |suits| build_for_suit(suit) }.flatten end private def build_for_suit(suit) cards_data.map do |card_data| Suit.new(*card_data, suit) end end end suits_factory = SuitsFactory.new([ ["Clubs", 1], ["Diamonds", 2], ["Hearts", 3], ["Diamonds", 4] ]) desk_factory = DesckFactory.new([ ["three", 3, [2]], ["seven", 7, [2, 3, 4, 5, 6]], ["ace", 14, [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]] ]) Пример использования: game = Game.new(suits_factory, desk_factory) game.new_game!

Ответ 3



Подозреваю, что отношения старшенства, можно описать некоторым свойством, value/weight, которое будет содержать число. 2 = 2 ... A = 14 // либо туз может быть младшим Тогда все отношения между картами, можно будет описать в виде математических или логических выражений. Если масть - козырь, то можно прибавлять некоторое значение ко всем картам одной масти.

Чем обусловлена линейная сложность функции list::size()?

#cpp #алгоритм #cpp11 #stl


В действительности вопроса даже два. К некоторому для себя удивлению узнал, что временная
сложность получения к-ва айтемов списка std::list::size() - линейная 


  C++98. Complexity: Up to linear. Источник


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

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


  C++11. Complexity: Constant.

    


Ответы

Ответ 1



Это следствие стародавнего "конфликта интересов" между функцими std::list::size() и std::list::splice(). Если делается частичный splice() из одного std::list в другой с требованием константного времени выполнения такого splice() (т.е. просто перецепить крайние элементы диапазона из одного списка в другой), то тогда невозможно будет сразу знать новые размеры контейнера-источника и контейнера-получателя, т.к. неизвестно сколько элементов перецепилось. Поэтому требование константного по времени splice() ведет к требованию линейного по времени size() - элементы в обоих контейнерах придется заново пересчитывать. Если же наоборот потребовать константности времени size() (т.е. фактически потребовать хранимый размер), то тогда придется согласиться на более медленный splice(), который в вышеупомянутой ситуации за линейное время будет вычислять новые размеры контейнеров. В С++98 этот вопрос был оставлен открытым. Реализациям разрешалось выбирать, где по их мнению константное время важнее - в size() или в splice(). В С++11 решили принять волевое решение: делаем хранимый размер, гарантируем size() за константное время и, так уж и быть, соглашаемся на линейное время в splice(). Именно этим и объясняется разница между С++98 и С++11. P.S. Реализация стандартной библиотеки С++ в GCC изначально пошла по пути быстрого splice() и медленного size(). После того, как С++11 настоял на ином варианте, GCC исправил реализацию своего std::list, но тут же наткнулся на бинарную несовместимость версий библиотек из-за добавления нового поля в std::list. Изменение было тут же откачено, и по сей день std::list в GCC не соответствует требованиям С++11. std::list::size() там линеен.

Исключить дублирование кода в функциях с разной константностью

#cpp #рефакторинг


Рассмотрим такой код:

struct B {};
struct D1 : B {};
struct D2 : B {};

#define get if (s) return d1; else return d2;

volatile bool s;

struct C {
    const B& f() const { get }
    B& f() { get } 
private:
    D1 d1;
    D2 d2;
};


Здесь видно, что разные версии f() должны возвращать один и тот же объект (в одном
случае - константный, в другом - нет), основываясь на некоторой логике выбора, которая
может быть достаточно сложной. В примере для исключения дублирования кода этой логики
использована макроподстановка через #define. 

Можно ли избежать дублирования кода в разных версиях f() не прибегая к услугам препроцессора?
    


Ответы

Ответ 1



Проблема дублирования кода из-за соображений константности обычно встречается в двух вариантах: На уровне отдельных функций: когда есть две функции с одинаковой реализацией, отличающиеся лишь константностью входных типов (в т.ч., как частный случай, константностью *this в методе класса) и соответствующей константностью возвращаемого значения. В языке С одной из известных идиом для решения этой проблемы является написание одной-единственной функции, которая решает поставленную задачу в рамках соблюдения константности входных данных, а затем просто безусловно снимает константность с возвращаемого значения (см., например, стандартную функцию strstr). В данном случае предполагается, что вызывающий код, будучи в курсе ситуации с константностью данных, "по-джентельменски" вернет "потерянную" в процессе вызова функции константность на место. В языке С++ этот подход технически тоже применим, но его использовать не принято. Точнее, традиционная С++ идиома, основанная внутренне фактически на том же самом подходе, внешне реализуется с небольшим отличием: полноценная реализация предоставляется для константной версии функции, а над ней надстраивается вторая - неконстантная - версия той же функции. Последняя реализуется через константную путем снятия константности с возвращаемого значения const return_type *foo(const input_type *argument) { ... } return_type *foo(input_type *argument) { return const_cast(foo(const_cast(argument)); } Вот именно этот подход прекрасно подойдет в вашем случае. На уровне отдельных классов: кода надо реализовать два класса, которые фактически идентичны с точки зрения исходного кода, а отличаются лишь внешней константностью обрабатываемых данных. Хороший пример: константная и неконстантная версия класса контейнерного итератора. В такой ситуации один из жизнеспособных подходов - реализация общей функциональности в виде шаблонного класса, параметризованного необходимым количеством типов (в простейшем случае - одним), и реализация требуемых финальных классов через специализации этого шаблона. Что-то вроде template class list { template class iterator_impl { ... }; typedef iterator_impl iterator; typedef iterator_impl const_iterator; ... }; P.S. Ваш собственный ответ, использующий шаблонную функцию - это фактически адаптирование вышеприведенного второго подхода к первой ситуации. Работать, без сомнения, будет, однако именно в такой ситуации банальный вариант с const_cast, как мне кажется, выглядит проще и уместнее.

Ответ 2



Можно написать что-то вроде следующего struct C { const B& f() const { if (s) return d1; else return d2; } B& f() { return const_cast( const_cast( this )->f() ); } private: D1 d1; D2 d2; }; Вот демонстрационная программа #include struct B {}; struct D1 : B {}; struct D2 : B {}; bool s; struct C { const B& f() const { std::cout << "const B & f() const" << std::endl; if (s) return d1; else return d2; } B& f() { std::cout << "B & f()" << std::endl; return const_cast( const_cast( this )->f() ); } private: D1 d1; D2 d2; }; int main() { C c1; c1.f(); std::cout << std::endl; const C c2; c2.f(); return 0; } Ее вывод на консоль B & f() const B & f() const const B & f() const

Ответ 3



Придумал вариант с шаблонной дружественной функцией: template R& g(T* t); struct C { const B& f() const { return g(this); } B& f() { return g(this); } private: D1 d1; D2 d2; template friend R& g(T* t); }; template R& g(T* t) { if (s) return t->d1; else return t->d2; } Или можно вовсе перенести в класс: struct C { const B& f() const; B& f(); private: D1 d1; D2 d2; template static R& g(T* t) { if (s) return t->d1; else return t->d2; } }; const B& C::f() const { return g(this); } B& C::f() { return g(this); } А т.к тип R по сути может быть выведен из факта наличия константности в T, то этот тип можно вовсе убрать из шаблона: template static std::conditional_t, const B&, B&> g(T* t) { if (s) return t->d1; else return t->d2; } Т.о. необходимость явно указывать тип при вызове g отпадает: const B& C::f() const { return g(this); } B& C::f() { return g(this); }

Ответ 4



Очевидный вариант: class A { bool flag; int a; int b; public: int& get() { return flag ? a : b; } const int& get() const { return ((A*)this)->get(); } }; Пример: int main() { A a {}; const A ca {}; static_assert(std::is_same::value, "!!"); static_assert(std::is_same::value, "!!"); }

Flexbox item — перенос на новую строку

#html #css #css3 #flexbox


Есть Flexbox сетка.



.flex {
  display: flex;
  flex-wrap: wrap;
  border: 2px solid red;
}

.item {
  width: 50px;
  height: 50px;
  margin: 5px;
  border: 2px solid blue;
}
Как перенести .new-string на новую строку вместе с элементами, которые идут после него?


Ответы

Ответ 1



Перевод ответа на enSO. Примечание переводчика: flex-элемент (flex item) — непосредственный ребёнок блока с display: flex. Наиболее простое и надёжное решение — это вставка flex-элементов в правильных местах. Если они достаточно широкие (width: 100%), они будут создавать перенос строки. .container { background: tomato; display: flex; flex-flow: row wrap; align-content: space-between; justify-content: space-between; } .item { width: 100px; background: gold; height: 100px; border: 1px solid black; font-size: 30px; line-height: 100px; text-align: center; margin: 10px } .item:nth-child(4n - 1) { background: silver; } .line-break { width: 100%; }
1
2
3
4
5
6
7
8
9
10
Но это уродливо и не семантически. Вместо этого, мы можем генерировать псевдоэлементы внутры flex-контейнера и использовать свойство order чтобы перемещать их в нужные места. .container { background: tomato; display: flex; flex-flow: row wrap; align-content: space-between; justify-content: space-between; } .item { width: 100px; background: gold; height: 100px; border: 1px solid black; font-size: 30px; line-height: 100px; text-align: center; margin: 10px } .item:nth-child(3n) { background: silver; } .container::before, .container::after { content: ''; width: 100%; order: 1; } .item:nth-child(n + 4) { order: 1; } .item:nth-child(n + 7) { order: 2; }
1
2
3
4
5
6
7
8
9
Но есть ограничение: flex-контейнер может иметь только псевдоэлементы ::before и ::after. Это значит, что мы можем создать только 2 переноса строки. Чтобы разрешить это, вы можете генерировать псевдоэлементы внутри flex-элементов вместо flex-контейнера. Таким способом вы не будете ограничены двумя. Но эти псевдоэлементы не будут flex-элементами, поэтому не будут создавать переносы строк. Но, к счастью, спецификация CSS Display L3 ввела display: contents (в данный момент поддерживаемая только Firefox 37): Элемент сам по себе не генерирует никаких блоков, но его дети и псевдоэлементы генерируют блоки как обычно. С целью генерации блоков и разметки элемент будет расцениваться как будто он был заменён его детьми и псевдоэлементами в дереве документа. Поэтому вы можете применять display: contents к детям flex-контейнера и обернуть содержимое каждого внутрь дополнительного блок. Таким образом flex-элементы будут этими дополнительными обёртками и псевдоэлементами детей. .container { background: tomato; display: flex; flex-flow: row wrap; align-content: space-between; justify-content: space-between; } .item { display: contents; } .item > div { width: 100px; background: gold; height: 100px; border: 1px solid black; font-size: 30px; line-height: 100px; text-align: center; margin: 10px; } .item:nth-child(3n) > div { background: silver; } .item:nth-child(3n)::after { content: ''; width: 100%; }
1
2
3
4
5
6
7
8
9
10
Кроме того, в соответствии с Fragmenting Flex Layout и CSS Fragmentation, flexbox позволяет принудительные переносы с помощью break-before, break-after или их псевдонимов в CSS 2.1: .item:nth-child(3n) { page-break-after: always; /* Синтаксис CSS 2.1 */ break-after: always; /* Новый синтаксис */ } .container { background: tomato; display: flex; flex-flow: row wrap; align-content: space-between; justify-content: space-between; } .item { width: 100px; background: gold; height: 100px; border: 1px solid black; font-size: 30px; line-height: 100px; text-align: center; margin: 10px } .item:nth-child(3n) { page-break-after: always; background: silver; }
1
2
3
4
5
6
7
8
9
10
Принудительные переносы строк во flexbox ещё широко не поддерживаются, но они работают в Firefox.

Ответ 2



Во flexbox, к сожалению, не силен, но вот такой трюк помню: .flex { display: flex; flex-wrap: wrap; border: 2px solid red; } .item { width: 50px; height: 50px; margin: 5px; border: 2px solid blue; } .flex:after { content:''; width:100%; order:0; } .item.new-string, .item.new-string ~ .item { order:1; }


Ответ 3



Решил с помощью добавления элемента разрыва .flex { display: flex; flex-wrap: wrap; border: 2px solid red; } .item { width: 50px; height: 50px; margin: 5px; border: 2px solid blue; } .new-string { width: 100%; }


Ответ 4



Может, как-нить с марджином попробовать поиграться? .flex { display: flex; flex-wrap: wrap; border: 2px solid red; } .item { width: 50px; height: 50px; margin: 5px; border: 2px solid blue; } .item:nth-child(2) { margin-right: calc(100% - 125px); }


Ответ 5



.container { display: flex; flex-wrap: wrap; justify-content: center; } Этого должно хватить!

Ответ 6



Можно в разметку вставить горизонтальную линию flex hr { width: 100%; border: 0; }
item
item

item new-string