Страницы

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

суббота, 21 декабря 2019 г.

Удаление ненужных функций

#java #android


Как очистить мусор в проекте или библиотеке?

Я использую сторонние библиотеки. Оттуда мне нужно только пару функций. А остальные
попросту занимают место, память. Как от них избавиться? Может есть программы или придется
все ручками удалять?
    


Ответы

Ответ 1



Можно воспользоваться Insptection. Если используете Android Studio то: нажимает Ctrl+Alt+Shift+I; в появившемся окне набираем unused; получаем подсказку в виде списка, такого плана: выбираем интересующую нас инспекцию.

Ответ 2



Вы можете провести посредством Analyze > Inspect Code полную проверку проекта. ermak0ff

Android NDK. Очень много ABI: armeabi, armeabi-v7a, arm64-v8a… Под какую из них реально стоит строить нативные .so-либы?

#android #android_sdk #android_ndk #arm


armeabi, armeabi-v7a, arm64-v8a, x86, x86_64, mips, mips64...

Ну, armeabi - вроде понятно. Эта вроде архитектура на всех (или почти на всех) реальных
гаджетах.
x86 - вроде тоже понятно. Это прежде всего для Genymotion и прочих эмуляторов, где
Android x86.

А остальные?

Будет ли идеальным решением просто построить либу под все ABI? А если нет возможности
протестить везде, а только на x86 и armeabi?

И почему столько armов: armeabi, armeabi-v7a, arm64-v8a? Какой из них актуальнее?
Как определить, какой поддерживается моим гаджетом? Могут ли быть несколько сразу?
    


Ответы

Ответ 1



x86 - это прежде всего процессоры Intel Atom, которые установлены в немалое число реальных устройств, а не эмуляторы. Эмулятор, как раз может эмулировать любую архитектуру, независимо от архитектуры процессора, на котором хостится - на то он и эмулятор. Вы можете в этом убедится сами, посмотрев на список образов для эмуляции: Минимальным набором поддерживаемых архитектур является набор APP_ABI := x86 armeabi. Далее следует определиться, будет ли ваше приложение использовать дополнительные возможности архитектуры armeabi-v7a, например NEON - если это так, то данную архитектуру тоже следует включить в список (расширенный список инструкций arm-v7) - на самом деле, сейчас практически все ARM процессоры, как минимум v7 В последнее время устройства с процессорами на 64-ех битной архитектуре все чаще появляются в продаже, поэтому их тоже стоит учесть. Точно проигнорировать можно mips/mips64 Узнать, какая архитектура у вашего устройства, можно по спецификации процессора, который оно использует. Например, Qualcomm Snapdragon 820 - ARM-V8A. Так же в маркете можно найти программы, которые покажут системную информацию. Несколько архитектур ARM в одном процессоре быть не может, но каждая следующая включает предыдущую, то есть в ARM-V7 включен ARM, в ARM-V8 - ARM-V7. По факту, самая актуальная сейчас ARM-платформа - ARM-V7, на ней построено абсолютное большинство действующих процессоров, но ARM-V8 активно наступает. Разница в ARM-архитектурах очевидна, каждая следующая - шаг в эволюции, добавляются новые возможности на уровне железного ядра. Вы можете указать собрать библиотеку под все abi и это будет хорошим решением, если итоговый размер приложения вас устроит - каждая дополнительная платформа - новые файлы, которые имеют размер. Официальное руководство по этому вопросу.

Hibernate many to many бесконечно вложенный json

#java #hibernate #spring_mvc


Есть три таблицы user, group, user2group. Связь много ко многим.

Если прописать мапинг только в классе User, то контроллер выдает json который и нужен.

@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "user2group", 
    joinColumns = { @JoinColumn(name = "user_id") }, 
    inverseJoinColumns = { @JoinColumn(name = "group_id") })
private Set groups = new HashSet();


Но если мапим связанные данные в классе Group, то при попытке получить список групп
с привязанными к ним клиентами, контроллер начинает выдавать json, но при этом бесконечно
проваливается в связанные сущности.

Мапинг для класса Group сейчас у меня следующий:

@ManyToMany(mappedBy = "groups")
private Set users = new HashSet();


Записи я получаю следующим образом 

Session session = sessionFactory.getCurrentSession();
Query query = session.createQuery("FROM User");
List users = query.list();


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

properties.put("hibernate.enable_lazy_load_no_trans",  true);


Вопрос. Чтобы получать json единой вложенности, нужны какие-то особенные настройки
хибернейта? Или прописывать особый маппинг?

В ходе гугления вопроса мне начало казаться, что особо ни у кого не возникает подобной
сложности, может тут подход нужен иной?

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

[
  {id:1, name:"user1", groups:[{id:1, title:"group1"}, {id:5, title:"group5"}]},
  {id:2, name:"user2", groups:[]},
  {id:3, name:"user3", groups:[{id:3, title:"group3"}]},
]


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

[
  {id:1, title:"group1", users:[{id:1, name:"user1"}, {id:5, name:"user5"}]},
  {id:2, title:"group2", users:[]},
  {id:3, title:"group3", users:[{id:3, name:"user3"}]},
]

    


Ответы

Ответ 1



Не очень хорошая практика - отдавать ваши ORM-сущности непосредственно наружу. Во-первых, вы смешиваете разные задачи в одном классе. Во-вторых, вы отдаете наружу все его состояние, которое, возможно, не стоит отдавать. В третьих, когда-нибудь вам потребуется отдавать для разных задач разный набор полей. Обычно, эта проблема решается паттерном DTO (Data Transfer Object). Вы просто создаете классы UserDto и GroupDto, которые содержат только необходимые поля, геттеры-сеттеры, конструктор по-умолчанию и JSON-аннотации. При отдаче наружу в слое представления (контроллере) перекладываете поля из ORM-объектов в DTO-объекты. Можно это делать как вручную (завести static-метод в DTO), так и использовать какой-нибудь маппер вроде Dozer. Для получения групп с вложенными в нее пользователями, нужно завести отдельную DTO, например, как GroupWithUsersDto в примере ниже. public class GroupDto { @JsonProperty("id") private Long id; @JsonProperty("title") private String title; // сеттеры-геттеры public static GroupDto fromModel(Group group) { GroupDto dto = new GroupDto(); dto.setId(group.getId()); dto.setTitle(group.getTitle()); return dto; } public class UserDto { @JsonProperty("id") private Long id; @JsonProperty("name") private String name; // сеттеры-геттеры public static UserWithGroupsDto fromModel(User user) { UserWithGroupsDto dto = new UserWithGroupsDto(); dto.setId(user.getId()); dto.setName(user.getName()); return dto; } } Для выдачи пользователей с их группами: public class UserWithGroupsDto { @JsonProperty("id") private Long id; @JsonProperty("name") private String name; @JsonProperty("groups") private List groups; // сеттеры-геттеры public static UserWithGroupsDto fromModel(User user) { UserWithGroupsDto dto = new UserWithGroupsDto(); dto.setId(user.getId()); dto.setName(user.getName()); List groupDtos = new ArrayList<>(); for (Group group : user.getGroups()) { groupDtos.add(GroupDto.fromModel(group)); } dto.setGroups(groupDtos); return dto; } } Для выдачи групп с пользователями: public class GroupWithUsersDto{ @JsonProperty("id") private Long id; @JsonProperty("title") private String title; @JsonProperty("users") private List // сеттеры-геттеры public static GroupWithUsersDto fromModel(Group group) { GroupWithUsersDto dto = new GroupWithUsersDto(); dto.setId(group.getId()); dto.setTitle(group.getTitle()); List usersDtos = new ArrayList<>(); for (User user : group.getUsers()) { usersDtos.add(UserDto.fromModel(user)); } dto.setUsers(usersDtos); return dto; }

Ответ 2



Можно повесить в классе Group на поле @ManyToMany(mappedBy = "groups") private Set users = new HashSet(); аннотацию @JsonIgnore Или использовать слой DTO package my.company.model; import javax.persistence.*; import java.util.Date; import java.util.List; import java.util.Set; @Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; private String name; private String surname; private String password; private String email; @Column(name="time_registration") private Date timeRegistration; @ManyToMany(fetch = FetchType.LAZY) @JoinTable(name = "users_to_roles", joinColumns = {@JoinColumn(name = "user_id")}, inverseJoinColumns = @JoinColumn(name = "role_id")) private Set roles; @OneToMany(mappedBy = "user", fetch = FetchType.LAZY) private List albums; @ManyToMany(fetch = FetchType.LAZY) @JoinTable(name = "users_to_dialogs", joinColumns = {@JoinColumn(name = "user_id")}, inverseJoinColumns = @JoinColumn(name = "dialog_id")) private List dialogs; @OneToMany(mappedBy = "user", fetch = FetchType.LAZY) private List messages; @OneToOne private Avatar avatar; public User() { } public User(String name, String surname, String password, String email, Date timeRegistration, Set roles) { this.name = name; this.surname = surname; this.password = password; this.email = email; this.timeRegistration = timeRegistration; this.roles = roles; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public Set getRoles() { return roles; } public void setRoles(Set roles) { this.roles = roles; } public List getAlbums() { return albums; } public void setAlbums(List albums) { this.albums = albums; } public Date getTimeRegistration() { return timeRegistration; } public void setTimeRegistration(Date timeRegistration) { this.timeRegistration = timeRegistration; } public List getDialogs() { return dialogs; } public void setDialogs(List dialogs) { this.dialogs = dialogs; } public List getMessages() { return messages; } public void setMessages(List messages) { this.messages = messages; } public String getSurname() { return surname; } public void setSurname(String surname) { this.surname = surname; } public Avatar getAvatar() { return avatar; } public void setAvatar(Avatar avatar) { this.avatar = avatar; } @Override public String toString() { return id + " " + name + " " + email; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; return id == user.id; } @Override public int hashCode() { return (int) (id ^ (id >>> 32)); } } package my.company.DTO; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Date; import java.util.List; @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY) @JsonInclude(JsonInclude.Include.NON_NULL) public class UserDTO { private Long id; private String name; private String surname; private String email; @JsonProperty("albums") private List albumDTOs; private Date timeRegistration; private Long avatarId; public UserDTO(Long id, String name, String surname, String email, Date timeRegistration, Long avatarId) { this.id = id; this.name = name; this.surname = surname; this.email = email; this.timeRegistration = timeRegistration; this.avatarId = avatarId; } public UserDTO(Long id, String name, String surname, String email, List albumDTOs, Date timeRegistration) { this.id = id; this.name = name; this.surname = surname; this.email = email; this.albumDTOs = albumDTOs; this.timeRegistration = timeRegistration; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public List getAlbumDTOs() { return albumDTOs; } public void setAlbumDTOs(List albumDTOs) { this.albumDTOs = albumDTOs; } public Date getTimeRegistration() { return timeRegistration; } public void setTimeRegistration(Date timeRegistration) { this.timeRegistration = timeRegistration; } public String getSurname() { return surname; } public void setSurname(String surname) { this.surname = surname; } public Long getAvatarId() { return avatarId; } public void setAvatarId(Long avatarId) { this.avatarId = avatarId; } }

Как реализовать в Edittext ввод символов справа налево

#android #android_edittext


Есть поле  Edittext, в которое пользователь должен ввести ответ, но при этом первый
введенный символ должен быть правее, чем второй. Т.е. если пользователь последовательно
вводит 123, то в итоге в  Edittext отобразится 321.
Можно ли как-то это реализовать используя 1 поле Edittext?
    


Ответы

Ответ 1



Суть идеи, насколько я поняла, только в том, чтобы правильно перемещать курсор в поле editText, а именно так, чтобы после первого введенного символа курсор становился перед ним (т.е. слева). Для этого нужно написать как-то так: public class MainActivity extends AppCompatActivity implements TextWatcher{ EditText editText; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); editText = (EditText) findViewById(R.id.editText); editText.addTextChangedListener(this); @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { editText.setSelection(0); } @Override public void afterTextChanged(Editable s) { } } Ну и в layout для editText соответственно указать свойство gravity right, чтобы все отвечало логике ввода текста справа-налево.

Ответ 2



Используйте класс TextWatcher. Должно получиться как-то так: editText.addTextChangedListener(new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { // Здесь опишем логику перестановки символа if (before > start) editText.setText(editText.getText().toString() + s); } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { // TODO Auto-generated method stub } @Override public void afterTextChanged(Editable s) { // TODO Auto-generated method stub } });

Ответ 3



android:textDirection="anyRtl" Поэкспериментируйте со значением этого атрибута

Прокрутка ScrollView до конца после добавления нового элемента

#java #android #android_scrollview


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

scrollView.fullScroll(ScrollView.FOCUS_DOWN);


scroll_view.xml

 
        

        
    

    


Ответы

Ответ 1



Только что такое делал у себя=) private void scrollDialogDown() { mainScrollView.post(new Runnable() { @Override public void run() { mainScrollView.fullScroll(ScrollView.FOCUS_DOWN); } }); }

Asp.Net MVC 5 или Web Api 2.0

#c_sharp #net #aspnet_mvc #aspnet_web_api #asp


Я совсем запутался... 
Asp.Net MVC - создание веб-приложений, Web Api - сервисов. Не могли бы вы объяснить
для каких задач пишутся сервисы, а для каких веб приложения? Насколько я понимаю, если
у нас клиент планируется не только веб, но еще, например, WPF-приложение, мобильное,
то выбор должен пасть в сторону сервисов.

Конкретно для моего случая: имеется некое десктопное приложение еще на WinWorms.
Хочется его перевести в веб. И встает логичный вопрос что использовать: Asp.Net Web
Api + AngularJs или Asp.Net MVC?

Буду благодарен за любую информацию.
    


Ответы

Ответ 1



Бакэнд для AngularJs можно написать хоть на ASP.NET MVC, хоть на Asp.Net Web Api, хоть на WCF (да, WCF тоже умеет делать REST-сервисы). Основная разница между ними в следующем: ASP.NET MVC подходит для генерации страниц на стороне сервера (но не мешает работать и клиентским фреймворкам). Выбрав связку Angular.js + Web Api, вы навсегда ограничите себя исключительно клиентской генерацией страниц, приобретя заодно возможные проблемы с индексацией поисковиками. Взамен - у вашего сайта автоматически появится API, которое можно сделать публичным. Думаю, начать надо с выяснения ответов на следующие вопросы: Сколько человек будут работать над проектом и какие технологии им знакомы? Насколько само ваше веб-приложение укладывается в концепцию HTTP? Оно будет состоять из набора отдельных динамически генерируемых страниц или из одной (или нескольких) страниц, содержащих формы, контролы и экраны? Пример приложения первого вида - Википедия. Пример приложения второго вида - Gmail. 2а. Предполагается ли, что страница никогда не будет обновляться полностью? 2б. Сможет ли пользователь открыть одновременно 2 части вашего приложения в одной вкладке? (+ ко второму виду) 2в. Понадобится ли пользователю делиться ссылкой с другими или ставить ссылку в закладки? (+ к первому виду) Нужно ли вам публичное API?

Алгоритм генерации случайных координат в двумерной плоскости

#алгоритм #случайные_числа #генерация_случайных_данных


Какой можно придумать алгоритм генерации случайных неповторяющихся координат на сетке
width и height размера, с range - расстоянием между ними и count количеством?
То есть чтобы результат был примерно следующим:
height = 10, width = 9 count = 3 range = 2

           или
.........      .........
.........      .........
....C....      .........
.........      .........
.........      .........
.C.......      .........
.........      .......C.
.......C.      ....C....
.........      .........
.........      ......C..

    


Ответы

Ответ 1



Двумерную сетку можно вытянуть в одномерный массив: x = i * width + j и соответственно вернуться к двумерной сетке: i = floor(x / width) j = x - width * i То есть задача сводится к генерации не повторяющихся индексов от 0 до width*height-1: Создаем массив x длиной N = width*height, заполняем его числами от 0 до width*height-1. Берем случайное число n в диапазоне от 0 до N-1 x[n] превращаем в пару (i,j) Ставим x[n] в конец массива (меняем его местами с x[N-1]) Уменьшаем N на 1. Повторяем начиная с шага 2.

Ответ 2



Думал, думал и наконец придумал и отладил алгоритм. У меня проверяется расстояние по ромбовидному принципу range = 3 ....o.... ...ooo... ..ooooo.. .oooXooo. ..ooooo.. ...ooo... ....o.... Создаем одномерный boolean массив. Забиваем массив значениями true (Если true - то место свободно) Создаем случайную координату в пределах поля (Для координат я использовал свой класс Coordinate. В нем я определил лишь два поля. X и Y) Проверяем, доступна ли координата. (Я использовал функцию GetAddress) Если доступна (true), генерируем координаты вокруг этой точки (Тут я пользовался функцией GetDiamondAreaCoordinates, но можно и любую другую вашу. Например квадратная область или круглая) и проверяем итератором валидны ли точки. Если не доступна возвращаемся к пункту 3. Если каждая точка области не за пределами поля, в boolean массив через функцию GetAddress забиваем false. Если за пределами, то продолжаем цикл. public Coordinate[] GetDiamondAreaCoordinates(Coordinate coordinate, int range) { ArrayList coordinates = new ArrayList<>(); int offsetHeight = 0, offsetWidth = range; int currentX = coordinate.getLeft(); int currentY = coordinate.getTop(); int localIterator = 0; for (int i = currentX-range; i < currentX+range+1; i++) { for (int j = currentY-offsetHeight; j < currentY+offsetHeight+1; j++) { coordinates.add(new Coordinate(i, j)); } offsetWidth --; offsetHeight += (currentX-offsetWidth <= currentX)? 1 : -1; } Coordinate[] resultCoords = coordinates.toArray(new Coordinate[coordinates.size()]); return resultCoords; } private int getAddress(int top, int left) { int index = top*width+left; if (index >= 0 && index < width*height) return index; return -1; }

ListView, конец списка

#android #listview


Как определить что список ListView прокручен до конца?
    


Ответы

Ответ 1



listView.setOnScrollListener(new AbsListView.OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE && (listView.getLastVisiblePosition() - listView.getHeaderViewsCount() - listView.getFooterViewsCount()) >= (adapter.getCount() - 1)) { // Now your listview has hit the bottom } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { } });

выборка из 3 таблиц с объединением данных

#sql #выборка


Добрый вечер,
имеется проблема, которую никак не могу решить.
Существует 3 таблицы:


tm_posts - данные о постах и страницах
tm_attributes - данные о тегах и категориях
tm_relationships - связи 1 и 2 таблицы


Пример структуры:

tm_posts:      

id | name | text
-------------------
1  | kols | <ТЕКСТ>
2  | ando | <ТЕКСТ>
3  | krot | <ТЕКСТ>

tm_attributes:    

id | sign | attr_name
-------------------
1  | tag  | учебник
2  | tag  | дождь
3  | cat  | личное
4  | cat  | опыт

tm_relationships:  

id | id_attr | id_post
-------------------
1  |    1    | 2
2  |    2    | 2
3  |    3    | 2
4  |    4    | 2


Мне нужно получить такой результат по нику ando:

id | name |  text   |      tags     |    category   |
-----------------------------------------------------
2  | ando | <ТЕКСТ> | учебник,дождь |  личное,опыт  |


Т.е. берутся данные из таблицы tm_posts по нику ando, ищется связка тегов и категорий
по id поста в таблице tm_relationships. После чего получаем данные из таблицы tm_attributes.
Эти данные сливаем в строку.

Вот что получилось сделать:

SELECT t1.*, GROUP_CONCAT(t3.attr_name) As `tags`
FROM tm_posts t1
JOIN tm_relationships t2 ON t1.id = t2.id_post
JOIN tm_attributes t3 ON t3.id = t2.id_attr
WHERE t1.name= 'ando'
AND t3.sign = 'tag'
GROUP BY t1.id


Этот код выводит все, кроме категорий. Как мне сюда добавить еще и категории.

-----------------------------Добавлено:

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

Структура таблиц остается такая же.
Результат должен выдать, примерно такой:

id | name |   text  |      tags       |      category     |
----------------------------------------------------------------------
2  | ando | <ТЕКСТ> | учебник,дождь   |       личное      |
5  | ник  | <ТЕКСТ> | какие-то теги   |       личное      |
6  | ник  | <ТЕКСТ> | какие-то теги   |       личное      |


Т.е. выбрались все записи с тегами, но только с определенной категорией "личное"
    


Ответы

Ответ 1



Думаю можно сделать два join к tm_attributes наподобие SELECT t1.*, GROUP_CONCAT(t3.attr_name) As `tags`,GROUP_CONCAT(t4.attr_name) As `cats` FROM tm_posts t1 JOIN tm_relationships t2 ON t1.id = t2.id_post left JOIN tm_attributes t3 ON t3.id = t2.id_attr and t3.sign = 'tag' left JOIN tm_attributes t4 ON t4.id = t2.id_attr and t4.sign = 'cat' WHERE t1.name= 'ando' GROUP BY t1.id Для выбора только категории личное пришел в голову только такой запрос fiddle: 1) сначала выбираем все Post с категория личное 2) делаем join к категориям select cat_data.*, GROUP_CONCAT(t3.attr_name) As `tags` from (select t1.*,t4.attr_name from tm_attributes t4 join tm_relationships t2 on id_attr=t4.id join tm_posts t1 on id_post=t1.id where t4.sign = 'cat' and t4.attr_name='личное' ) cat_data join tm_relationships t2 on id_post=cat_data.id left JOIN tm_attributes t3 ON t3.id = t2.id_attr and t3.sign = 'tag' group by cat_data.id

Ответ 2



SELECT t1.*, GROUP_CONCAT(if(t3.sign='tag',t3.attr_name,NULL)) As `tags`, GROUP_CONCAT(if(t3.sign='cat',t3.attr_name,NULL)) As `cats` FROM tm_posts t1 JOIN tm_relationships t2 ON t1.id = t2.id_post JOIN tm_attributes t3 ON t3.id = t2.id_attr WHERE t1.name= 'ando' AND t3.sign in('tag','cat') GROUP BY t1.id Условие t3.sign in('tag','cat') можно убрать, если кроме tag и cat нет других видов и не предвидится. И tm_relationships думаю стоит клеить по LEFT JOIN, в случае, если у какой то статьи может не быть ни тегов ни категорий.

Для чего делать do {} while (0)?

#c


Уже не раз замечаю подобный код

#if FUSE_TRACE
#define TRACE(x...) fprintf(stderr,x)
#else
#define TRACE(x...) do {} while (0)
#endif


Почему именно так, а не

#if FUSE_TRACE
#define TRACE(x...) fprintf(stderr,x)
#else
#define TRACE(x...)
#endif


Насколько я понял  do {} while (0) транслируется в NOP инструкцию, но зачем она здесь?
    


Ответы

Ответ 1



Макросы - вещь достаточно тонкая. Представьте ситуацию, кто-то написал код (пример условный) (но лучше так не пишете именно из-за этого). for (int i = 0;i < 10; i++) TRACE("ok"), s+=i; do_any(); Здесь всё будет корректно работать. В релиз режиме если мы будем использовать как вы предлагаете #define TRACE(x...) Произойдёт следующее, код преобразуется в if (something) , s+=i; do_any(); И он не скомпилируется. Так же не скомпилируется expr? TRACE("y") : TRACE("n"); Однако, и с использованием макроса #define TRACE(x...) do {} while (0) есть аналогичные проблемы. Я лично использую следующий макрос в таких целях: #define TRACE(x...) ((void)0) Либо же использую отдельную функцию (а не макрос) для вывода логов.

Какой существует алгоритм для объединения точек в области?

#алгоритм


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

Как-то вот так:


    


Ответы

Ответ 1



Использование матрицы расстояний в качестве решения задачи кластеризации во многих случаях вполне оправданно, хотя и ведёт, в общем-то, к излишнему резервированию памяти в связи с так называемым эффектом зеркалирования данных. Впрочем, рассмотрим именно этот метод. В тегах вопроса отсутствует упоминание о языке реализации, а потому, наверное, допустимо использовать любой, в частности С++. Для начала создадим традиционно используемую в таких случаях структуру (можно и класс, но особого смысла нет): struct Point { Point() : _x(0), _y(0) {} Point(int x, int y) : _x(x), _y(y) {} int _x, _y; }; Экспериментировать будем с теми же точками, что указаны в вопросе (разумеется, можно любые иные): std::vector pnts; pnts.push_back(Point(45,45)); pnts.push_back(Point(0,0)); pnts.push_back(Point(21,21)); pnts.push_back(Point(29,29)); pnts.push_back(Point(5,5)); pnts.push_back(Point(33,33)); pnts.push_back(Point(40,40)); pnts.push_back(Point(80,80)); pnts.push_back(Point(20,20)); pnts.push_back(Point(2,2)); pnts.push_back(Point(25,25)); Матрица расстояний создаётся перебором каждой точки с каждой и сохранением, собственно, расстояния между ними: const int n = pnts.size(); std::vector > distances(n); for(int i = 0; i < n; ++i) { const Point &p1 = pnts.at(i); // По умолчанию заполняем каждую колонку нулями. std::vector dist(n,0); for(int j = 0; j < n; ++j) { const Point &p2 = pnts.at(j); if(i == j) { // Если речь об одной и той же точке, // то и нечего вычислять, расстояние - 0. continue; } else if(i > j) { // Если уже ранее успели вычислить расстояние между // двумя точками, то просто копируем значение. dist[j] = distances[j][i]; } else { // Собственно, вычисление расстояния. dist[j] = std::sqrt(std::pow(p1._x-p2._x,2) + std::pow(p1._y-p2._y,2)); } } distances[i] = dist; } Получившаяся матрица на имеющемся наборе данных будет выглядеть так: 0 63 33 22 56 16 7 49 35 60 28 63 0 29 41 7 46 56 113 28 2 35 33 29 0 11 22 16 26 83 1 26 5 22 41 11 0 33 5 15 72 12 38 5 56 7 22 33 0 39 49 106 21 4 28 16 46 16 5 39 0 9 66 18 43 11 07 56 26 15 49 9 0 56 28 53 21 49 113 83 72 106 66 56 0 84 110 77 35 28 1 12 21 18 28 84 0 25 7 60 2 26 38 4 43 53 110 25 0 32 28 35 5 5 28 11 21 77 7 32 0 Как хорошо заметно, она зеркальна, а значит избыточна, и именно этот фактор может стать основной причиной невозможности использования рассматриваемого метода. Я намеренно не использую для хранения расстояния тип double, поскольку точности с целым (int) более чем достаточно, плюс некоторая экономия памяти, плюс этот же самый вектор можно будет легко задействовать как матрицу индексов. Матрица индексов служит аккурат задаче снижения количества итераций массива точек. Это фактически карта индексов, отсортированных по возрастанию расстояния. Таким образом получится, что для каждой точки самые близкие к ней иные точки будут всегда на первых местах. // Здесь можно сэкономить и не создавать отдельный вектор // под матрицу индексов, просто перезаписав значения вектора расстояний, // если конечно не планируется его использовать далее. std::vector > indexes(n); // Шаблон колонки индексов. std::vector tpl(n); for(int i = 0; i < n; ++i) tpl[i] = i; for(int i = 0; i < n; ++i) { std::vector vct = tpl; const std::vector &dist = distances[i]; std::sort(vct.begin(), vct.end(), [&dist](int i1, int i2) { return dist[i1] < dist[i2]; }); indexes[i] = vct; } Получится следующий набор индексов точек: 0 6 5 3 10 2 8 7 4 9 1 1 9 4 8 2 10 3 5 6 0 7 2 8 10 3 5 4 6 9 1 0 7 3 5 10 2 8 6 0 4 9 1 7 4 9 1 8 2 10 3 5 6 0 7 5 3 6 10 0 2 8 4 9 1 7 6 0 5 3 10 2 8 4 9 1 7 7 0 6 5 3 10 2 8 4 9 1 8 2 10 3 5 4 9 1 6 0 7 9 1 4 8 2 10 3 5 6 0 7 10 2 3 8 5 6 0 4 9 1 7 В первой колонке (индекс "0") рассматриваемой матрицы содержатся индексы точек по отношению самих к себе. Расстояние здесь очевидно нулевое и именно по этой причине после сортировки цикл формирования кластера можно начинать с колонки под индексом "1". Вторая колонка (индекс "1") - это индекс самой близкой точки по отношению к точке с индексом в первой колонке. Аналогично содержимое и всех последующих колонок, но с увеличением расстояния. Имея на руках матрицу отсортированных индексов, ничего более не остаётся, как приступить, собственно, к кластеризации. У автора вопроса задача содержит особенность, которая требует учёта различных максимумов для горизонтальных и вертикальных пиксельных расстояний: const int hrz_max = 10; const int vrt_max = 10; Кластеризацию удобно производить рекурсивно: void check(const std::vector &pnts , const std::vector > &indexes , int pnt_idx, std::vector &d) { const Point &p1 = pnts[pnt_idx]; d.push_back(pnt_idx); // ... со второй колонки. for(int i = 1, n = pnts.size(); i < n; ++i) { const int pnt2_idx = indexes[pnt_idx][i]; if(std::find(d.begin(),d.end(),pnt2_idx) != d.end()) continue; const Point &p2 = pnts[pnt2_idx]; if(std::abs(p1._x-p2._x) <= hrz_max && std::abs(p1._y-p2._y) <= vrt_max) { check(pnts, indexes, pnt2_idx, d); } else { // Незачем крутить цикл далее, т.к. расстояния // отсортированы по возрастанию значения. break; } } } Аргументы функции: const std::vector &pnts - вектор исходных точек; const std::vector > &indexes - матрица индексов; int pnt_idx - индекс точки, для которой формируем кластер; std::vector &d - вектор, который будет содержать индексы точек одного кластера.

Ответ 2



написал свой велосипед на javascript, может кому еще пригодится Вывод: матрица расстояний 0,64,34,23,57,17,7,49,35,61,28 64,0,30,41,7,47,57,113,28,3,35 34,30,0,11,23,17,27,83,1,27,6 23,41,11,0,34,6,16,72,13,38,6 57,7,23,34,0,40,49,106,21,4,28 17,47,17,6,40,0,10,66,18,44,11 7,57,27,16,49,10,0,57,28,54,21 49,113,83,72,106,66,57,0,85,110,78 35,28,1,13,21,18,28,85,0,25,7 61,3,27,38,4,44,54,110,25,0,33 28,35,6,6,28,11,21,78,7,33,0 матрица близости 0,6,5,3,10,2,8,7,4,9,1 1,9,4,8,2,10,3,5,6,0,7 2,8,10,3,5,4,9,6,1,0,7 3,5,10,2,8,6,0,4,9,1,7 4,9,1,8,2,10,3,5,6,0,7 5,3,6,10,0,2,8,4,9,1,7 6,0,5,3,10,2,8,4,9,7,1 7,0,6,5,3,10,2,8,4,9,1 8,2,10,3,5,4,9,6,1,0,7 9,1,4,8,2,10,3,5,6,0,7 10,3,2,8,5,6,0,4,9,1,7 матрица кластеров 0,6 1,9,4 2,8,10,3,5 7

Восстановить файлы и каталоги после rm -r

#debian


Как восстановить удалённые файлы и каталоги в дистрибутиве debian gnu/linux?
    


Ответы

Ответ 1



программы для восстановления (из одноимённых пакетов): testdisk, extundelete (только для файловых систем ext), foremost, scalpel. возможно, некоторые из них и позволяют работать с примонтированными файловыми системами, но безопаснее, конечно, этого не делать. и восстанавливать удалённые файлы безопаснее в другую файловую систему (примонтированную). т.е., если удалённые файлы находились, например, в корневой файловой системе, то проще всего воспользоваться каким-нибудь live-cd/dvd/usb (к примеру, systemrescuecd), загрузившись с него. инструкции по использованию перечисленных программ можно почитать, например, здесь: http://help.ubuntu.ru/wiki/восстановление_данных

Парсинг всего сайта с помощью Jsoup

#java #jsoup #парсер


Осваиваю парсинг сайтов, в связи с чем возник вопрос, на который никак не могу найти
ответ. 

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


Ответы

Ответ 1



Задачу можно разбить на несколько этапов: Получаем содержимое страницы и выполняем нужный анализ. Document doc = Jsoup.connect("http://www.site.com/").get(); processDoc(doc); Находим на текущей странице все ссылки с этим же доменом и добавляем их в очередь, предварительно отфильтровав те ссылки, которые уже были посещены. Elements links = doc.select("a[href]"); for (Element link : links) { String absLink = link.attr("abs:href"); if (absLink.contains("site.com") && notYetVisited(absLink)) addToQueue(absLink); } Пока очередь еще не пуста, извлекаем очередную ссылку и переходим шагу 1. Если сайт большой, то необходимо предусмотреть хранение очереди ссылок и списка посещенных ссылок в базе данных. Вот подробный пример с решением похожей задачи.

Как в NavigationView в header установить текст?

#java #android #android_layout #android_navigation_drawer #android_navigation_view


У меня есть вот такой NavigationView 



Вот такой код:











и вот такой header:













Мне нужно в этот header в поле для имени user поставить его имя, а в поле для email
поставить соответственно email

Я понимаю, что все стандартно и нужно найти по id view с которым хочешь работать
и установить в него TextView. 

Но когда я нахожу view и ставлю text, то вылетает ошибка, что такого view еще нет.
Это логично т.к. NavigationView еще не открыт...

Так вот вопрос. Как отследить что NavigationView уже открыт и только после этого
устанавливать text в view?

Если я в onCreate() нахожу и устанавливаю текст

TextView tvName = (TextView) findViewById(R.id.tvName);
    TextView textEmail = (TextView) findViewById(R.id.textEmail);

    tvName.setText("test");
    textEmail.setText("test");


то получаю такую ошибку в строке tvName.setText("test");


  FATAL EXCEPTION: main
                                                                                
   Process: com.example.android.camera2basic, PID: 32152
                                                                                
   java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.android.camera2basic/com.example.android.camera2basic.activities.MainActivity}:
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)'
on a null object reference


Если применять такой подход, то ошибки нет, но текст остается таким как по умолчанию

NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
    navigationView.setNavigationItemSelectedListener(this);
    RelativeLayout headerView = (RelativeLayout) LayoutInflater.from(this).inflate(R.layout.nav_header_mainsecond,
null);
    TextView tvName = (TextView) headerView.findViewById(R.id.tvName);
    TextView textEmail = (TextView) headerView.findViewById(R.id.textEmail);
    tvName.setText("name");
    textEmail.setText("email");

    


Ответы

Ответ 1



Вам надо: Получить NavigationView NavigationView nv = ...; Получить разметку его Header View header = nv.getHeaderView(0); Найти нужные поля и с ними что-то сделать TextView tv = (TextView)header.findViewById(...);

Событие клика на ссылку

#html #css


Здравствуйте. 
При клике на ссылку появляются пункты меню, и если после этого кликнуть в любое место
экрана (кроме ссылки которая активирует меню) - меню пропадает.

Каким образом сделать чтоб меню пропадало еще и при повторном клике на эту же ссылку?



.menu {
  display: none;
  padding: 3px;
}
.menu a {
  display: block;
  margin: 1px;
  padding: 10px;
}
.active_menu {
  display: block;
}
.active_menu:focus ~ .menu,
.active_menu:active ~ .menu,
.menu:hover {
  display: block;
}
Развернуть
меню





    


Ответы

Ответ 1



.menu { display: none; } .menu a { display: block; margin: 1px; padding: 10px; } .active_menu input:checked + .menu { display: block; }

Ответ 2



Его можно сделать еще с помощью плагина jQuery таким способом: Развернуть меню А если ты хочешь сделать на CSS, то сделай с помощью chekbox так: .menu, #menyu { display: none; } .NajmiMenya { padding: 10px; background: yellow; color: black; cursor: pointer; -webkit-user-select: none; } #menyu:checked ~ .menu { display: block; } .ubrattocki { list-style-type: none; }

Не понимаю почему for выполняется

#c #типы


Не думал что задам такой вопрос но при переделывании кода с Pascal в C получилась
странность.
Pascal:

for j := 1 to Length(tmp)-1 do begin


и C

for(j = 1; j <= strlen(tmp)-1; j ++) {


Это оказывается разные вещи при Length(tmp)=0... 
    


Ответы

Ответ 1



Оказывается в отладчике и в реальности вещи ведут себя по-разному. Ломал голову понял , в реальности: (strlen(tmp) - 1) не -1 )) Т.к strlen возвращает size_t (implementation этого типа я не нашёл) а это unsigned то C почему-то работает с (strlen(tmp) - 1) как с unsigned и соотв-но результатом будет макс_ПОЛОЖИТЕЛЬНОЕ_значение_size_t - 1 ... А отладчик считал что (strlen(tmp) - 1) типа signed и отображал -1 как результат и более того в отладчике j <= (strlen(tmp) - 1) == 0 а в реальности j <= (strlen(tmp) - 1) == 1 по указанной причине... Вот так вот "весело" программировать на C) Исправляется это кстати так: for(j = 1; j <= (signed)(strlen(tmp) - 1); j ++)

Ответ 2



Опередили)) #include int main(void) { size_t i = 0; int j = 1; if (j < i - 1) printf("OK\n"); else printf("NO\n"); return 0; } Печатает ОК. Аккуратнее с signed и unsigned типами, у меня компилятор выдает ворнинг на сравнение

Деструкторы в php - какой в них смысл?

#php


Начал изучение магических методов PHP. С большинством все понятно. Но вот __destruct(),
я не могу понять практическое применение его именно в PHP. В каких случаях он нужен
вообще? Время жизни объектов же крайне не большое. Какая практическая польза? 

Всем спасибо за ответы. Конкретно определить самый правильный не могу. Много интересных
вещей написано в комментариях.
    


Ответы

Ответ 1



Деструктор позволяет вашим объектам предоставлять гарантии по управлению ресурсами. В деструкторе вы можете написать логику, которая так или иначе - во время сборки мусора или при выключении VM - будет выполнена, и не оставит ваше приложение в неконсистентном состоянии. Сама логика может быть произвольной - чаще всего в пример приводят файл, который необходимо закрыть. Клиентский код может забыть это сделать, и тогда работу за него произведет деструктор. В качестве более интересного примера можно привести ссылочную систему: например, в системе есть кэш с коротким временем жизни, который самоуничтожается после того, как последний клиент отключился. В этом случае в деструкторе клиента такого кэша должна выполняться логика дерегистрации, чтобы кэш вовремя смог освободить оперативную память. При этом все нельзя забывать, что машина, на которой выполняется код, ненадежна и может упасть прямо посередине какой-либо транзакции - в этом случае код в деструкторе не выполнится.

Ответ 2



деструктор вызываеться при завершение скрипта и при освобождение ссылок т.е. если у нас есть myClass с содержимым class myClass { public function __construct() { echo 'construct'; } public function __destruct() { echo 'destruct'; } } то при выполнение $test = new myClass(); unset($test); //destruct $test = null; //destruct exit(); // destruct Но если смотреть на web программирование. то при завершение скрипта все ресурсы которорые были задействованы освобождаються автоматически(по этому использование сдесь его не целесообразно). Если смотреть на 'демоны' на php. Он будет срабатывать на unset($object), но тут тоже вопрос будет ли этот unset или его не будет, а реализованно всё это будет как register т.е. нужин ли нам instance объекта постоянно и неизменно, в конце концов нужны ли объекты вообще...

Ответ 3



Я когда-то писал класс, который записывает в базу данных активность пользователя, обновляет время последнего посещения, браузер и т.д. Так вот в конструкторе я указывал мне нужные переменные, базы например. А в деструкторе, чтобы не писать одну строчку где-либо в коде, обновлял всю инфу в базе. Может быть это глупо, не знаю. Ну в общем достойное придумать для деструктора что-то можно)

getResources().getColor() is deprecated

#java #android


Почему выводит ошибку getResources().getColor() is deprecated при попытке изменить
цвет фона?
    


Ответы

Ответ 1



Это не ошибка, но предупреждение об устаревании метода. Т.е. он будет работать, но, однажды, может перестать. Теперь надо пользоваться методом ContextCompat#getColor(Context context, int color) вот так: ContextCompat.getColor(context, R.color.your_color); Если заглянуть в исходники сего метода, то можно увидеть как он работает: public static final int getColor(Context context, int id) { final int version = Build.VERSION.SDK_INT; if (version >= 23) { return ContextCompatApi23.getColor(context, id); } else { return context.getResources().getColor(id); } }

Переместить unique_ptr из vector в deque

#cpp #cpp11


Необходимо перенести объект типа std::unique_ptr из вектора в дек.

Пример кода:

using UPtr = std::unique_ptr;

std::deque d;
std::vector v;

for (int i = 0; i < 5; ++i) {
    v.emplace_back(new int(i));
}

for (const auto& item : v) {
    d.emplace_front(std::move(item));
}


Но неожиданно возникает ошибка компиляции:

error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp,
_Dp>&) [with _Tp = int; _Dp = std::default_delete]'
  { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
    ^
In file included from /usr/include/c++/5/memory:81:0,
                 from prog.cpp:4:
/usr/include/c++/5/bits/unique_ptr.h:356:7: note: declared here
       unique_ptr(const unique_ptr&) = delete;


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


Ответы

Ответ 1



Проблема оказалась в квалификаторе const. В цикле for (const auto& item : v) { d.emplace_front(std::move(item)); } переменная item имеет тип const std::unique_ptr&, соответственно при вызове std::move получаем тип const std::unique_ptr&&. Перемещающего конструктора, который бы принимал этот тип, нет, поэтому компилятор использует копирующий конструктор, что и приводит к ошибке. Похожая ошибка рассмотрена у Скотта Мейерса в книге "Эффективный и современный C++" в разделе 5.1.

Положение символа `^` в регулярном выражении

#регулярные_выражения


Простой вроде вопрос. Что означает регулярка "a^b" и можно ли ее найти в какой либо
строке?

import re
p = re.compile("a^b")
p.search("ab") --> None
p.search("a^b") --> None
p.search("a\nb") --> None


Если такое выражение действительно не соответствует никакой строке (действительно,
как может символ a встретиться до начала проверяемой строки), то почему оно вообще
компилируется?

Или, перефразируя, меняется ли смысл символа ^/$ в зависимости от его положения в
регулярном выражении (не внутри квадратных скобок)? Если нет, то почему?

Вопрос безотносительно ЯП, python просто примера ради.
    


Ответы

Ответ 1



^ означает "начало строки" вне зависимости от того, в какой части выражения встретилось. Так же как и $ - конец "строки". Но вот "начал" и "концов" в "строке" может быть несколько. Возьмем пример (на питоне, так на питоне): import re p=re.compile("a.*^b",re.M+re.S) p.search("abc\nabcd") --> None p.search("abc\nbcd") --> <_sre.SRE_Match object; span=(0, 5), match='abc\nb'> При компиляции выражения указаны флаги: re.S заставляет . соответствовать любым символам, включая перевод каретки. re.M заставляет рассматривать "строку", как "многострочную" - т.е. ^ начинает соответствовать не только началу данных, но и точке после любого перевода каретки. Вот мы и получили, что "a" встретился до начала строки. Флаги можно задавать не только вторым параметром compile, но и внутри самой регулярки, конструкциями вроде (?sm). Думаю разработчикам регулярок влом проводить такой глубокий анализ (Есть флаги, нет, а когда они начали действовать), ради выдачи ошибок вроде "начала строки тут быть не может". Собственно по этому никто и не рискует рассматривать ^ и $ по разному, в зависимости от положения (кроме случаев с квадратными скобками, конечно). Пример на regexp101.com

Разница между присваиванием this.state.x = 4 и this.setState({x: 4})

#javascript #reactjs


Я не понимаю в чем разница между вот этими присваиваниями в React: 



this.state.x = 4;






this.setState({
x: 4
})




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


Ответы

Ответ 1



Первый способ - варварски изменить состояние напрямую, не используя внутренние механизмы оптимизации реакта. Документация большими буквами говорит что так делать не надо и стоит считать стейт иммутабельным. Второй способ - правильный и делать надо так. Другое дело, что реакт изменяет стейт не сразу, а когда посчитает нужным согласно логике приложения. Если это конфликтует с логикой вашего приложения - значит вы уже чего-то там неправильно сделали, не согласуясь с принципами заложенными в реакт. Если вам надо что-то делать когда state уже изменился, то setState вторым параметром принимает колбек.

Ответ 2



Кратко смысл в том, что state нужно устанавливать только через setState() – т.к. это вызовет некоторые события, а также является операцией асинхронной. Подробнее первоисточник – документация React компонентов (на англ.) Notes: NEVER mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable. setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value. There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains. setState() will always trigger a re-render unless conditional rendering logic is implemented in shouldComponentUpdate(). If mutable objects are being used and the logic cannot be implemented in shouldComponentUpdate(), calling setState() only when the new state differs from the previous state will avoid unnecessary re-renders. Перевод (отсюда): Примечания: НИКОГДА не изменяйте this.state непосредственно, вызовом setState() который после может заменить изменения которые вы сделали. Обрабатывайте this.state, как будто оно было неизменным. setState() не сразу изменяет this.state, но создает состояние отложенного перехода. Доступ this.state после вызова этого метода потенциально может вернуть существующее значение. Нет никакой гарантии, синхронной работы операций вызова setState, вызовы могут группироваться в целях повышения производительности. setState() всегда будет вызывать повторный рендеринг, пока условная логика рендеринга не реализуется в shouldComponentUpdate(). Если изменяемые объекты используются и логика не может быть реализована в shouldComponentUpdate(), вызывается setState() только тогда, когда новое состояние отличается от предыдущего состояния чтобы избежать ненужного ререндеринга.

Telegram bot php. Inline keyboard. Подскажите, что делать дальше с callback_data. Что бы после нажатия на кнопку inline keyboard - бот ее обработал

#php #telegram_api #telegram_bot


Подскажите, пожалуйста как написать простой сценарий для бота, например:
написал команду "/test",
 бот спрашивает: "Вы уверены?"
пишешь в ответ: "Да"
бот отправляет следующее сообщение по сценарию, если пишешь "нет" - сценарий заканчивается.
Важно, что бы мой ответ "Да" не обрабатывался ботом, пока он не спросит "Вы уверены?"(после
команды /test)  

"First Button","callback_data"=>"test1");
$x2 = array("text"=>"Second Button","callback_data"=>"test2");
$opz = [[$x1,$x2]];
$keyboard=array("inline_keyboard"=>$opz);
$keyboard = json_encode($keyboard);  
      sendMessage($chat_id, "testt2", $keyboard);
      break;


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


Ответы

Ответ 1



Вот рабочий код, если кому нужен. "Google url","url"=>"http://google.com"); $inline_button2 = array("text"=>"work plz","callback_data"=>'/plz'); $inline_keyboard = [[$inline_button1,$inline_button2]]; $keyboard=array("inline_keyboard"=>$inline_keyboard); $replyMarkup = json_encode($keyboard); sendMessage($chat_id, "ok", $replyMarkup); break; } switch($data){ case '/plz': sendMessage($chat_id_in, "plz"); break; } function sendMessage($chat_id, $message, $replyMarkup) { file_get_contents($GLOBALS['api'] . '/sendMessage?chat_id=' . $chat_id . '&text=' . urlencode($message) . '&reply_markup=' . $replyMarkup); }

Расположение методов в многопоточном приложении

#java #многопоточность


При изучении многопоточности в Java столкнулся с непонятным для меня явлением при
перестановке двух методов местами внутри одного блока if.

Ниже пример, в котором запускаются 10 потоков (ссылка на пример кода в GitHub). Их
задача в порядке возрастания Id произвести некую работу, в данном случае вывести сообщение,
содержащее Id потока и значение переменной в вспомогательном классе. 

Пример содержит три класса:  

Класс Main.
В нем в цикле создается 10 экземпляров класса ThreadExample, которые, в свою очередь
являются элементами массива. Id первого экземпляра потока, до его старта заносится
в переменную вспомогательного класса. После этого поочередно в цикле вызывается метод
start всех потоков. 

package lan.example.thread_example;

public class Main {

    static HelperSingletonBillPugh INSTANCE_TEST_THREAD = HelperSingletonBillPugh.getInstance();    
    static ThreadExample[] myThreads = new ThreadExample[10];
    static final int COUNT = 10;

    public static void main(String[] args) { 

        for (int i = 0; i < COUNT; i++) {
            myThreads[i] = new ThreadExample();
             if (i == 0) {              
                INSTANCE_TEST_THREAD.setCurentThreadId(myThreads[i].getId());
             }
             myThreads[i].start();
        }
    }
}


Класс HelperSingletonBillPugh.
Это вспомогательный хелпер класс, реализованрый по типу синглетона способом Била
Пью. Вроде такой тип реализации синглетона считается потокобезопасным. Класс содержит
переменную curentThreadId типа long с Id потока и публичные методы setCurentThreadId(long
threadId), getCurentThreadId(), incremenCurentThreadId() для изменения, чтения и инкремента
значения этой переменной.  

package lan.example.thread_example;

public class HelperSingletonBillPugh {

    private HelperSingletonBillPugh() {
    }

    private static class SingletonHandler {
        private static final HelperSingletonBillPugh INSTANCE = new HelperSingletonBillPugh();
    }

    public static HelperSingletonBillPugh getInstance() {
        return SingletonHandler.INSTANCE;
    }

    private long curentThreadId = 0L;   // Id потока, который должен выполнить вывод
сообщения

    public long getCurentThreadId() {
        return this.curentThreadId;
    }

    public void setCurentThreadId(long threadId) {
        this.curentThreadId = threadId;
    }

    public void incremenCurentThreadId() {
        this.curentThreadId++;
    }
}


Класс ThreadExample.
Класс наследник Thread. В нем вечный цикл, с паузами и постоянным сравнением значения
getId() потока с переменной curentThreadId экземпляра вспомогательного класса HelperSingletonBillPugh.
Если равенство выполняется, то выводится сообщение, переменная curentThreadId увеличивается
на 1 и поток завершает работу.

package lan.example.thread_example;

public final class ThreadExample extends Thread {

    static HelperSingletonBillPugh INSTANCE_TEST_THREAD = HelperSingletonBillPugh.getInstance();

    @Override
    public void run() {

        while (true) {

            if (INSTANCE_TEST_THREAD.getCurentThreadId() == this.getId()) {

                // Раскомментируй следующую строку
                //INSTANCE_TEST_THREAD.incremenCurentThreadId();
                System.out.println("Print " + this.getName()
                        + " ### ID:" + this.getId()
                        + " ### getCurentThreadId: " + INSTANCE_TEST_THREAD.getCurentThreadId());

                // Закоментируй следующую строку
                INSTANCE_TEST_THREAD.incremenCurentThreadId();

                break;
            }
        }
    }
}


В данном примере кода сообщения выводятся последовательно и потоки завершают свою
работу, все стабильно от запуска к запуску, вот такой вывод:

Print Thread-1 ### ID:11 ### getCurentThreadId: 11
Print Thread-2 ### ID:12 ### getCurentThreadId: 12
Print Thread-3 ### ID:13 ### getCurentThreadId: 13
Print Thread-4 ### ID:14 ### getCurentThreadId: 14
Print Thread-5 ### ID:15 ### getCurentThreadId: 15
Print Thread-6 ### ID:16 ### getCurentThreadId: 16
Print Thread-7 ### ID:17 ### getCurentThreadId: 17
Print Thread-8 ### ID:18 ### getCurentThreadId: 18
Print Thread-9 ### ID:19 ### getCurentThreadId: 19
Print Thread-10 ### ID:20 ### getCurentThreadId: 20


Но стоит только переставить местами метод INSTANCE_TEST_THREAD.incremenCurentThreadId()
и метод System.out.println() (в коде помечено где убрать и добавить коммент) и результат
становится не стабильным и для меня не понятным, хотя перестановка осуществляется внутри
блока if. Если быть точнее, то он не всегда стабильный, то есть, несколько запусков
может пройти вполне корректно. Вот вывод одного из запусков после перестановки методов
местами:

Print Thread-1 ### ID:11 ### getCurentThreadId: 12
Print Thread-2 ### ID:12 ### getCurentThreadId: 15
Print Thread-6 ### ID:16 ### getCurentThreadId: 17
Print Thread-5 ### ID:15 ### getCurentThreadId: 16
Print Thread-4 ### ID:14 ### getCurentThreadId: 15
Print Thread-7 ### ID:17 ### getCurentThreadId: 21
Print Thread-3 ### ID:13 ### getCurentThreadId: 21
Print Thread-8 ### ID:18 ### getCurentThreadId: 21
Print Thread-9 ### ID:19 ### getCurentThreadId: 21
Print Thread-10 ### ID:20 ### getCurentThreadId: 21


Из примера видно, что потоки выводят сообщения хаотично и значение переменной getCurentThreadId
временами отличается от Id текущего потока более чем на 1. Что-же происходит при изменении
местами двух этих методов? Дело в том, что когда System.out.println() находится перед
INSTANCE_TEST_THREAD.incremenCurentThreadId(), то у меня нет ни одного непредсказуемого
результата. Можно ли считать это стабильной работой или все-таки эта стабильность обманчива
и при неких обстоятельствах пример отработает не корректно?

P/S. На всякий случай уточню. Я немного имею представление про Java Memory Model,
оператор volatile, блок synchronize, про кэши данных и атомарность, правда глубоких
знаний пока нет, но добиться гарантированной стабильной работы примера скорее всего
смогу. Интересует именно понимание того, какие-такие серьезные изменения происходят
при перестановки местами этих двух методов, так влияющие на результат работы примера.
И, как следствие, можно ли считать стабильной работу примера в первом случае и почему?
Проверял пример на разных операционных системах (Linux Mint 32 и 64 bit, Windows 10
64 bit), но правда только с Oracle JDK.
    


Ответы

Ответ 1



Дело в том, что операция инкремента не является атомарной, а состоит из двух операций: чтения текущего значения и записи увеличенного значения. Если развернуть ее, то цикл в первом сценарии будет выглядеть так: while (...) { <чтение id> // вывод на консоль <чтение id> // инкремент, шаг 1 <запись id> // инкремент, шаг 2 } Такое расположение операций вкупе с хитрым условием while по сути служит локом, дающим доступ к телу цикла только одному потоку. Сначала только первый поток заходит внутрь цикла, а остальные просто прокручивают его ("блокируются"). Когда первый поток записывает увеличенный id и выходит из цикла, то уже он начинает прокручивать цикл ("блокируется"), а второй поток заходит внутрь. И так по очереди для всех потоков. Именно за счет инкремента все потоки получают этот доступ последовательно, согласно своим номерам. Важным здесь является то, что запись id по сути освобождает "лок" текущего потока и "разрешает" выполнение другого потока. Пока она идет последней в теле цикла, проблем не возникает. Во втором сценарии цикл выглядит следующим образом: while (...) { <чтение id> // инкремент, шаг 1 <запись id> // инкремент, шаг 2 <чтение id> // вывод на консоль } Теперь мы видим, что "лок" освобождается чуть раньше. К чему это может привести? К тому, что между записью id и вторым чтением id могут начать исполняться другие потоки, поскольку "лок" свободен и доступ к телу цикла может получить другой поток. Т.е. возникает классическая гонка, в результате которой может происходить следующее: поток на втором чтении id может получить уже обновленное значение (это объясняет, например, почему Thread-2 выводит 15, а не 13) вывод в консоль может перепутаться из-за того, что другие потоки "встревают" сразу после записи id while (...) { <чтение id> // инкремент, шаг 1 <запись id> // инкремент, шаг 2 <возможное исполнение других потоков> <чтение id> // вывод на консоль (потенциально уже нового значения) } Сценарий выполнения для приведенного вами в вопросе лога может выглядеть так: все потоки начинают выполнение все потоки кроме потока 1 "блокируются" на условии while поток 1 читает id (=11) поток 1 увеличивает id (=12) поток 2 "разблокируется" поток 2 читает id (=12) поток 1 читает id и выводит на консоль (=12) поток 1 "засыпает" поток 2 увеличивает id (=13) поток 3 "разблокируется" поток 3 читает id (=13) поток 3 увеличивает id (=14) поток 4 "разблокируется" поток 4 читает id (=14) поток 4 увеличивает id (=15) поток 2 читает id и выводит на консоль (=15) поток 2 "засыпает" поток 5 "разблокируется" поток 5 читает id (=15) поток 5 увеличивает id (=16) поток 6 "разблокируется" поток 6 читает id (=16) поток 6 увеличивает id (=17) поток 6 читает id и выводит на консоль (=17) поток 6 "засыпает" ...и так далее

Ответ 2



На самом деле всё достаточно просто. Вы создаёте несколько потоков, а данный метод разрешает выполняться только 1 с нужным id. Этот id содержится в переменной INSTANCE_TEST_THREAD.getCurentThreadId(). Теперь вы меняете местами. Может быть такая ситуация: 1 поток зашёл, значение 1 1 поток увеличил значение до 2 2 поток зашёл значение 2 2 поток увеличил значение до 3 1 поток вывел 3 (!!) 2 поток вывел 3 (!!) 3 поток увеличил значение до 4 3 поток вывел 4. Думаю идею вы поняли. Поменяв местами мы делаем гонку потоков с непредсказуемым порядком выполнения, по сути сделав весь код синхронизации нерабочим.

cron и несколько одновременных заданий

#linux #cron


Если я поставлю в cron две задачи на исполнение в одно и тоже время, то как они будут
запущены на выполнение (и выполняться): параллельно или поочередно (вторая выполнится
после первой)?
    


Ответы

Ответ 1



Если задачи описаны в crontab в разных строках то они будут выполнятся независимо друг от друга, в том числе и параллельно, при совпадении времени. Если надо, что бы две задачи выполнились строго друг за другом то можно написать их в одну строку, разделив точкой с запятой: 01 12 * * * mike task1 parameters; task2 parameters; ... Кроме того, если к времени своего следующего старта предыдущий экземпляр задачи еще не завершился, крон не будет ничего проверять и запустит вторую копию. Отслеживание параллельного запуска и препятствование этому остается на совести самой выполняемой задачи.

В чем разница между QPushButton и QPushButtonPrivate?

#cpp #qt


Есть целая серия классов в Qt с постфиксом Private. 

В чем разница между ними и обычными классами? 
    


Ответы

Ответ 1



Это классы для реализации идиомы PIMPL. Pimpl — Pointer to private implementation. Основная идея этого паттерна — это вынести все приватные члены класса и, в некоторых случаях, функционал в приватный класс. Зачем это нужно, можете почитать здесь и здесь.

Генерация случайной ASCII строки

#python


Генерирую ASCII-строку следующим образом:

def buildblock(size):
    out_str = ''
    for i in range(0, size):
        a = random.randint(65,90)
        out_str += chr(a)
    return(out_str)


Но в таком случае генерируются символы только верхнего регистра. Например, FGLKJDSDH.
А как сделать так, чтобы генерировались строки и нижнего и верхнего регистра одновременно?
По типу YjhmrdVDgm
    


Ответы

Ответ 1



Как-то так: import random import string def buildblock(size): return ''.join(random.choice(string.ascii_letters) for _ in range(size)) Можно добавить ещё и цифры: import random import string def buildblock(size): return ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(size)) Действующий код

Ответ 2



from random import choice from string import ascii_letters print(''.join(choice(ascii_letters) for i in range(12)))

Процесс не может получить доступ к файлу (Исключение HRESULT: 0x80070020)

#iis #faq #iis_express


При попытке запустить сайт в IIS появляется окно с ошибкой: 


  Процесс не может получить доступ к файлу, так как этот файл занят
  другим процессом. (Исключение из HRESULT: 0x80070020)




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



Нашел вот эту статью: Ошибка 0x80070020 при использовании сайта Центра обновления
Windows или Центра обновления Майкрософт для установки обновлений

Обновил систему, перезагрузил компьютер, очистил мусор, скопившийся в системе, перезагрузил
компьютер - не решило проблемы. Да, понимаю, что ошибка в IIS, но на всякий случай
проверил. Антивируса у меня на компьютере нет.
    


Ответы

Ответ 1



Решение проблемы оказалось следующим: Запустить командную строку и написать в ней команду: netstat -aon | find ":80" в результате, в окне командной строки будет видно следующее: Необходимо найти процесс, который прослушивает необходимый нас порт. Здесь мы видим, что процесс 3544 прослушивает наш занятый 80 порт. Открываем диспетчер задач и ищем там наш процесс по идентификатору(ИД): Как видим - это программа Skype. Открываем Skype, и выполняем следующие действия: Инструменты -> Настройки... -> Дополнительно -> Соединение В появившемся окне будет несколько настроек, нас интересует «Для дополнительных входящих соединений следует использовать порты 80 и 443», поэтому убираем активную галочку: Для дополнительных входящих соединений следует использовать порты 80 и 443 Сохраняем изменения. Перезапускаем Skype, заходим в Диспетчер служб IIS и запускаем сайт, который во время попытки запуска валился с ошибкой. Подсказка для решения была найдена на MSDN: Ошибка 0x80070020 при запуске веб-сайта в IIS 7.0

PHP - Запись в файл в многопользовательском режиме

#php #flock


Записываю протокол событий вот таким скриптом:




Но возникает вопрос:
Что будет, если вызовов будет слишком много и со всех сторон?
Не выйдет ли, что пока я залочил файл в одном вызове скрипта, другой тоже попытался,
ругнулся и ничего не записал?
Как предусмотреть это в коде?
    


Ответы

Ответ 1



Функция flock() создает для файла флаг, указывающий на то, что с файлом идет работа. При этом другой скрипт или другая программа, умеющая распознавать подобный флаг, сделает соответствующие выводы и содержимого файла не нарушит. По умолчанию, данная функция будет ждать получения эксклюзивной блокировки на запись, это поведение можно изменить с помощью параметра LOCK_NB. Если у Вас будет многопоточная запись в файл, то каждый следующий запрос просто будет дольше выполняться из-за наличия блокировки. Кстати, весь ваш код можно перенести в одну конструкцию: file_put_contents('track.log', "$trnum \t" . date("Y-m-d h:ia") ."\n", LOCK_EX | FILE_APPEND);

Ответ 2



Что будет, если вызовов будет слишком много и со всех сторон? flock реализует механизм блокировки - синхронизации, при которой все, кто хочет завладеть ресурсом и превышают некоторую квоту (определяемую типом блокировки), просто ожидают в очереди до тех пор, пока ресурс не освободится. Поэтому в данном случае доступ к файлу будет синхронизирован, и запись в файл будет вестись только одним процессом, а все остальные будут ожидать освобождения блокировки.

Yield и потоки и все все все

#c_sharp #многопоточность #yield


Оператор yield

class UserCollection
{
    public static IEnumerable Power()
    {
        yield return "Hello world!";
    }
}


Оператор yield из .Net Reflector - перенесенный в рялии C# и Visual Studio

class UserCollection
{
    public static IEnumerable Power()
    {
        return new ClassPower(-2);
    }

    private sealed class ClassPower : IEnumerable,  IEnumerator,
IEnumerator, IDisposable
    {
        // Поля.
        private int state;
        private object current;
        private int initialThreadId;

        // Конструктор.
        public ClassPower(int state)
        {
            this.state = state;
            this.initialThreadId = Thread.CurrentThread.ManagedThreadId;
        }

        //private bool IEnumerator.MoveNext() // Так в Рефлекторе
        bool IEnumerator.MoveNext()
        {
            switch (this.state)
            {
                case 0:
                    this.state = -1;
                    this.current = "Hello world!";
                    this.state = 1;
                    return true;

                case 1:
                    this.state = -1;
                    break;
            }
            return false;
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            if ((Thread.CurrentThread.ManagedThreadId == this.initialThreadId) &&
(this.state == -2))
            {
               this.state = 0;
               return this;
            }
            return new UserCollection.ClassPower(0);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            // Так в Рефлекторе 
            //return this.System.Collections.Generic.IEnumerable.GetEnumerator(); 

            return (this as IEnumerable).GetEnumerator();
        }

        void IEnumerator.Reset()
        {
            throw new NotSupportedException();
        }

        void IDisposable.Dispose()
        {
        }

        // Свойства.
        object IEnumerator.Current
        {
            get
            {
                return this.current;
            }
        }

        object IEnumerator.Current
        {
            get
            {
                return this.current;
            }
        }
    }
}


Меня интересует строка и как ее комментирует автор

this.initialThreadId = Thread.CurrentThread.ManagedThreadId;


данная строка свидетельствует о синхронизации и доступа к этой коллекции как к разделяемому
ресурсу.
Почему именно при работе с yield коллекция воспринимается как разделяемый ресурс
и нужна вообще в принципе работа с потоками?

См. рисунок как я понял работу оператора yield
и может т.к это в эту коробку все операторы yield помещают свое значение , то может
поэтому нужна работа с потоками? 

    


Ответы

Ответ 1



Обратите внимание на функцию IEnumerable.GetEnumerator(). Данный объект реализует как IEnumerable, так и IEnumerator. Состояние -2 означает, что объект «свежий», только что вернулся из вызова функции Power, и его энумерация ещё не начиналась. В этой ситуации, когда тут же вызывается GetEnumerator в том же потоке (а это происходит в подавляющем большинстве случаев: например, при вызове foreach (var x in Power())), то из соображений эффективности можно вернуть этот же объект (т. к. он служит и энумератором тоже). Но если энумерация уже прошла, внутреннее состояние объекта может быть испорчено, для этого случая возвращают новый объект. Точно так же если энумерация производится в другом потоке, то чтобы избежать необходимости синхронизации, лучше создать новый объект. Остановимся специально на последнем пункте. Если вдруг IEnumerable уйдёт в другой поток, и оттуда буден вызван метод GetEnumerator(), и одновременно в основном потоке будет тоже вызван этот же метод, то без проверки thread id может случиться так, что оба потока пройдут одновременно проверку this.state == -2, и получат один и тот же объект! В результате при энумерации они будут мешать друг другу. В этом случае мы даём «выиграть» тому же потоку, который создал объект. Таким образом, это специальная оптимизация для обычного случая (выделение только одного объекта); «необычные» случаи использования проходят более сложным путём. Подробнее по теме: Jon Skeet, C# in Depth. Iterator block implementation details: auto-generated state machines.

Определение свойства, по которому нужно произвести фильтр

#c_sharp #запрос #рефлексия


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

public class User {
    public string Name {get; set;}
    public string Nick {get; set;}
}


И мне нужно вытащить из базы некоторых пользователей но критерий заранее не известен,
в запросе Name или Nick могут быть null. 

В данный момент это выглядит примерно так: 

//это часть когда находится в классе user
IQarable query ... тут создается query и передается в метод ниже
...

if (!string.IsNullOrEmpty(Name))
        {
                query = query.Where(x => x.VenueName.Contains(Venue));
        }

if (!string.IsNullOrEmpty(Nick))
        {
                query = query.Where(x => x.City.Contains(City));

        } //и так далее 


Внутри блоков If есть еще кое какая проверка, вот поэтому я пытаюсь это вынести в
1 метод, но не в этом суть.

Я пытаюсь сделать метод, который принимает query и свойство в виде строки, по которому
нужно выполнить Where(...), что бы это выглядело так 

if (!string.IsNullOrEmpty(Name))
        {
                query = SearchMethod(query, "Name", "Jhon");
        }


Я не могу представить как мне заменить выражение Where(x => x./*тут свойство, которое
каким-то образом определено*/.Contais("SearchingValue")) что-то другое, что может вычислить
свойство по которому я веду поиск, и подставить его в это выражение. По рефлексии я
смог получить только само свойство.

Type t = this.GetType();
PropertyInfo prop = t.GetProperty("EventName");


Прошу вашей помощи в решении этой проблемы.
    


Ответы

Ответ 1



Отфильтровать IQueryable по Func нельзя. (У вас получится IEnumerable.) Для сохранения IQueryable вам придётся строить Expression (и кажется, вручную). Вот документация. Для вашего случая, если нужно сравнивать значение с константой, можно сделать так (не тестировал, возможны вылеты в рантайме): IQueryable Filter(IQueryable original, Expression> еxtractor, V value) { return original.Where(ProjectionEquals(еxtractor, value)); } Expression> ProjectionEquals(Expression> еxtractor, V value) { var body = Expression.Equal(еxtractor.Body, Expression.Constant(value)); return Expression.Lambda>(body, еxtractor.Parameters[0]); } Пользоваться так: query = Filter(query, x => x.Name, "Jhon"); Внутри Filter можно накрутить, понятно, более сложную логику. Если всё же очень хочется потерять проверки на этапе компиляции и передавать имена свойств как строки, можно так: IQueryable Filter(IQueryable original, string propName, V value) { return original.Where(PropertyEquals(propName, value)); } Expression> PropertyEquals(string propName, V value) { var parameter = Expression.Parameter(typeof(T), "t"); var left = Expression.PropertyOrField(parameter, propName); var body = Expression.Equal(left, Expression.Constant(value)); return Expression.Lambda>(body, parameter); } и пользоваться так: query = Filter(query, "Name", "Jhon"); Понятна схема? Для примера, если вам нужно Contains: Expression> GetContains(string propName, string value) { var parameter = Expression.Parameter(typeof(T), "t"); var prop = Expression.Property(parameter, propName); var containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) }); var valueAsExpr = Expression.Constant(value, typeof(string)); var contains = Expression.Call(propOrField, containsMethod, valueAsExpr); return Expression.Lambda>(contains, parameter); } Как подсказывает в комментарии @Pavel Mayorov, последнюю функцию можно переписать проще: Expression> GetContains(string propName, string value) { var parameter = Expression.Parameter(typeof(T), "t"); var prop = Expression.Property(parameter, propName); var valueAsExpr = Expression.Constant(value, typeof(string)); var contains = Expression.Call(prop, "Contains", null, valueAsExpr); return Expression.Lambda>(contains, parameter); }

Ответ 2



Исходный код у меня получился такой if (!string.IsNullOrEmpty(EventName)) { query = query.ContainsOrStartWithQuery(x => x.EventName, EventName); } if (!string.IsNullOrEmpty(Venue)) { query = query.ContainsOrStartWithQuery(x => x.VenueName, Venue); } Сам метод находится в статическом классе, он получился довольно большим. public static class ExpressionHelper { #region EgorAdded private static MethodInfo containsMethod; private static MethodInfo startsWithMethod; static ExpressionHelper() { containsMethod = typeof(string).GetMethods().First(m => m.Name == "Contains" && m.GetParameters().Length == 1); startsWithMethod = typeof(string).GetMethods().First(m => m.Name == "StartsWith" && m.GetParameters().Length == 1); } public static Expression> AddContains(this Expression> selector, string value) { var body = selector.GetBody().AsString(); var x = Expression.Call(body, containsMethod, Expression.Constant(value)); LambdaExpression e = Expression.Lambda(x, selector.Parameters.ToArray()); return (Expression>)e; } public static Expression> AddStartsWith(this Expression> selector, string value) { var body = selector.GetBody().AsString(); var x = Expression.Call(body, startsWithMethod, Expression.Constant(value)); LambdaExpression e = Expression.Lambda(x, selector.Parameters.ToArray()); return (Expression>)e; } private static Expression GetBody(this LambdaExpression expression) { Expression body; if (expression.Body is UnaryExpression) body = ((UnaryExpression)expression.Body).Operand; else body = expression.Body; return body; } private static Expression AsString(this Expression expression) { if (expression.Type == typeof(string)) return expression; MethodInfo toString = typeof(SqlFunctions).GetMethods().First(m => m.Name == "StringConvert" && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(double?)); var cast = Expression.Convert(expression, typeof(double?)); return Expression.Call(toString, cast); } public static IQueryable ContainsOrStartWithQuery(this IQueryable query, Expression> selector, string search) { if (search.StartsWith("*")) { search = search.Substring(1); query = query.Where(selector.AddContains(search)); } else { query = query.Where(selector.AddStartsWith(search)); } return query; } } https://stackoverflow.com/questions/16460057/call-contains-method-in-linq-to-entities-expression-on-a-type-other-than-strin Руководствовался этим вопросом

Возможные причины появления Debug Error - R6025 pure virtual function call

#cpp


Моя программа завершает своё выполнение с ошибкой:


  R6025 pure virtual function call. 


Как вообще такое возможно, чтобы была вызвана чисто виртуальная функция? Экземпляр
абстрактного класса не может быть создан. Если функция виртуальная, то используется
позднее связывание, т.е. вызываться должна функция по типу объекта, а не по типу ссылки.
Т.е. чисто виртуальная функция не может быть вызвана.
Что мне делать?
    


Ответы

Ответ 1



В ответе @AnT было объяснено, как может получиться такая ошибка. (Это вызов виртуальной функции, прямо или косвенно, в конструкторе или деструкторе.) Теперь вопрос в том, что делать. Для начала, попробуйте воспроизвести проблему под отладчиком. Microsoft советует заменить абстрактный метод на вызов DebugBreak, или запустить из-под отладчика и установить точки останова на _purecall в PureVirt.c. Но у меня проблема отловилась в Visual Studio 2015 и без этих заклинаний. Вы увидите в стеке, какой именно абстрактный метод вы вызываете, и поискав по стеку конструктор или деструктор объекта данного класса, найдёте ошибочный вызов. Помните, что это вполне может быть косвенный вызов, через другие функции. Если вам в конструкторе реально понадобился виртуальный метод, возможно, ваш конструктор делает слишком много. Может быть, имеет смысл вынести функциональность, требующую виртуальной функции, в отдельный метод, а конструктор закрыть, и конструировать класс через статическую фабричную функцию. Ещё одной причиной данной ошибки может служить вызов функции по указателю на уже умерший объект. Если деструктор объекта отработал, то при условии, что память, занимаемая объектом, никем не затёрта, при попытке вызова метода по мёртвому указателю будет также выполнен чисто виртуальный метод (эта ситуация аналогична вызову метода в деструкторе), с понятными последствиями. Так что если в вашем стеке нету конструктора/деструктора, всё куда хуже: у вас умер указатель.

Ответ 2



Если функция виртуальная, то используется позднее связывание, т.е. вызываться должна функция по типу объекта, Это верно, но не забываем, что пока работает конструктор или деструктор, текущий тип объекта (в т.ч. для целей виртуальных вызовов) - этот тот тип, чей конструктор/деструктор сейчас активен. Поэтому классический способ вызвать pure virtual функцию - это вызвать ее из конструктора через функцию-посредник class C { public: C() { foo(); } virtual void pure() = 0; void foo() { pure(); } }; class D : C { public: virtual void pure() {} }; int main() { D d; } Несмотря на то, что в конечном итоге мы создаем объект класса D, во время работы конструктора класса C никакого объекта класса D еще не существует. Все виртуальные вызовы во время работы конструктора класса C будут работать так, как будто мы имеем дело с объектом класса C. Формально для достижения того же эффекта вы можете попытаться вызвать pure прямо из C::C(), но компиляторы, как правило, оптимизируют такие виртуальные вызовы и заменяют их на невиртуальные. При этом они сразу замечают подвох еще на стадии компиляции. Обмануть компилятор в таком варианте можно, сделав вызов не напрямую, а через указатель на метод, что дает нам немножко более компактный способ достичь того же эффекта class C { public: C() { (this->*&C::pure)(); } virtual void pure() = 0; }; class D : C { public: virtual void pure() {} }; int main() { D d; }

Ответ 3



Моя проблема заключалась в том, что я обращался к "мёртвой ссылке", как было предсказано @VLadD. Вот упрощённая версия моего кода, с такой же ошибкой: #include using namespace std; class A { public: virtual void f()=0; virtual ~A(){} }; class B:public A { public: void f(){} }; class C:public A { A&a; public: C(A&a):a(a){} void f(){a.f();} }; class D { public: D(A&a){ a.f(); } }; int main() { C c( (B()) ); D d(c); system("pause"); } Если изменить метод main вот так: int main() { B b = B(); C c( b ); D d(c); system("pause"); } То ошибки не случится, ибо объект B() не будет удалён.