Страницы

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

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

Как добавить на растровой карте анимированный маршрут до офиса

#javascript #html #css #svg #анимация


Часто заказчики просят в разделе сайта "Контакты" добавить фрагмент карты с маршрутом
до их офиса.     

Вот например фрагмент карты Санкт-Петербурга. Допустим офис  находится во внутриквартальной
территории между улицами Восстания и Лиговским проспектом. Картинка ниже    

   

С прокладыванием статического маршрута замечательно справляется Yandex карты 

Но необходимо, как-то анимировать этот статичный маршрут в виде рисования линии от
метро до офиса.  Причём при изменении масштаба карты анимационная линия не должна смещаться
в стороны относительно улиц карты, по которым проложен маршрут. 

Другими словами, всё должно быть адаптивным, всё должно изменяться в размерах пропорционально.

    

Допустимо применение любой техники для реализации анимации и адаптивности приложения:
svg,css,javascript или все техники вместе.      

Это учебный вопрос-пример, но он дополнительно имеет и практическое применение. Допускается
любое количество ответов, все будут отмечены.
    


Ответы

Ответ 1



Первый шаг: Начнём с того, что нам нужно найти место на карте (в данном случае будем использовать Яндекс.Карты). После этого нужно создать path с помощью векторного редактора Inkscape и сохранить в формате .svg: Второй шаг: Достаём path из файла и включаем его в наш HTML-документ: P.S. Также мы добавим background у viewport'a c помощью тега Теперь нам нужно изменить стили path, как показано на картинке(у автора данного вопроса): Третий шаг (последний): Последняя задача - это создать анимацию path. Для этого мы изначально узнаем его длину, после добавим stroke-dashoffset & stroke-dasharray и заключим тег внутрь самого path. 1. Чтобы узнать длину path, нам нужно написать функцию на JS: P.S. Допишем полученное число в stroke-dashoffset & stroke-dasharray function LengthofPath() { var path = document.querySelector('#mainPath'); var len = Math.round(path.getTotalLength()); alert("Length - " + len); }; 2. Осталось добавить анимацию в path: P.S. Здесь подробно изложено про парные и одиночные теги Все фрагменты кода отображаются адаптивно, как и просил автор вопроса.

Способы позиционирования и их разумное применение

#html #css


Вопрос банальный, но надеюсь простите новичку;) 

Теорию и значение свойств position (в вопросе интересует только мнение об absolute
и relative) и отступов я конечно знаю, но дабы впоследствии не наступать на грабли
и избежать возникновения недопониманий объясните, пожалуйста, в каких случаях какой
способ позиционирования 


абсолютное
относительное
margin,padding


предпочтительнее использовать. Я конечно понимаю, что теоретически можно сайт и на
only absolute'е сверстать но я хотел бы услышать примеры, где определенное свойство
было бы более уместно (или даже незаменимо) относительно других. 

Заранее спасибо за помощь начинающему верстальщику;)
    


Ответы

Ответ 1



Наверное, это не будет прямым ответом автору, где какой способ позиционирования использовать (для этого нужно было бы грепнуть все свёрстанные проекты, выделить и сгруппировать кейсы применения), но, надеюсь, мой ответ окажется полезным. Основная разница применения этих способов позиционирования в том, как они взаимодействуют с другими элементами и относительно чего позиционируются — это даёт основное понимание, где какой способ применить. absolute Может использоваться для попапов, плавающих декоративных элементов, контролов (например, выпадающего меню). Такой способ позиционирования вынимает элемент из контекста, соответственно, его можно использовать, когда другие элементы никак не завязаны на абсолютно позиционируемый элемент. Также top и left этого элемента Также absolute удобно использовать для вертикального и/или горизонтального центрирования блока в блоке. При этом нужно учитывать, что внешний блок может уменьшиться и центрированный блок уже не влезет: .wrapper { width: 100vw; height: 100vh; background: #ccc; } .inner { background: #000; width: 50px; height: 50px; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
relative Способ позиционируется относительно других элементов, вынимает его из контекста, но не двигает другие элементы. Его удобно использовать, когда нужно подвинуть какой-то элемент и не "зацепить" находящиеся рядом. Также его удобно использовать для выносных элементов — степени, индексы в химических формулах, т. к. он ведёт себя более предсказуемо, чем vertical-align. .jump { position: relative; top: -10px; } span { position: relative; top: 10px; }

The quick brown fox jumps over the lazy dog

The quick brown fox jumps over the lazy dog

The quick brown fox jumps over the lazy dog

Важно помнить, relative задаёт, условно, нулевую точку для абсолютно позиционированных элементов, которые находятся внутри него. Также relative удобно использовать в таких случаях (не смог вспомнить ни одного примера из реальной жизни, но точно помню что применял такой подход): span { position: relative; z-index: 100; } .text { position: relative; } .over { width: 500px; height: 100px; top: 0; left: 10%; background: red; position: absolute; }
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean tellus dui, accumsan sed odio in, dictum ornare risus. Donec viverra ipsum vitae nisi ornare vehicula. Proin tempus et ipsum eu porta. Duis vitae luctus mi, sit amet aliquet mauris. Nulla facilisi. Sed suscipit nunc ac augue volutpat congue. Donec sollicitudin venenatis est sed feugiat. Vestibulum at risus metus.
margin Margin не вынимает элемент из контекста и при изменении положения влияет на другие элементы. Основное использование в качестве позиционирования — это margin с отрицательными значениями. Это можно использовать, например, для того чтобы "подтянуть" блок на странице повыше к другому блоку, если у него задан слишком большой отступ снизу (это не совсем хорошо, но иногда другого выхода нет), при этом остальные элементы, находящиеся ниже, также подтянутся выше: .relative-jump { position: relative; top: -10px; } .margin-jump { margin-top: -10px; }

The quick brown fox jumps over the lazy dog

The quick brown fox jumps over the lazy dog

The quick brown fox jumps over the lazy dog

The quick brown fox jumps over the lazy dog

The quick brown fox jumps over the lazy dog

The quick brown fox jumps over the lazy dog

Отрицательный margin часто использовался для задания порядка колонок (без изменения html) в мультиколоночных дизайнах. Также отрицательный margin можно использовать в качестве костыля с абсолютно позиционированными элементами чтобы задать им дополнительный отступ, не ломая уже созданный код. Ещё одно применение — это горизонтальное центрирование блока внутри другого блока с использованием margin: 0 auto (эдакий text-align: center для блочных элементов): .wrapper { background: #ccc; height: 200px; } .inner { margin: 0 auto; width: 100px; background: #000; color: #fff; }
inner text
padding Padding я бы не рассматривал как способ позиционирования: он применяется для отступов с разных сторон. Важно помнить что это внутренний отступ до border. Единственный приём относительно позиционирования который бы я привёл — это отступы элемента + box-sizing: .wrapper { padding: 20px; box-sizing: border-box; }

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas pulvinar, arcu sit amet fringilla iaculis, augue ipsum vulputate augue, posuere mattis sem massa et urna. Mauris aliquam quam id mollis tempus. Donec eu tempus quam. Donec tincidunt imperdiet purus, quis aliquet massa convallis sit amet. Maecenas sollicitudin tempor justo nec cursus. Proin luctus sapien a lobortis suscipit. Nulla vitae tristique velit. Etiam luctus leo sit amet rutrum pellentesque.

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

Ответ 2



Позиционирование отделяет элемент от общей картины и позволяет сдвигать его по всем векторам: x, y и z. Можно представить, что позиционированный элемент отрывается и подлетает над плоскостью (которой является страница), ну как летающий юнит в Warcraft 3, например, и затем его можно смещать в любую сторону, указывая координаты свойствами left и right, а также z-index. При этом абсолютный элемент теряет свою позицию и его координаты отсчитываются от краёв контейнера (контейнером для такого элемента становится первый родительский элемент с НЕ статичным позиционированием или вся страница). Потеря позиции также значит, что его игнорируют все прочие элементы на странице. При left и right равным auto, абсолютный элемент встаёт туда, где он должен бы был быть, если бы он не потерял своей позиции. Относительно позиционированный элемент своей позиции не теряет, с ним всё также считаются все остальные НЕ абсолютные элементы, а его смещение по координатам происходит относительно его текущей позиции. Важно знать, что смещение относительного элемента свойствами left и top не меняет его фактической позиции, а только визуально смещает в сторону, все прочие элементы будут думать, что он так и стоит, где стоял. Маргин же просто двигает элемент в определённую сторону, тоесть меняет его фактическое местоположение. Паддинг не из этой оперы, он добавляет пустого пространства внутрь элемента, но иногда и его можно использовать для позиционирования. P.S. Только написав всё это вспомнил, что ТСу нужны были примеры использования, а не объяснения что к чему, но такой талмуд удалять жалко. Только не кидайте камнями, считайте я просто релаксируя написать что-то по теме захотел.

Ответ 3



Как можете видеть, ниже 2 картинки и одна находится под другой Это не сверстаешь без позиционирования одними margin'ами и padding'ами .about-us { background-color: #000; min-width: 1200px; min-height: 600px; } .about-us .wrapper { display: -webkit-box; display: -ms-flexbox; display: flex; width: 1200px; margin: 0 auto; } .about-us__images { position: relative; } .about-us__images img:first-child { position: absolute; top: 175px; z-index: 5; width: 302px; } .about-us__images img:last-child { margin-left: 65px; z-index: 2; width: 419px; } .about-us__text { display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-orient: vertical; -webkit-box-direction: normal; -ms-flex-direction: column; flex-direction: column; -webkit-box-align: start; -ms-flex-align: start; align-items: flex-start; -webkit-box-pack: end; -ms-flex-pack: end; justify-content: flex-end; margin-left: 152px; } .about-us__theme { color: #95a7ff; font-family: "Fightspirit"; font-size: 20px; font-weight: 400; line-height: 30px; } .about-us__paragraph { margin-top: 35px; color: #ffffff; font-family: "Fightspirit"; font-size: 50px; font-weight: 400; line-height: 30px; } .about-us__article { margin-top: 63px; color: #ffffff; font-family: Montserrat; font-size: 13px; font-weight: 400; line-height: 36px; letter-spacing: 0.35px; }
Graffiti - Love Share Graffiti - Moro
About us Lorem Ipsum Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur sollicitudin aliquam suscipit. Vestibulum ligula dolor, vehicula ac metus lobortis, lobortis maximus mauris


Как нарисовать ёлку используя только символы “_” и “*”?

#javascript


Начал изучать javascript. Застрял на задаче. 


  Нарисовать елку с символами "_" и "*". Вывести в консоль браузера. И в каждом втором
уровне елки должна быть игрушка в виде "o", которая расположена в случайной позиции уровня.


Мой код сейчас такой. Застряла на игрушке. 



function stars(n) {
    for (var i = 0; i < n; i++) {
        var str ="_";
        if(i%2 !== 0){
            
        }
        for (var k = 1; k <= (2*i+1); k++) {
                str = str + '*';
        }
        for (var j = 1; j < n-i; j++) {
            str = " "+str;     
        }
        console.log(str+"_");
    }
}
stars(4);



    


Ответы

Ответ 1



function stars(n) { for (var i = 0; i < n; i++) { var ballIndex = -1; var str = "_"; if (i % 2 !== 0) { ballIndex = Math.floor(Math.random() * (2 * i + 1)) + 1; } for (var k = 1; k <= (2 * i + 1); k++) { str = str + ((ballIndex == k)? 'o' : '*'); } for (var j = 1; j < n - i; j++) { str = " " + str; } console.log(str + "_"); } } stars(6);

Выбор кроссплатформенного мобильного движка для игр [закрыт]

#unity3d #android #разработка_игр #coronasdk #ios


        
             
                
                    
                        
                            Closed. This question is opinion-based. It is not currently
accepting answers.
                            
                        
                    
                
                            
                                
                
                        
                            
                        
                    
                        
                            Want to improve this question? Update the question so
it can be answered with facts and citations by editing this post.
                        
                        Closed 2 года назад.
                                                                                
           
                
        
Цель: разработка кросплатформенных игр на мобильные телефоны.
Основной target: iOS (iPhone/iPad) и Android, большим плюсом будет поддержка Windows
Phone 7, Symbian, Bada.
Задача: максимально быстрая и удобная разработка игр, цена движка роли не играет.
Из игр, предположительно 2D аркады, казуалки. Должна быть возможность показа рекламы,
поддержка мультиплеера и интеграции с Game Center (iOS) / OpenFeint, шаринг в соц сети.
Доступ к Native API будет большим плюсом. Большое количество 3rd Party Tools, плагинов
и наработок, ускоряющих процесс разработки тоже крайне желательно.
На данный момент есть два претендента: Corona SDK и Unity 3d. У обоих есть как плюсы
так и минусы. Слышал разработка 2D игр на Unity требует довольно больших усилий, за
то в замен получаем desktop и web версии!
В общем дискас. Плюсы, минусы, подводные камни. Возможно есть еще годные аналоги
которые я упустил.    


Ответы

Ответ 1



Я конечно в этом деле не спец, но могу сказать что часто вижу как используются движки типа Unity 3D и Box2D, последний используется во флэше тоже. Box2D бесплатен и довольно таки хорош. UPD: Не малое количество всяких туториалов по движкам есть тут.

Ответ 2



Остановился на Corona SDK, быстро, удобно. Код пишется на Lua, поддерживаютсю платформы iOS / Android / Kindle Fire / Nook и совсем скоро будет поддержка десктопных Mac App Store приложений. В качестве редактора можно использовать TextMate, Sublime Text 2, Intellij IDEA, VIM. Для них всех есть бандлы добавляющие поддержку короны. Я пока остановился на ST2. Большой плюс Короны, это очень много уроков, примеров кода и библиотек, а так же сторонних программ, типа LevelBuilder, PhysicsHelper, значительно упрощающих разработку. Вот еще пару ссылок на туториалы: Официальные семплы Mobile.Tuts+ Learning Corona – вообще мега сайт, собраны почти все толковые уроки, что есть в сети.

“тонкая синхронизация” в односвязном списке

#c #многопоточность #синхронизация


Добрый день!
Недавно подсмотрела в курсах интересный вариант работы со списками в многопоточной
среде, обозвали эту модель там "тонкой синхронизацией". Суть состоит в том, что во
время работы со списком блокируется доступ только к текущему и предыдущему элементу
списка при любых операциях типа удаления/вставки, и при этом коллизий не возникает.
Кто-нибудь использовал этот способ синхронизации на реальных задачах?
Вот приведенный в курсах пример добавления элемента к связному списку на псевдокоде : 
  Node prev = head; prev.lock();
  Node curr = prev.next; curr.lock();

  try{
    while(curr.key < key){
    prev.unlock(); prev = curr;
    curr = curr.next; curr.lock();
    }
    if(key == curr.key) return false;
    else{
      node = new Node(key,item);
      node.next = curr;
      pred.next = node;
      return true;
    }
  } finally{curr.unlock();pred.unlock();}

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

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


Ответы

Ответ 1



Насколько я понимаю, этот код исходит из того, что голова списка фиктивна и неизменна. Это часто используемая техника, позволяющая не выделять специальный случай пустого списка. В таких предположениях код, кажется, верен. Инвариант алгоритма таков: для работы с элементом необходима блокировка как самого Node, так и его предшественника. Если у вас есть блокировка на Node или на его предшественника, ваш Node гарантированно жив. Я бы, однако, не стал использовать обработку исключений для анализа достижения конца списка (тем более, что в данном коде не проверяется head.next), и оставил исключения для исключительных ситуаций.

Возвращение из функции &&

#cpp #cpp11


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

int&& f()
{
      return 2;
}


Тут xvalue, но на временную переменную в стеке функции. Он будет не корректен, правильно?
Знаю что &&/const& продлевают жизнь (12.2/5), но не в таком контексте же:

const int& f()
{
     return 2;
}


Может быть, в первом коде, какой то трюк с литералом и xvalue?

(Этот код должен быть некорректным, но статья популярней меня)
    


Ответы

Ответ 1



Чтобы убедиться, что функция некорректна, можно запустить следующую тестовую программу #include struct Int { Int( int x ) : x( x ) { std::cout << "Int::Int( int )" << std::endl; } ~Int() { std::cout << "Int::~Int()" << std::endl; x = 100; } Int( const Int &rhs ) : x( rhs.x ) { std::cout << "Int::Int( const Int & )" << std::endl; } Int( Int &&rhs ) : x( rhs.x ) { std::cout << "Int::Int( Int && )" << std::endl; } int x; }; std::ostream & operator <<( std::ostream &os, const Int &rhs ) { return os << rhs.x; } Int && f() { return 2; } int main() { Int x = f(); std::cout << "x = " << x << std::endl; } Ее вывод на консоль может выглядеть следующим образом (хотя из-за неопределенного поведения программы вывод может отличаться) Int::Int( int ) Int::~Int() Int::Int( Int && ) x = 100 Int::~Int() Как видно в функции main переменная x получила значение уже удаленного объекта.

Матрица достижимости

#алгоритм


По матрице смежности нужно построить матрицу достижимости. Использую Алгоритм Флойда
— Уоршелла: 

for (int k = 0; k < n; k++)
        for (int i = 0; i < n; i++)
            for (int j = 0; j < n; j++)
                W[i][j] = (W[i][j] || (W[i][k] && W[k][j]));


На графе  

0 1 0 0 0  
0 0 0 1 0  
0 1 0 0 0  
0 0 1 0 0  
1 0 1 1 0  


Данный алгоритм выдает:  

0 1 1 1 0  
0 1 1 1 0  
0 1 1 1 0  
0 1 1 1 0  
1 1 1 1 0  


Но на сайте у них получилась немного другая матрица. T(D)
Где ошибка?
    


Ответы

Ответ 1



Никакой ошибки нет. Посмотрите внимательно на граф: Из узлов 2 3 4 можно добраться "из себя в себя" по некоторому пути. А из узлов 1 и 5 "в себя" не доберёшься. Поэтому такая матрица и получается: 0 1 1 1 0 0 1 1 1 0 0 1 1 1 0 0 1 1 1 0 1 1 1 1 0 Если вам для задачи этой информации не требуется, то после работы алгоритма можно пробежаться по диагональным элементам и проставить единички.

Qt, Linux, С++. Кто может мешать связи по TCP/IP [решено]?

#cpp #linux #qt #tcp_ip


Написал небольшую библиотечку для синхронизации данных между программами. Есть очередь
сообщений. Есть сервер, цепляющийся к очереди, открывающий порт и отвечающий на запросы
типа "дай все сообщения", "дай сообщения с такого-то времени", "дай сообщения за последние
n мс" и так далее. Есть клиент, цепляющийся к аналогичной очереди, и периодически опрашивающий
сервера с указанными адресами/портами, занося новые сообщения в очередь. Подключается,
опрашивает и отключается. Написал несколько программ, обменивающихся через такие очереди.
Под Windows всё работает в любых версиях от W7 до W10, проверял c версиями Qt 4.7.1,
4.8.6, 5.5.1. Под Linux (Ubuntu 14.04, Qt 5.5.1) тоже работает практически всё. Проблема
в слове "практически".

Итак, есть программы A, B, C. B опрашивает несколько источников (A) и формирует выходную
очередь, которую опрашивают несколько клиентов (С). Доставка от A к B или от A к C
работает всегда. А вот от B к C иногда, если С запущена позже, чем B, связь не налаживается
- идут timeout'ы и порча пакетов. При этом, если B и C находятся на одном узле, всё
работает. Пробовал для отладки поднимать на ноутбуке виртуальные машины Virtualbox
и запускать B и C в разных виртуалках или одну в виртуалке, другую на хосте - всё работает,
даже если сетевые задержки сильно увеличивать. Проблемы появляются только при работе
между разными физическими компами. Попробовал пересобирать B под Qt 4.8.6 - проблема
появляется реже, но всё равно иногда остаётся. Если запускать B в виндовой версии,
через Wine - всё тоже работает. Сетевые экраны отключены. Порты (9200-9300), используемые
для сервера, никем не заняты, после запуска программ-серверов (A, B) сканер показывает,
что они (порты) открыты.

Сейчас собираю на работе тестовый стенд (несколько узлов с Linux) для того, чтобы
запустить B под отладчиком. Но в целом ситуация вызывает у меня глубочайшее недоумение,
так как:


один и тот же код работает между A и B, между A и C, но не между B и C
один и тот же код работает между разными виртуальными машинами, но не между разными
физическими
... в сборке для `Windows`, но не в сборке для `Linux`.
... если C запущена раньше, чем B, но не наоборот.


Код проверен Valgrind'ом и cppcheck'ом. Натыкался ли кто-нибудь на похожие "грабли",
можете ли предположить хоть одну причину такого странного поведения?

Update: Всё страньше и страньше. Собрал тестовый стенд - и не могу воспроизвести
проблему. Те же самые A, B и C цепляются друг к другу во всех возможных комбинациях.
Чувствую, придётся запасной switch на объект везти.

Update2: Удалось воспроизвести проблему на стенде. Цепляю к одному B несколько C,
через некоторое время часть клиентов (C) перестаёт цепляться. Что удивительно: когда
клиент цепляться перестал, я пробую с его машины пинговать машину, на которой развёрнут
сервер (B) - и она не пингуется. И начинает пинговаться только после того, как машина
B в свою очередь начнёт пинговать клиентскую (С). У меня появилось подозрение, что
Ubuntu воспринимает эту кучу запросов, приходящую с клиента, как DOS-атаку, и блокирует
хост клиента. Осталось найти, где в настройках системы это прописано, и отключить.

Update3: Ситуация действительно очень похожа на защиту от DDOS-атаки. И такая версия
объясняет, почему связь между A и B работает, а между B и C - нет. К одному источнику
данных (A) цепляется максимум один промежуточный обработчик (B), а вот к нему может
цепляться несколько клиентов C. И именно обращения на один порт с разных узлов, видимо,
интерпретируются как DDOS-атака. При этом первый подключившийся клиент остаётся работоспособным,
а вот последующие обрубаются так, что даже ping'и от них не проходят.

Update4: найден workaround. Я запустил на машине, на которой запущен B, команду ping
до хостов, на которых запущены C. После этого отрубаться эти хосты перестали. Вместе
с тем, понять, что же вызывает такую реакцию системы, пока не получается. ufw отключён.

Update5: проблема решена. Сервер не должен рвать соединение, надо дождаться, пока
соединение разорвёт клиент (см. Update к комментарию).
    


Ответы

Ответ 1



Причины возникновения проблемы нашлись. Их было две. Одна - в тестовой системе, там серьёзные неполадки с сетью, и даже pingи теряются процентов на 30. Вторая и главная причина - в моём коде, сервер неправильно закрывал соединение. Обработав входящее соединение и сделав socket->write(), он висел на socket->waitForBytesWritten() (таким образом, до завершения отправки поток сервера был заблокирован), а после этого выполнял socket->close(). Видимо, такое принудительное закрытие сокета и не нравилось ОС. Когда я убрал waitForBytesWritten() и вместо close() сделал disconnectFromHost(), всё заработало. Update: Заработало не всё. На сервере проблема осталась, workaround из Update4 помогает, пока конфигурация остаётся стационарной. Когда мы добавляем мобильный клиент, подключающийся из разных мест, с разных IP, добавить его в пингующий скрипт уже не получается, и он довольно скоро отваливается. На клиентах, даже стационарных, иногда появляются сообщения об обрыве и восстановлении связи. Решение проблемы нашёл только недавно. Итак: когда сервер стал закрывать сокет более осторожно, через disconnectFromHost, проблема стала менее острой. Однако правильное решение оказалось таким: сервер вообще не должен закрывать сокет самостоятельно! Он должен реагировать на сигналы disconnected и error, по которым вызывает deleteLater(). Когда сервер сам решает закрыть сокет, завершив отправку данных, возникает гонка сигналов. Сигнал об отключении с сервера может прилететь раньше, чем ответ сервера клиенту, и тогда мы на клиенте получаем ситуацию "сервер отключился - сервер подключился". А когда до сервера долетает запрос на отключение от клиента, а сокета уже нет, видимо, возникает аварийная ситуация, и накопление таких ситуаций приводит к блокировке IP клиента (под Linux. Windows такие запросы, похоже, просто игнорирует). Итог: из сервера просто удалил disconnectFromHost(), и старый клиент работает (завершается неделя тестирования 2 клиента на 1 сервер) без единой потери связи. Timeoutы уменьшил с 400-600 мс до 10мс, и всё равно разрывов нет. Главный вывод: сервер не должен закрывать соединение сам, он должен реагировать на сигналы disconnected и error, возникающие вследствие действий клиента и среды передачи данных.

systemd/udev: запуск демона при подключении модема и остановка демона при отсоединении модема

#linux #модемы #systemd


Возможно ли в systemd/udev реализовать так, чтобы при появлении в системе модема
(/dev/ttyACM%i) запускался демон modem@%i.service, и чтобы при отключении модема демон
останавливался?

Готовый полный универсальный рабочий рецепт.

/etc/ppp/peers/modem

460800
defaultroute
usepeerdns
crtscts
lock
noauth
local
persist
modem
nopcomp
novjccomp
nobsdcomp
nodeflate
noaccomp
ipcp-accept-local
ipcp-accept-remote
noipdefault
connect "/usr/sbin/chat -t10 -f /etc/ppp/chatscripts/modem.chat"


/etc/ppp/chatscripts/modem.chat

В поле точки доступа "internet.mts.ru" или подставьте точку доступа своего провайдера
или оставьте такой, если у вас российский МТС. Большинство провайдеров игнорируют это
поле, и его вообще можно не указывать, но МТС её требует и без неё не соединяет.

ABORT 'BUSY'
ABORT 'NO CARRIER'
ABORT 'VOICE'
ABORT 'NO DIALTONE'
ABORT 'NO DIAL TONE'
ABORT 'NO ANSWER'
ABORT 'DELAYED'
REPORT CONNECT
TIMEOUT 5
'' 'ATQ0'
'OK-AT-OK' 'ATZ'
TIMEOUT 3
'OK-AT-OK' 'ATI'
'OK' 'ATZ'
'OK' 'ATQ0 V1 E1 S0=0 &C1 &D2 +FCLASS=0'
'OK' 'AT\^SYSCFG=2,2,3fffffff,0,1'
'OK-AT-OK' 'AT+CGDCONT=1,"IP","internet.mts.ru"'
'OK' 'ATDT*99#'
CONNECT


/usr/lib/systemd/system/modem@.service

[Unit]
Description=Modem /dev/ttyACM%i
BindsTo=dev-ttyACM%i.device
After=dev-ttyACM%i.device
Before=network.target
Wants=network.target

[Service]
ExecStart=/usr/sbin/pppd call modem /dev/ttyACM%i

[Install]
WantedBy=multi-user.target


/etc/udev/rules.d/90-modem.rules

KERNEL=="ttyACM[0-9]*", TAG+="systemd", ENV{SYSTEMD_WANTS}="modem@%n.service"


Затем перезагрузить конфигурацию systemd.

sudo systemctl daemon-reload

И теперь достаточно просто подключить любой телефон, кроме огрызка, с любым оператором
в режим не накопителя. (Nokia suite, Телефон, Модем, Синхронизация и т.д.)

Ну, конечно же, желательно убедиться, что у вас определяется модем и его файл устройства,
как у меня, /dev/ttyACM0 и udev на него ссылается как dev-ttyACM0.device,  а не /dev/ttyUSB0
и dev-ttyUSB0.device или как-то иначе.
Цифру, обозначающую порядковый номер устройства (0) в демоне systemd заменяем на
%i, а в правилах udev на %n. Это даёт возможность не привязывать демон и правила только
к одному модему, а работать с неограниченным количеством модемов и корректно управлять
каждым модемом отдельно друг от друга.

systemctl list-units -tdevice -a | grep dev-tty

Телефон теперь всегда подключается к интернету при подключении к компьютеру, даже
если компьютер успешно подключён через ethernet или Wi-Fi к интернету. Правда, в этом
случае, когда в системе поднят (ifup) хоть 1 интерфейс, трафик идёт через него, а с
модемом лишь устанавливает соединение, но передачи данных не происходит.

Чтобы отключить автодозвон при подключении модема нужно просто  замаскировать демон
sudo systemctl mask modem@0.service, а потом, когда понадобится модем размаскировать
sudo systemctl unmask modem@0.service
0 - это порядковый номер модема, если у вас их несколько, то можно отключить ненужные
или отключить все.

И самое не приятное, если ethernet и/или Wi-Fi подняты (ifup), то трафик не будет
идти через модем, пока все интерфейсы не будут опущены (ifdown) даже если к ним не
подключен кабель или нет выхода в интернет. Но это уже особенность модемной маршрутизации.
    


Ответы

Ответ 1



Правильнее будет, чтобы udev запускал/останавливал systemd.service — это его работа. my.service [Unit] Description=Modem ppp0 <--> /dev/ttyACM0 BindsTo=dev-ttyACM0.device After=dev-ttyACM0.device Before=network.target Wants=network.target [Service] ExecStart=/usr/sbin/pppd call modem ExecStop=комманда на стоп KillMode=none Type=oneshot RemainAfterExit=yes [Install] WantedBy=multi-user.target Список всех устройств, но со своим ты уже разобрался: systemctl list-units --all --full | grep ".device" 90-my.rules в папку /etc/udev/rules.d/ KERNEL=="ttyACM*", ATTRS{serial}=="<СЕРИЙНИК>", TAG+="systemd", ENV{SYSTEMD_WANTS}="my.service" Можно добавить SYMLINK=="gsmmodem", тогда появится симлинк /dev/gsmmodem. А ATTR{serial} или подобные нужны, чтобы не спутать разные устройства. Получить серийник и прочие аттрибуты: udevadm info -a -n /dev/ttyACM* UPD Можно сделать так, чтобы из KERNEL=="ttyACM*" передавался номер устройства (цифра под звёздочкой) в сервис, которая бы потом дополняла бы строку запуска? 2.a Или будет всё же проще и лучше написать 2 демона (modem0.service и modem1.service) для ACM0 и ACM1? Для этого тебе нужен не просто сервис, а шаблонный сервис. Для этого переименуй my.service в my@.service и запускать их мы будем как my@ttyACM0.service и my@ttyACM1.service, тогда внутри файла тебе будут доступны такие переменные как %i %I (которые и будут именем сервиса, тот что после @). Меняешь все упоминания ttyACMX на %I и наслаждаешься =) Внутри udev доступны операторы %k and %n, это имя и номер соответственно (ты просил именно %k). Соответственно, исправляешь ENV{SYSTEMD_WANTS}="my@%k.service" и будет стартовать сервис my@ttyACM0.service. По поводу отключения. Тут сложнее, потому что когда устройство отключается, то его параметры становятся недоступны и уже сложно понять что же было отключено, можешь запустить udevadm monitor --environment --udev и выдернуть устройство, должен увидеть что-то типа #######REMOVE####### monitor will print the received events for: UDEV - the event which udev sends out after rule processing UDEV [21800.789239] remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.0/input/input75/mouse1 (input) ACTION=remove DEVLINKS=/dev/input/by-id/usb-Logitech_USB_Receiver-mouse /dev/input/by-path/pci-0000:00:1d.0-usb-0:1.3:1.0-mouse DEVNAME=/dev/input/mouse1 DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.0/input/input75/mouse1 ID_BUS=usb ID_INPUT=1 ID_INPUT_MOUSE=1 ID_MODEL=USB_Receiver ID_MODEL_ENC=USB\x20Receiver ID_MODEL_ID=c51a ID_PATH=pci-0000:00:1d.0-usb-0:1.3:1.0 ID_PATH_TAG=pci-0000_00_1d_0-usb-0_1_3_1_0 ID_REVISION=4100 ID_SERIAL=Logitech_USB_Receiver ID_TYPE=hid ID_USB_DRIVER=usbhid ID_USB_INTERFACES=:030102:030000: ID_USB_INTERFACE_NUM=00 ID_VENDOR=Logitech ID_VENDOR_ENC=Logitech ID_VENDOR_ID=046d MAJOR=13 MINOR=33 SEQNUM=3178 SUBSYSTEM=input UDEV_LOG=3 USEC_INITIALIZED=21797014959 UDEV [21800.792866] remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.0/input/input75/event6 (input) ACTION=remove DEVLINKS=/dev/input/by-id/usb-Logitech_USB_Receiver-event-mouse /dev/input/by-path/pci-0000:00:1d.0-usb-0:1.3:1.0-event-mouse DEVNAME=/dev/input/event6 DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.0/input/input75/event6 ID_BUS=usb ID_INPUT=1 ID_INPUT_MOUSE=1 ID_MODEL=USB_Receiver ID_MODEL_ENC=USB\x20Receiver ID_MODEL_ID=c51a ID_PATH=pci-0000:00:1d.0-usb-0:1.3:1.0 ID_PATH_TAG=pci-0000_00_1d_0-usb-0_1_3_1_0 ID_REVISION=4100 ID_SERIAL=Logitech_USB_Receiver ID_TYPE=hid ID_USB_DRIVER=usbhid ID_USB_INTERFACES=:030102:030000: ID_USB_INTERFACE_NUM=00 ID_VENDOR=Logitech ID_VENDOR_ENC=Logitech ID_VENDOR_ID=046d MAJOR=13 MINOR=70 SEQNUM=3179 SUBSYSTEM=input UDEV_LOG=3 USEC_INITIALIZED=21797013896 UDEV [21800.797061] remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.0/input/input75 (input) ACTION=remove DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.0/input/input75 EV=17 ID_BUS=usb ID_INPUT=1 ID_INPUT_MOUSE=1 ID_MODEL=USB_Receiver ID_MODEL_ENC=USB\x20Receiver ID_MODEL_ID=c51a ID_PATH=pci-0000:00:1d.0-usb-0:1.3:1.0 ID_PATH_TAG=pci-0000_00_1d_0-usb-0_1_3_1_0 ID_REVISION=4100 ID_SERIAL=Logitech_USB_Receiver ID_TYPE=hid ID_USB_DRIVER=usbhid ID_USB_INTERFACES=:030102:030000: ID_USB_INTERFACE_NUM=00 ID_VENDOR=Logitech ID_VENDOR_ENC=Logitech ID_VENDOR_ID=046d KEY=ffff0000 0 0 0 0 0 0 0 0 MODALIAS=input:b0003v046DpC51Ae0111-e0,1,2,4,k110,111,112,113,114,115,116,117,118,119,11A,11B,11C,11D,11E,11F,r0,1,6,8,am4,lsfw MSC=10 NAME="Logitech USB Receiver" PHYS="usb-0000:00:1d.0-1.3/input0" PRODUCT=3/46d/c51a/111 PROP=0 REL=143 SEQNUM=3180 SUBSYSTEM=input UDEV_LOG=3 UNIQ="" USEC_INITIALIZED=21796479085 UDEV [21800.797132] remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.0/0003:046D:C51A.004D/hidraw/hidraw2 (hidraw) ACTION=remove DEVNAME=/dev/hidraw2 DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.0/0003:046D:C51A.004D/hidraw/hidraw2 MAJOR=251 MINOR=2 SEQNUM=3181 SUBSYSTEM=hidraw UDEV_LOG=3 USEC_INITIALIZED=21800796369 UDEV [21800.797172] remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.0/0003:046D:C51A.004D (hid) ACTION=remove DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.0/0003:046D:C51A.004D HID_ID=0003:0000046D:0000C51A HID_NAME=Logitech USB Receiver HID_PHYS=usb-0000:00:1d.0-1.3/input0 MODALIAS=hid:b0003g0001v0000046Dp0000C51A SEQNUM=3182 SUBSYSTEM=hid UDEV_LOG=3 USEC_INITIALIZED=21800796501 UDEV [21800.798537] remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.0 (usb) ACTION=remove DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.0 DEVTYPE=usb_interface INTERFACE=3/1/2 MODALIAS=usb:v046DpC51Ad4100dc00dsc00dp00ic03isc01ip02in00 PRODUCT=46d/c51a/4100 SEQNUM=3183 SUBSYSTEM=usb TYPE=0/0/0 UDEV_LOG=3 USEC_INITIALIZED=21800796538 UDEV [21800.814181] remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.1/input/input76/event7 (input) ACTION=remove DEVLINKS=/dev/input/by-id/usb-Logitech_USB_Receiver-event-if01 /dev/input/by-path/pci-0000:00:1d.0-usb-0:1.3:1.1-event DEVNAME=/dev/input/event7 DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.1/input/input76/event7 ID_BUS=usb ID_INPUT=1 ID_INPUT_KEY=1 ID_MODEL=USB_Receiver ID_MODEL_ENC=USB\x20Receiver ID_MODEL_ID=c51a ID_PATH=pci-0000:00:1d.0-usb-0:1.3:1.1 ID_PATH_TAG=pci-0000_00_1d_0-usb-0_1_3_1_1 ID_REVISION=4100 ID_SERIAL=Logitech_USB_Receiver ID_TYPE=hid ID_USB_DRIVER=usbhid ID_USB_INTERFACES=:030102:030000: ID_USB_INTERFACE_NUM=01 ID_VENDOR=Logitech ID_VENDOR_ENC=Logitech ID_VENDOR_ID=046d MAJOR=13 MINOR=71 SEQNUM=3184 SUBSYSTEM=input UDEV_LOG=3 USEC_INITIALIZED=21797013933 XKBLAYOUT=de XKBMODEL=pc105 UDEV [21800.816765] remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.1/input/input76 (input) ABS=1 0 ACTION=remove DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.1/input/input76 EV=1f ID_BUS=usb ID_INPUT=1 ID_INPUT_KEY=1 ID_MODEL=USB_Receiver ID_MODEL_ENC=USB\x20Receiver ID_MODEL_ID=c51a ID_PATH=pci-0000:00:1d.0-usb-0:1.3:1.1 ID_PATH_TAG=pci-0000_00_1d_0-usb-0_1_3_1_1 ID_REVISION=4100 ID_SERIAL=Logitech_USB_Receiver ID_TYPE=hid ID_USB_DRIVER=usbhid ID_USB_INTERFACES=:030102:030000: ID_USB_INTERFACE_NUM=01 ID_VENDOR=Logitech ID_VENDOR_ENC=Logitech ID_VENDOR_ID=046d KEY=4837fff 72ff32d bf544446 0 0 1 20f90 8b17c000 677bfa d9415fed 9ed680 4400 0 10000002 MODALIAS=input:b0003v046DpC51Ae0111-e0,1,2,3,4,k71,72,73,74,77,80,82,83,85,86,87,88,89,8A,8B,8C,8E,90,96,98,9B,9C,9E,9F,A1,A3,A4,A5,A6,A7,A8,A9,AB,AC,AD,AE,B0,B1,B2,B5,B6,CE,CF,D0,D1,D2,D4,D8,D9,DB,DF,E4,E7,E8,E9,EA,EB,F1,100,161,162,166,16A,16E,172,174,176,178,179,17A,17B,17C,17D,17F,180,182,183,185,188,189,18C,18D,18E,18F,190,191,192,193,195,198,199,19A,1A0,1A1,1A2,1A3,1A4,1A5,1A6,1A7,1A8,1A9,1AA,1AB,1AC,1AD,1AE,1B0,1B1,1B7,1BA,r6,a20,m4,lsfw MSC=10 NAME="Logitech USB Receiver" PHYS="usb-0000:00:1d.0-1.3/input1" PRODUCT=3/46d/c51a/111 PROP=0 REL=40 SEQNUM=3185 SUBSYSTEM=input UDEV_LOG=3 UNIQ="" USEC_INITIALIZED=21796482192 UDEV [21800.817249] remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.1/usbmisc/hiddev1 (usbmisc) ACTION=remove DEVNAME=/dev/usb/hiddev1 DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.1/usbmisc/hiddev1 MAJOR=180 MINOR=1 SEQNUM=3186 SUBSYSTEM=usbmisc UDEV_LOG=3 USEC_INITIALIZED=21800816392 UDEV [21800.818490] remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.1/0003:046D:C51A.004E/hidraw/hidraw3 (hidraw) ACTION=remove DEVNAME=/dev/hidraw3 DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.1/0003:046D:C51A.004E/hidraw/hidraw3 MAJOR=251 MINOR=3 SEQNUM=3187 SUBSYSTEM=hidraw UDEV_LOG=3 USEC_INITIALIZED=21800816472 UDEV [21800.818536] remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.1/0003:046D:C51A.004E (hid) ACTION=remove DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.1/0003:046D:C51A.004E HID_ID=0003:0000046D:0000C51A HID_NAME=Logitech USB Receiver HID_PHYS=usb-0000:00:1d.0-1.3/input1 MODALIAS=hid:b0003g0001v0000046Dp0000C51A SEQNUM=3188 SUBSYSTEM=hid UDEV_LOG=3 USEC_INITIALIZED=21800816908 UDEV [21800.818580] remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.1 (usb) ACTION=remove DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.1 DEVTYPE=usb_interface INTERFACE=3/0/0 MODALIAS=usb:v046DpC51Ad4100dc00dsc00dp00ic03isc00ip00in01 PRODUCT=46d/c51a/4100 SEQNUM=3189 SUBSYSTEM=usb TYPE=0/0/0 UDEV_LOG=3 USEC_INITIALIZED=21800816945 UDEV [21800.818645] remove /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3 (usb) ACTION=remove BUSNUM=002 DEVNAME=/dev/bus/usb/002/038 DEVNUM=038 DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3 DEVTYPE=usb_device ID_BUS=usb ID_MODEL=USB_Receiver ID_MODEL_ENC=USB\x20Receiver ID_MODEL_ID=c51a ID_REVISION=4100 ID_SERIAL=Logitech_USB_Receiver ID_USB_INTERFACES=:030102:030000: ID_VENDOR=Logitech ID_VENDOR_ENC=Logitech ID_VENDOR_ID=046d MAJOR=189 MINOR=165 PRODUCT=46d/c51a/4100 SEQNUM=3190 SUBSYSTEM=usb TYPE=0/0/0 UDEV_LOG=3 UPOWER_VENDOR=Logitech, Inc. USEC_INITIALIZED=21796488421 и понять что там к чему. Добавить к существующему правилу ACTION=="add" тогда оно будет срабатывать только при подключении устройства. А вот что писать в ACTION=="remove" что бы systemd стопил сервис - я не знаю. Но мне кажется, остановки с помощью BindsTo= вполне достаточно. Ответ на 3) При выполнении /usr/sbin/pppd call modem он разве не завершается? Там другой процесс порождается демоном, но systemd об этом ничего не знает. Если хочется, то можете попробывать заставить systemd мониторить процесс с помощью Type=forking и, желательно, PIDFile=. Как там работает pppd я не в курсе, может поможет GuessMainPID=yes (хотя если PIDFile пуст, то он по умолчанию вкл). По управлению udev: Может быть полезно сгенерировать различные события udev. Например, вы хотите симулировать отключение USB-устройства на удаленной машине. В таких случаях, используйте udevadm trigger: udevadm trigger -v -t subsystems -c remove -s usb -a "idVendor=id_поставщика" У тебя соответственно -s ttyACM. Хотя тут я не уверен, потому что сам плохо знаю этот момент.

Рисование кругов в libgdx

#java #android #libgdx


Имеется 3 круга, каждый круг разбит на 3 части в которых задается цвет, каждый круг
должен вращаться в разные стороны. Как это сделать?


    


Ответы

Ответ 1



Мой ответ не для libgdx, а для просто Android. Но может быть кому-то будет интересно. Это кастомное представление: public class ProgressView extends View { private Paint paint; private float mProgress; private final RectF oval = new RectF(); public ProgressView(Context context) { super(context); initPaint(); } public void setProgress(final float progress) { if (progress == mProgress) { return; } mProgress = progress; invalidate(); } private void initPaint() { paint = new Paint(); paint.setStyle(Paint.Style.FILL); paint.setStyle(Paint.Style.STROKE); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); float width = (float)getWidth(); float height = (float)getHeight(); float radius; if (width > height){ radius = height/4; } else{ radius = width/4; } float center_x, center_y; center_x = width/2; center_y = height/2; oval.set(center_x - radius, center_y - radius, center_x + radius, center_y + radius); int initialAngle = (int)(mProgress / 100 * 360); drawCircle(canvas, oval, 15, initialAngle); oval.set(center_x - 2*radius/3, center_y - 2*radius/3, center_x + 2*radius/3, center_y + 2*radius/3); initialAngle = -initialAngle; drawCircle(canvas, oval, 10, initialAngle); oval.set(center_x - radius/3, center_y - radius/3, center_x + radius/3, center_y + radius/3); initialAngle = -initialAngle; drawCircle(canvas, oval, 5, initialAngle); } private void drawCircle(Canvas canvas, RectF rectF, int strokeWidth, int initAngle) { paint.setStrokeWidth(strokeWidth); paint.setColor(Color.RED); canvas.drawArc(rectF, initAngle, 120, false, paint); paint.setColor(Color.YELLOW); canvas.drawArc(rectF, initAngle + 120, 120, false, paint); paint.setColor(Color.WHITE); canvas.drawArc(rectF, initAngle + 240, 120, false, paint); } } Это активность, использующая этот View, где движение в разные стороны достигается с помощью ValueAnimator: public class MainActivity extends AppCompatActivity { private ProgressView progressBar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); progressBar = new ProgressView(this); setContentView(progressBar); ValueAnimator anim = ValueAnimator.ofInt(0, 1000); anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { progressBar.setProgress((int)animation.getAnimatedValue()); } }); anim.setDuration(40000); anim.setRepeatCount(ValueAnimator.INFINITE); anim.start(); } }

Работа с большими объемами данных в React Flux/Redux

#javascript #redux #flux


Существуют ли паттерны работы со store во Flux/Redux, которые бы подразумевали загрузку
в них больших объемов данных для последующего их воспроизведениях в других местах приложения.

Например:

Имеется некий список автомобильных брендов с кратким описанием их истории и списком
моделей (забираем GET-запросом). Бренды мы можем добавлять/изменять/удалять через конструктор
(постим изменения в БД). Этот список используется на странице А, на странице Б, а также
в модальном окне. 

Так вот, является ли хорошей практикой весь список загружать в store?
    


Ответы

Ответ 1



Я использую redux и все сказанное дальше распространяется на него, про flux ничего не скажу, там все мутно раньше было, куча реализаций и тп (сейчас не знаю). И так в идеале вы сохраняете ВСЕ состояние приложения в store, именно все. Заполнили поле формы - в store, поменяли состояние кнопки в store, загружаете данные - флаг loading должен быть в store, данные загрузились - должны быть в store. Но мир не идеальный и поэтому мелкие вещи, типа "состояние плитки: перевернута" лучше хранить локально в state компонента. Плюсы: отлично работает undo\redo функционал в redux-devtools вы можете буквально сохранить store при ошибке и возпроизвести у себя минимум теневой логики, искать баги очень просто почти все компоненты stateless... просто рендер того самого состояния У вас выходит огромный объект со всеми данными приложения. Но такой огромный он не нужен для каждого компонента, по этому вы используете reselect (ОБЯЗАТЕЛЬНО ИЛИ ПОДОБНОЕ РЕШЕНИЕ, он вытаскивает маленький объект с данными из общего состояние оптимизациями, работает очень круто, бояться огромного store с ним не нужно). Кстати для загрузки данных можете использовать redux-api (не самое изящное решение, но довольно мощное) и react-redux-form (очень изящное). Посмотрите серию скринкастов от разработчика redux, очень много вопросов отпадает https://egghead.io/lessons/javascript-redux-the-single-immutable-state-tree

Как реализовать вертикальную шкалу с номерами на HTML и CSS

#html #css




Требуется ваш совет, каким образом лучше реализовать такой список по середине с цифрами?
Html и Css
    


Ответы

Ответ 1



Пример * { padding: 0; margin: 0; box-sizing: border-box; } body { background-color: tomato; } ol { list-style: none; color: #fff; counter-reset: count; padding: 1em; position: relative; } ol:before { content: ''; position: absolute; top: 40px; left: 50%; width: 2px; height: 80%; background: #fff; margin-left: -1px; } ol > li { position: relative; margin: 20px 0; padding-right: calc(50% + 28px); } ol > li:nth-child(even) { padding-right: 0; padding-left: calc(50% + 28px); } ol > li:before { counter-increment: count; content: counter(count)" "; position: absolute; top: 0; left: 50%; margin-left: -14px; width: 28px; height: 28px; line-height: 28px; border: 2px solid #fff; background: tomato; z-index: 1; color: #fff; text-align: center; border-radius: 50%; box-sizing: border-box; }
  1. Как реализовать такой список с номерами на Html и Css
  2. Как реализовать такой список с номерами на Html и Css
  3. Как реализовать такой список с номерами на Html и Css
  4. Как реализовать такой список с номерами на Html и Css
  5. Как реализовать такой список с номерами на Html и Css
  6. Как реализовать такой список с номерами на Html и Css
  7. Как реализовать такой список с номерами на Html и Css
  8. Как реализовать такой список с номерами на Html и Css


Ответ 2



Нумерацию можно сгенерировать при помощи CSS свойств counter* Таким образом удастся отдельно стилизовать счетчик. Также придется отдельно стилизовать четные и нечетные элементы, но это уже при помощи :nth-child() Все это поддерживается в текущих версиях браузеров

Архитектура web torrent клиента

#java #spring_mvc #архитектура #torrent


Подскажите как правильно организовать локальную торрент качалку с веб интервейсом.
Сам торрент клиент уже написан. Осталось прикрутить веб. Стоит ли ради "такого" дела
поднимать спринг или же есть альтернативные пути? Если стоит прикрутить спринг, насколько
подходит архитектура mvc?

Торрент клиент на вход получает файл.torrent(или же путь к файлу) и путь для сохранения.
С возможностью выдачи статистических данных. Никакой безопасности и многопользовательности.
    


Ответы

Ответ 1



А в чем сомнения возникли? Вам же по сути надо сделать CRUD для управления торрентами. Любой MVC тут подойдет. Но можно и без него. Архитектурно у вас уже есть разделение на UI и саму качалку. Если у вас нет конкретных требований к аппаратным ресурсам делать можно на чем нравится. Варианты: если хочется клиентскую часть сделать максимально тонкой - Spring Boot, Spring MVC, любой шаблонизатор (JSP, Thymeleaf, Velocity). если хочется попробовать SPA, то какой-нибудь не слишком тяжелый сервер (Jetty, Undertow, Ratpack) и JAX-RS обвязка для REST (Jersey, RESTeasy), или даже вовсе без нее, а UI ваять на JS. если хочется SPA без JavaScript, то можно попробовать GWT (Google Web Toolkit), но это путь в никуда. если все же железо слабое, и хочется чего-то с минимальным footprint, то лучше вообще отказаться от Java, а попробовать Python или даже Lua. Под них тоже достаточно как полноценный MVC, так минималистичных микро-web-фреймворков.

Парадокс Монти Холла - не реализовать

#теория_вероятностей


Пытаюсь запрограмировать компьютер на победу с помощью парадокса Монти Холла

Моя задача однако угадать генерируемое число из пяти чисел.
То есть компьютер генерирует любом число = 0, 1, 2, 3, 4
И другая функция угадывает это число.
Игра повторятся большое количество раз и по статистике моя функция должна привести
к победе.
Я попыталась следовать логике, описанной теорией вероятностей.
Однако, к сожалению, мой код не приводит к выигрышу. 
Может быть кто-то может подсказать в чем ошибка. 

val choice1 = random.nextInt(5)

var shown1:Int;
do shown = random.nextInt(5) while (shown1 == choice1)

var choice2: Int
do choice2 = random.nextInt(5) while ( choice2 == shown1 || choice2 == choice1)

var shown2:Int
do shown2 = random.nextInt(5) while (shown2 == shown1 || shown2 == choice1 || shown2
== choice2)

do guess = random.nextInt(5) while (guess == choice1 || guess == shown1 || guess
== choice2 || guess == shown2)

return Guess(guess)

    


Ответы

Ответ 1



Вопрос решен, всем спасибо. Готовый вариант на java: import java.util.Random; public class Monty{ public static void main(String[] args){ int switchWins = 0; int stayWins = 0; Random gen = new Random(); for(int plays = 0;plays < 32768;plays++ ){ int[] doors = {0,0,0};//0 is a goat, 1 is a car doors[gen.nextInt(3)] = 1;//put a winner in a random door int choice = gen.nextInt(3); //pick a door, any door int shown; //the shown door do{ shown = gen.nextInt(3); //don't show the winner or the choice }while(doors[shown] == 1 || shown == choice); stayWins += doors[choice];//if you won by staying, count it //the switched (last remaining) door is (3 - choice - shown), because 0+1+2=3 switchWins += doors[3 - choice - shown]; } System.out.println("Switching wins " + switchWins + " times."); System.out.println("Staying wins " + stayWins + " times."); } }

Оптимизация SQL запроса в Postgres

#sql #postgresql


есть запрос для построения маршрутов из рейсов

WITH RECURSIVE tree AS (
  SELECT f0.id::text AS flights,
         '/'||origin_id||'/'||destination_id||'/' AS route,
         '/'||airline||'/' AS airlines,
         '/'||invisible||'/' AS invisible,
    origin_id, destination_id,
         std AS departure,
         sta AS arrival,
         0 as hops,
         (sta - std)::INTERVAL AS total_time,
         '00:00'::INTERVAL AS waiting_time,
         available AS available
  FROM flights f0
    INNER JOIN airports a
      ON f0.origin_id = a.id
         AND NOT a.cargo_accepted = false
  WHERE origin_id = '13'
        AND airline IN ('LH', 'OK', 'QS', 'U6')
        AND std >= '2017-06-02 06:11:02 UTC'
        AND NOT f0.available = false
  UNION ALL
  SELECT s.flights || '-->' || f1.id::text AS flights,
    s.route||f1.destination_id||'/',
    s.airlines||f1.airline||'/',
    s.invisible||f1.invisible||'/',
    s.origin_id, f1.destination_id,
         s.departure AS departure,
         f1.sta AS arrival,
         s.hops + 1 AS hops,
         s.total_time + (f1.sta - f1.std)::INTERVAL AS total_time,
         s.waiting_time + (f1.std - s.arrival)::INTERVAL AS waiting_time,
    s.available
  FROM tree s
    JOIN flights f1
    INNER JOIN airports a1
      ON f1.origin_id = a1.id
         AND NOT a1.cargo_accepted = false
         AND NOT a1.transit = false
      ON f1.origin_id = s.destination_id
         AND f1.std >= s.arrival + '8 hour'
         AND f1.std <= s.arrival + '7 day'
         AND NOT s.route LIKE '%/'||f1.destination_id||'/%'
         AND s.hops < 4
         AND NOT s.available = false
         AND NOT f1.available = false
         AND f1.airline IN ('LH', 'OK', 'QS', 'U6')
)
SELECT *
FROM tree
  INNER JOIN airports a3
    ON destination_id = a3.id
       AND NOT a3.cargo_accepted = false
WHERE destination_id = '112'
      AND waiting_time < '25 day'
      AND arrival <= '2017-06-27 06:00:28 UTC'
      AND airlines LIKE '%U6%'
ORDER BY arrival, departure, waiting_time


количество рейсов растет и запрос начинает все дольше и дольше думать
на данный момент в базе 64105 рейсов, как бы это мелочи для DB но запрос проходит
примерно за 12-16 секунд.

Помогите разобраться, что не так?

делал анализ
https://gist.github.com/sanyco86/26f07fbf567e09639cc75ac7b557e660

ну и собственно, если кто-то захочет вникнуть в проблему
тестовые данные
http://beta.u6-cargo.com/assets/Postgtes_backup.sql
    


Ответы

Ответ 1



Основная проблема данного запроса в быстром размножении количества обрабатываемых записей в рекурсии. Основным и практически единственным способом борьбы с этим является как можно большее ограничение подбираемых внутри рекурсии записей. А одна лишняя запись на первом шаге рекурсии может породить за собой тысячи записей к 4 шагу... Первое, что бросается в глаза: запрос внутри рекурсивной части выбирает все рейсы начиная с конкретной даты, но никак не ограничивая максимально возможную дату. При этом после рекурсии все рейсы с датой посадки больше определенной отбрасываются. Добавление условий на максимальную дату в обе части рекурсивного CTE сокращают время работы с 12 секунд до 2.5. Второе, что можно сделать - это оптимизировать работу с индексами. На таблице создано много индексов, но по одиночным полям. А использование сразу нескольких индексов по одной таблице в запросе хоть и возможно, но не сильно увеличивает производительность, а иногда ее и снижает. Весь вопрос в селективности того или иного индекса. Если по условию индекса отбирается слишком много записей, то работа по нему лишь замедляет выборку. Для того что бы повысить селективность стоит делать составные индексы из нескольких полей подбирающие по условию в запросе как можно меньше записей. В вашем случае необходимо сделать следующее: DROP INDEX "index_flights_on_available"; CREATE INDEX "index_dep_std" ON "flights" USING "btree" ("origin_id", "std", "sta", "destination_id", "available"); Индекс по бинарному полю available обладает очень низкой селективностю, отбирая по одному ключу сразу половину всей таблицы. В нем нет смысла. А в данном случае оптимизатор все таки его использовал объединяя результаты с выбором по другому индексу, но это несколько замедляло запрос, затраты на объединение были выше, чем давала фильтрация по нему. Создаваемый же индекс максимально покрывает потребности данного запроса, так как поиск рейсов начинается с определенного АП вылета по интервалу дат, поэтому мы включаем эти поля первыми, сначала проверяемое на точное равенство, потом те, что на диапазон. Включение АП назначения и available позволяет проверить практически все условия из запроса двигаясь по дереву индекса. Данная операция сокращает запрос до 1 секунды. И наконец еще одно предложение, самое сложное по реализации ... В настоящее время запрос перебирает все возможные сочетания аэропортов из всей маршрутой сети, но очевидно, что полеты через некоторые АП проверять бесполезно. Если нам надо попасть из Москвы в Сочи то лететь первым делом в Мирный, который мягко говоря не в той стороне и главное, у которого весьма специфичная сеть своих рейсов, заведомо проигрышный вариант. Однако рекурсия рассматривает и такие варианты и порождает за одним перелетом в Мирный еще ворох рейсов, следующих из него. Для фильтрации таких ситуаций можно заранее рассчитать достижимость каких либо АП при пролете через другие и в рекурсии не заходить в ветви, по которым требуемый АП назначения заведомо не достижим за оставшиеся N перелетов. Создадим таблицу достижимости АП из других: create table approach( orig integer not null, -- Аэропорт вылета dest integer not null, -- Аэропорт назначения hop integer not null, -- Минимальное количество перелетов для достижения constraint approach_pkey primary key(dest,orig) ); Теперь ее надо заполнить, это самая интересная и сложная часть. Дело в том, что содержимое данной таблицы надо будет всегда поддерживать в актуальном состоянии, как только появляется рейс связывающий аэропорты, между которыми до этого не было прямого сообщения - нам надо пересчитать всю сеть. Изменяются поля cargo_accepted и transit в справочнике аэропортов - опять пересчитать. И что бы таблица отвечала текущей ситуации дополнительно надо ее пересчитывать например раз в сутки. Пересчет выполняется следующим запросом: WITH RECURSIVE Q(o,d,transit) as( select origin_id o, destination_id d, a.transit -- Список пар АП из рейсов from flights f, airports a where std >= localtimestamp - interval '1 day' -- and std <= localtimestamp + interval '60 day' and a.id=f.origin_id and NOT a.cargo_accepted = false group by origin_id, destination_id, a.transit ), Tree(orig, dest, hop, transit, route) as( select o, d, 1, transit, -- Затравка рекурсивной части '/'||o||'/'||d||'/' AS route from Q union all -- Рекурсия select T.orig, Q.d, T.hop+1, T.transit, T.route||Q.d||'/' from Tree T, Q where Q.o=T.dest and not Q.transit=false and T.route not like '%/'||Q.d||'/%' and T.hop<4 ), New(orig,dest,hop) as( -- Таким должно быть содержимое таблицы select orig, dest, min(hop) hop from Tree group by orig, dest union select distinct o,o,0 -- Записи из АП в него самого за 0 перелетов нужны поиску from Q ), Upd(orig,dest) as( -- Обновляем существующие записи update approach a set hop=n.hop from New n where a.orig=n.orig and a.dest=n.dest returning a.orig, a.dest ), Ins(orig, dest) as( -- Вставляем недостающие insert into approach(orig,dest,hop) select orig, dest, hop from New n where (n.orig,n.dest) not in(select orig,dest from Upd) returning orig,dest ) -- И удаляем устаревшие delete from approach where (orig,dest) not in(select orig,dest from New) Особое внимание обратите на условия в самом начале. В данном случае берутся все рейсы, начиная со вчерашнего дня, поэтому использовать таблицу можно будет только при поиске будущих рейсов. Если надо искать, что было в прошлом (надеюсь все таки не надо), придется либо менять это условие, что понизит точность в текущий момент, либо не пользоваться ей при поиске в истории. Так же, если например системе никогда не надо искать рейсы, которые вылетят через два месяца, можете раскоментарить ограничение на максимальную дату. Еще раз напоминаю, что таблицу надо поддерживать в актуальном состоянии. Для этого рекомендую сделать хранимую процедуру с данным запросом и вызывать ее раз в день, в триггере на справочник аэропортов и в триггере на добавление рейсов (что бы это не было слишком частым, вызывать ее только если в таблице approach нет записи с парой аэропортов из добавляемого рейса с полем hop равным 1 (прямой перелет)). В принципе, на текущей сети рейсов она выполняется 80 ms. И наконец итоговая версия запроса поиска: WITH RECURSIVE tree AS ( SELECT f0.id::text AS flights, '/'||origin_id||'/'||destination_id||'/' AS route, '/'||airline||'/' AS airlines, '/'||invisible||'/' AS invisible, origin_id, destination_id, std AS departure, sta AS arrival, 0 as hops, (sta - std)::INTERVAL AS total_time, '00:00'::INTERVAL AS waiting_time, available AS available FROM flights f0 INNER JOIN airports a ON f0.origin_id = a.id AND NOT a.cargo_accepted = false WHERE origin_id = 13 AND airline IN ('LH', 'OK', 'QS', 'U6') AND std >= '2017-06-02 06:11:02 UTC' and sta <= '2017-06-27 06:00:28 UTC' AND NOT f0.available = false UNION ALL SELECT s.flights || '-->' || f1.id::text AS flights, s.route||f1.destination_id||'/', s.airlines||f1.airline||'/', s.invisible||f1.invisible||'/', s.origin_id, f1.destination_id, s.departure AS departure, f1.sta AS arrival, s.hops + 1 AS hops, s.total_time + (f1.sta - f1.std)::INTERVAL AS total_time, s.waiting_time + (f1.std - s.arrival)::INTERVAL AS waiting_time, s.available FROM tree s JOIN airports a1 ON s.destination_id = a1.id AND NOT a1.cargo_accepted = false AND NOT a1.transit = false JOIN flights f1 ON f1.origin_id = s.destination_id AND f1.std >= s.arrival + '8 hour' AND f1.std <= s.arrival + '7 day' AND NOT f1.available = false JOIN approach app ON app.dest=49 -- <-- Заменить на требуемый АП назначения and app.hop<4-1-s.hops -- <-- 4 - та же, что в условии s.hops<4 and f1.destination_id=app.orig WHERE NOT s.route LIKE '%/'||f1.destination_id||'/%' AND s.hops < 4 AND NOT s.available = false AND f1.airline IN ('LH', 'OK', 'QS', 'U6') and f1.sta <= '2017-06-27 06:00:28 UTC' ) SELECT * FROM tree INNER JOIN airports a3 ON destination_id = a3.id AND NOT a3.cargo_accepted = false WHERE destination_id = 49 AND waiting_time < '25 day' AND arrival <= '2017-06-27 06:00:28 UTC' AND airlines LIKE '%U6%' ORDER BY arrival, departure, waiting_time К сожалению, на маршрутах, действительно требующих нескольких перелетов, скорость уменьшилась только до 500-600ms (вроде 13-49 в примере). Зато на рейсе 13-112 время выполнения уменьшилось до 11 ms (т.е. в 1000 раз по сравнению с вариантом из вопроса). Рекомендую посравнивать на всякий случай результаты после выполнения реальных запросов без использования approach и с ней. В принципе разницы в количестве записей на нескольких тестовых направлениях я не заметил.

Ответ 2



Рекомендую взглянуть на документацию postgrestpro по производительности По вашему плану запроса, видно что он вообще не оптимизирован, слишком много операций FILTER(она очень дорогая), свидетельствует о том, что либо нет нужных индексов, либо не правильно построены(есть несколько индексов по отдельным столбцам, хотя нужен мультиколоночный индекс). Если эти данные только вставляются(операции DELETE или UPDATE редки) и они нужны для простого отображения, рекомендую сделать дополнительную плоскую таблицу(без джоинов), куда будут записываться данные при вставке в основную, - это будет работать гораздо быстрее.

Переопределить ключевые слова Python на русском языке

#python


Добрый день!

Решил обучить ребенка (9 лет) программированию. Всякие игры типа Blockly уже пройдены,
пора переходить к тексту. Для обучения выбрал Python, как, на мой взгляд, самый простой
язык. 

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

Без понятия, как перевести ветвления и циклы. Была идея написать функции типа

def ЕслиТоВыполнить(condition, code):
  if condition:
    eval(code)

ЕслиТоВыполнить(Правда, "вывести('да, это правда')")


Но тут есть 2 проблемы: 


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


Второе можно исправить, передав функцию в качестве параметра. Но это приводит к новым
вопросам.

Такая же ерунда с циклами и функциями. 
Кроме того мне не совсем нравятся методы И(expr1, expr2) и ИЛИ(expr1, expr2)
С классами проблем нет, так как думаю, что пока мы до них дойдём - уже будем писать
по-английски. 

Может, есть какая-нибудь возможность задефайнить ключевые слова? 
Вопрос "Зачем?" не обсуждается :) Я считаю, что надо максимально упростить задачу
для вхождения, чтобы не отпало настроение учиться с самого начала. 

Спасибо! :)
    


Ответы

Ответ 1



Похоже, что это невозможно. Язык Python не включает в себя механизмов для переопределения ключевых слов. Вольно перевожу ответ с enSO: Ключевые слова — жестко заданная часть языка. Вы можете написать свой собственный язык или интерпретатор, но переопределить ключевые слова в Python не можете (а в Lisp или C/C++ смогли бы). Это и не нужно. Язык программирования — это английский. Это может быть не очень честно, но это так. Вся документация — на английском, все методы во всех библиотеках — тоже. Цитируя Эрика Стивена Реймонда (ссылка выше): Английский — рабочий язык хакерского сообщества и интернета, и вам придется изучить его, чтобы стать частью этого сообщества. Линус Торвальдс, финн по национальности, комментирует свой код на английском. (Похоже, что он никогда и не пытался делать иначе.) То, что он хорошо владеет английским, помогло ему собрать международное сообщество разработчиков Линукса. Это поучительный пример. Добавлю от себя: конструкциями вроде ЕслиТоВыполнить("вывести('да, это правда')") вы прячете реальный красивый и лаконичный синтаксис языка и заменяете его уродливым и неестественным. Синтаксис Python приближен к естественным языкам, а они формируют наше сознание и мышление. Нам попросту легче изучать языки программирования, которые похожи на нашу речь. ЕслиТоВыполнить(оценить(вы будете применить(ребенок, обучение таким образом)), вернуть(ему будет значительно сложнее учиться)).

Ответ 2



Если вы твердо решили научить ребенка плохому и испортить ему жизнь, то такая мелочевка как "невозможность переопределить ключевые слова" не должна останавливать истинного родителя. Следует вспомнить, что исходные коды языка открыты и могут быть модифицированы. Причем вы можете не только приготовить патчсет для себя, но и выложить его на Гитхаб, чтобы другие родители тоже могли повторить свой опыт. Если банальной локализации будет недостаточно, то можно пойти дальше и начать применять сторонние патчи, которые меняют синтаксис или добавляют новые, уникальные методы, которых в нормальном языке нет и с отсутствием которых во внешнем мире ребенок рано или поздно столкнется. Можно пойти еще более жестоким образом и сразу изучать язык 1С - язык на русском языке как вы и хотите, бухгалтерия нужна почти везде, в ряде регионов на рынке труда нужны только 1С-ники, при желании на 1С можно писать игры и даже сложные утилитки, да и даже английский можно не учить - все равно за полями и лесами и высокими горами про такой язык и не слыхивали, потому и помощи от англоговорящих можно и не ждать.

Почему сервер выдает not found из-за буквы х

#php


Здравствуйте. Есть правило в .htaccess

RewriteRule ^page\/(.+)/(.+?)$ php/page.php?type=$1&theme=$2


На сайте страницы открываются по ссылке: 


  сайт.ру/page/категория/название


и все хорошо. все страницы открывает как нужно. но проблема с открытием страницы
по ссылке:


  сайт.ру/page/категория/хрюша


То есть, если есть русская буква х - выдает Not Found

В чем может быть проблема?

.htaccess

AddDefaultCharset WINDOWS-1251
DirectoryIndex index.php

php_value register_globals 0 
php_value magic_quotes_gpc 0 


RewriteEngine On
RewriteRule ^ready modules/ready.php [L,QSA]
RewriteRule ^2017\/(.+)$ modules/document.php?url=$1
RewriteRule ^error modules/error-no-doc.php [L,QSA]
RewriteRule ^not-verified modules/not-verified.php [L,QSA]
RewriteRule ^moderation modules/moderation.php [L,QSA]
RewriteRule ^type\/(.+)$ modules/type.php?url=$1
RewriteRule ^all-document\/(.+)/(.+?)$ modules/all-document.php?type=$1&theme=$2
RewriteRule ^page\/(.+)/(.+?)$ php/page.php?type=$1&theme=$2




##ErrorDocument 404 /error`

    


Ответы

Ответ 1



Уважаемый, я не силен в регулярках, но может вам как то поможет пример с экранированием всех наклонных черт RewriteRule ^page\/(.+)\/(.+?)$ php/page.php?type=$1&theme=$2 я про второе \/ все равно. та же ошибка Я не силен в .htaccess, но попробуйте использовать флаги RewriteRule ^page\/(.+)\/(.+?)$ php/page.php?type=$1&theme=$2 [L,NC] Это может предотвратить ошибку если у вас хрюша например где-то вылезает с большой буквы. Если это не поможет - переведите формат файла в UTF-8, предварительно сохранив копию оригинала и отпишите о результатах. После каждых изменений перезапускайте сервер(хотя меня здесь могут поправить о необходимости данного шага).

Ответ 2



Вопросительный знак во втором выражении стоит не там. Удалите его или вынесите за круглую скобку - вот пример: Если вам нужно что-то ещё после ввода то привнесите варианты для конкретного рассмотрения в своём вопросе.

Ответ 3



Вопросительный знак лишний и не имеет смысла: RewriteRule ^page\/(.+)/(.+)$ php/page.php?type=$1&theme=$2

Как эмулировать большое разрешение экрана для адаптивной вёрстки? [дубликат]

#html #css #media_queries


        
             
                
                    
                        
                            This question already has an answer here:
                            
                        
                    
                
                        
                            Как правильно верстать макет, когда ширина экрана меньше
ширины макета в пикселях?
                                
                                    (1 ответ)
                                
                        
                                Closed 2 года назад.
            
                    
Как эмулировать большое разрешение экрана для адаптивной вёрстки?
    


Ответы

Ответ 1



Для более точного определения разрешения, можно сделать следующим образом: В мозилле: ctrl+shift+m Вверху можете вручную писать нужное разрешение В хроме: Правой кнопкой мыши на сайте и выбираете посмотреть код Тоже самое, можете задавать свои параметры

C/C++ сравнение указателей на разные объекты на равенство и отношение

#cpp #c #указатели #неопределенное_поведение


Уже который день пытаюсь разобраться, можно ли сравнивать указатели, относящиеся
к разным объектам...

Проблема заключается в том, что в Стандарте эта тема обрисована крайне расплывчато.

Вот это исследование говорит, что сравнение указателей, относящихся к разным объектам,
скорее UB, чем норма:

https://habr.com/company/pvs-studio/blog/418023/

Мои эксперименты показывают аналогичные результаты. В нескольких книгах по C11/C++11
так же нашел упоминание о том, что сравнивать можно только указатели на один и тот
же массив, плюс дополнительный элемент в конце массива.

Так же меня очень запутывает хрестоматийное соглашение о перегрузке оператора присваивания
(copy/move):

T &operator=(const T &_t)
{
    if (this != &_t)// Получается, это неопределенное поведение?
    {
    }
    return *this;
}


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

Мой вопрос:

Что конкретно (и по отдельности) говорят Стандарты C и C++ о сравнении указателей
на разные объекты? То, что написано в общедоступных черновиках, лично я понять не смог...
    


Ответы

Ответ 1



Денис Риччи: "Если р и q указывают на элементы одного массива, то к ним можно применять операторы отношения ==, !=, <, >= и т. д. Например, отношение вида p < q истинно, если р указывает на более ранний элемент массива, чем q. Любой указатель всегда можно сравнить на равенство и неравенство с нулем. А вот для указателей, не указывающих на элементы одного массива, результат арифметических операций или сравнений не определен." Что касается этого примера: T &operator=(const T &_t) { if (this != &_t)// Получается, это неопределенное поведение? { } return *this; } здесь производится проверка, не написал ли кто-то в коде что-то типа а = а;. В этом случае this и &_t будут указывать на один и тот же объект и (this != &_t) будет false.

Ответ 2



На практике, для всех компов, на которых я писал на языке Си, можно приводить к char * (или преобразовывать в size_t) и сравнивать. Imho после приведения к char * на равенство можно сравнивать всегда (и это останется в обозримом будущем справедливо для всех архитектур).

Ответ 3



О каком именно типе "сравнения" идет речь? Сравнивать на равенство/неравенство можно любые указатели (при условии совместимости типов), независимо от того, указывают ли они в один массив или нет. В С++ не специфицируется результат сравнения на равенство двух указателей, если один из них указывает на воображаемый элемент за одним объектом, а второй указывает на другой объект. В С в такой ситуации говорится, что указатели будут равны, если указывают в одно и то же место в памяти. Упорядочивающие сравнения разрешается применять только к указателям на элементы одного массива или полям одного класса. (Правило рекурсивно распространяется на подобъекты этих объектов.) В остальных случаях: в С++ на результат сравнения не накладывается никаких требований. В С открытым текстом говорится, что поведение не определено.

Ответ 4



Если даже не обращаться к стандарту ( в стандарте все по логике...), просто рассуждать логически: Что такое указатель?... Это место в памяти на одно машинное слово, где записывается адрес какого то обьекта или нулевое значение(нулевой указатель). Как можно сравнить эти адреса(что меньше, что больше?), по каким критериям?... Только если эти адреса совпадают или не совпадают, или нулевое или нет. А если это в пределах какого то обьекта, где адреса идут один за другим (массив), то тогда можно и сравнить какой адрес раньше(меньше) другого... По моему так...

Кто должен отвечать за уничтожение переданных в конструктор другого класса объектов?

#c_sharp #net


Скажем, есть класс работы с БД.

Ему на вход может быть передан Connection. Затем, этот Connection запоминается в
поле класса и класс работает через него. А можно передать вообще new Connection(),
тогда ссылка на этот экземпляр будет только в этом классе. Так же может быть еще один
конструктор, где строковое предстовление будет передано и я уже в своем конструкторе
создам сам Connection.

Как правило один объектConnectionможет быть использован параллельно. В MS SQL точно,
на сколько я помню, это разрешено.

Так вот, кто в данном случае должен отвечать за уничтожения объекта?

У нас получается 3 случая:


Connection может быть использоваться параллельно в вызывающем коде
Connection используется только в 1 классе
Connection используется только в 1 классе 


В первом случае, очевидно, вызывающий код должен быть ответственным, а во втором
по идее, сам класс, в третим очевидно мы сами, так как мы собственноручно создали объект
из строки => не понятно, как реализовать паттерн IDiposible. Хранить где-то флаги?
Однако, с флагами опять же не будет понятно, храню ли я только в своем классе ссылку
или вызывающий код ее тоже имеет.

Более того- это должно быть очевидно для конечного пользователя, так как класс для
него- это черный ящик.

Для примера, если заглянуть в исходники майков:


SqlCommand 
SqlDataReader


, то мы видим, что в первом случае команда не диспозит Connection, хотя он хранится
в полях класса, а во втором случае так вообще не реализован интерфейс IDisposible.

Может быть вообще правильно наружу выставлять подобные ресурсы в ReadOnly виде. Тогда
сам вызывающий код в любом случае сможет это прибивать?

Еще одним решением я вижу- это в конструкторе задавать bool флаг по типу closeAfterDispose
и тогда мы однозначно сможем определить нужно ли соединение пользователю.

Пример со строкой подключения- это просто один из примеров. Такие же вопросы могут
возникнуть при использовании Stream'ов, например.
    


Ответы

Ответ 1



Сейчас при построении архитектуры приложения активно применяется внедрение зависимостей (Dependency Injection, DI). В общем случае, внедрение предполагает, что классы получают ссылки друг на друга через конструктор, но не создают и не уничтожают объекты. За создание и уничтожение объектов отвечает такая штука, которую называют IoC-контейнер. Уточнение. IoC это старое название DI. Подробности о том, почему IoC превратилось в DI читайте в статье Мартина Фаулера, Inversion of Control Containers and Dependency Injection pattern. Это та самая статья, где введён термин Dependency Injection. Вот вольный перевод на русский язык. Чтобы всё это работало, обычно используют библиотеку, такую как Castle, Autofac, Ninject. Всё то же самое можно сделать и вручную, но это будет сложнее и дольше. Если мы решаем, что именно IoC-контейнер отвечает за создание и уничтожение объектов, вопрос нужно переформулировать: как компоновать классы с разным временем жизни, которые зависят друг от друга? Например, у вас есть SqlConnection, который в приложении REST API обычно «живёт» на протяжении HTTP-запроса, а для доступа к данным вы используете какой-нибудь OrderRepository. Репозитории не содержат собственного состояния, поэтому их эффективно создать один раз и просто хранить в течение всего времени работы приложения. Мы не может передать в конструктор репозитория готовое соединение, его просто не существует. Мы должны создать фабрику или локатор, скажем, SqlConnectionFactory, которую будем вызывать, когда нам потребуется подключение. Фабрика создаёт нам новый объект, и, если он disposable, мы должны освободить его в том же методе, пользуясь оператором using. Локатор находит готовый объект, и мы не должны его освобождать сами. Для конкретного примера возьмём библиотеку Autofac. Для начала опишем зависимости между классами/интерфейсами. public class OrderRepository : IOrderRepository { private readonly Func _locateSqlConnection; public OrderRepository(Func locateSqlConnection) { _locateSqlConnection = locateSqlConnection; } public Order ReadById(int id) { var connection = _locateSqlConnection(); using (var command = connection.CreateCommand()) { // Здесь загружаем данные, конструируем Order и возвращаем его . . . return new Order(id, createdAt, amount); } } . . . } Здесь показано, как локатор может быть описан в Autofac. Когда будет вызвана функция _locateSqlConnection, Autofac вернёт существующее подключение, создав его, если его не было. Если ответственность за создание SqlConnection лежит на библиотеке Autofac, она же должна освободить объект. По умолчанию, Autofac сохраняет все ссылки на созданные IDisposable объекты и освобождает их, когда контейнер больше не нужен. Контейнеров в программе может быть несколько, и Autofac умеет создавать специальный контейнер для HTTP-запроса. Для разных запросов будут созданы разные контейнеры и, соответственно, разные экземпляры SqlConnection. Чтобы это всё работало, достаточно правильно описать «время жизни» при регистрации репозитория в Autofac. builder.Register() .As() .SingleInstance(); OrderRepository это singleton, который будет создан один раз. SqlConnection создаётся один раз на HTTP-запрос: builder.Register() .InstancePerRequest(); А что с SqlCommand? SqlCommand мы создаём сами в рамках метода OrderRepository.ReadById, поэтому и освобождать его мы должны сами. Подробнее о DI/IoC можно почитать в книге Внедрение зависимостей в .NET. В сети бродит PDF, но ссылку давать не буду, потому что не знаю, как там с авторскими правами.

Ответ 2



Надеюсь, что вопрос понял правильно :) На сколько я понимаю GarbageCollector(GC) важно количество ссылок на обьект с разных мест. То есть, если на некий обьект ссылается еще хотя бы один обьект - он не будет уничтожен. Так же есть обьекты которые нужно принудительно уничтожать из памяти, до того как их захавает GC. Они наследуются через iDisposable. У них нужно вызывать Dispose() для того, что бы очистить вручную не дожидаясь когда это сделает GC. В основном это используется для больших обьектов, например, картинок. Что б прога не жрала памяти по пару гигабайт на пустом месте, до момента, пока не проработает GC :) Connection может быть использоваться параллельно в вызывающем коде Он будет помечен на освобожденние как только закончится работа ОБЕИХ методов - и вызывающего и вызываемого. А освобожден во время вызова GC. В случае если это конекшн, то он наследуется от IDisposable только в случае, если ты хочешь сделать нагрузку на БД меньшей. То есть сделать так, что бы к ней было приконекчено как можно меньше пользователей одновременно. Если же проэкт маленький изначально, то с этим можно и не запариватся. Connection используется только в 1 классе Он будет помечен на освободление как только закончится этот метод. И будет диспоузонут автоматически при вызове GC. Ну или, лучше, диспоузонуть вручную (раз уж гарантированно!!! не затронет ничего лишнего) Вызывать Dispose() ты должен только в том случае, когда ты уверен что конекшн никем более не используется. Если же такой уверенности нет - лучше отдать эту обязанность на GC. И в случае с конекшнами к базам данных, хочу напомнить что базы данных сами могут обрывать конекшн по таймауту. То есть если клиент не отзывается некоторое время, то сервер перестает с ним работать.

Анти-паттерн сессия-на-операцию

#java #hibernate #java_ee #шаблоны_проектирования #java_8


В одной статье по Hibernate прочитал, что если вы хотите использовать многопоточность,
то создавайте новую сессию для каждой CRUD-операции. То есть вот как выглядит, например,
операция сохранения в моем DAO-классе:

public void save(User user) {
    try (Session session = HibernateSessionFactoryUtil.getSessionFactory().openSession()) {
        Transaction tx = session.beginTransaction();
        session.save(user);
        tx.commit();
    }
}


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


  Это анти-паттерн про открытие и закрытие объекта Session на каждую операцию к БД
в одном потоке. Это также анти-паттерн в терминах транзакций БД. Группируйте ваши вызовы
в одну запланированную последовательность. Также, не делайте авто-коммит транзакции
на каждое SQL-выражение. Hibernate выключает, или ожидает, что сервер приложений немедленно
выключит режим авто-коммита. 


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


  Наиболее распространенный паттерн транзакций. Термин “запрос” здесь следует понимать
в контексте системы, реагирующей на серии запросов от пользователя/клиента. Веб-приложения
является основным примером таких систем, но, конечно, не только они одни. На этапе
начала обработки запроса, приложение открывает объект Session, инициирует транзакцию,
проводит всю сопутствующую работу с данными, завершает транзакцию и закрывает Session.
Суть паттерна – это отношение один-к-одному между транзакцией и сессией. 


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


Ответы

Ответ 1



Если обратится к документации Hibernate - то по ней сессия-на-операцию официально обьявлена антипаттерном. Что касается сессии, то сам ее смысл предполагает что вы можете и по возможности должны объединять связанные по смыслу операции в рамках одной или нескольких транзакций в одну сессию. В тоже время ничего плохого нет в том что ваша сессия может содержать лишь одну операцию, если кроме этой операции ничего делать не надо. Чтобы понять разницу между первым и вторым приведу пример: представьте что существует еще один класс связанный с User, например UserHistory (пусть он будет хранить историю изменения имени пользователя). Мы предполагаем по смыслу что изменяя name в User должен обязательно обновляться и класс UserHistory. Теперь представим что мы сделали все через operation-per-session. В этом случае у нас будет две разные сессии и две транзакции которые обновят User и UserHistory независимо друг от друга. Проблема возникнет тогда когда одна из транзакций не дойдет (например из за внезапных потерь на сети) что неизбежно приведет к нарушению целостности данных в БД. И да, при работе с БД не через пулы - крайне ресурсоемко создавать и закрывать соединения с БД при каждом действии.

Ответ 2



Действительно как пишется в документации к Hibernate: The scope of a Hibernate org.hibernate.Session is flexible but you should never design your application to use a new Hibernate org.hibernate.Session for every database operation. Even though it is used in the following examples, consider session-per-operation an anti-pattern Обратите внимание, что говорится о новой сессии, по сути предлагается использовать текущую сессию, а именно: Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); //blah-blah session.getTransaction().commit(); То есть если нет текущей сессии, то создаем, если есть берем текущую. Создание сессии при каждой операции является антипаттерном, но не потому, что типа в другой сессии будет произведено обновление или там не дойдет до конца транзакция (менеджер транзакций СУБД вполне умеет с такими вещами работать), а из-за стоимости создания сессии - по сути это новый коннект к БД - крайне дорогая и ресурсоемкая операция.

Как добавить возможность изменять размеры столбцов таблицы с помощью мыши в GWT?

#gwt #java


Не хочу использовать gwt-ext и подобные ради этой цели. Как на чистом GWT реализовать
ресайзеры для столбцов таблицы?    


Ответы

Ответ 1



Есть два основных варианта, как это можно сделать: Отслеживать сообщения MouseDown/Move/Up с помощью sinkEvents & onBrowserEvent в самой компоненте заголовка, и анализировать координату если она +/-5 от границы, то обрабатывать как при dnd. Использовать отдельные компоненты в качестве управляющих изменением размера, и поместить их поверх заголовка таблицы, например, с помощью layout panel, и сделать невидимыми (opacity = 0.0). Второй способ более правильный, он используется, например, в google-docs (главная таблица с файлами).

Ответ 2



В GWT-2.2-M1 эта функциональность есть в самой библиотеке. См.коммит от 7 янв 2011.

Как получить аргументы функции?

#c


Здравствуйте. Есть исходник
void some_func()
{
    SOME_CODE_HERE;
}

int main(int argc, char *argv[])
{
    some_func(1,2,3);
    return 0;
}

Вопросы: 

Как из функции some_func получить аргументы без ассемблерных вставок?
Зачем вообще в Си было ввведено различие между объявлением

-
void some_func() {}

и
void some_func(void) {}

P.S.:
На всякий пожарный: крики "твоё творение не компилится" не принимаются, потому что
это враки и gcc всё компилит даже без варнингов.
Заранее спасибо за ответы.    


Ответы

Ответ 1



Для 32-bit такой фокус возможен, т.к. компилятор передает параметры через стек. В нем же запоминается base pointer (регистр от которого берутся смещения локальных переменных и параметров). Поэтому, если вспомогательная функция вернет значение bp интересующей нас функции, то мы имеем доступ к аргументам. Короче int **getbp (int arg) { /* caller bp (arg[-2] == bp; arg[-1] == return_addr; arg[0] == arg) */ return (int **)(&arg-2); } void some_func() { //SOME_CODE_HERE; write(1,"xaxa\n",5); int **bp = getbp(1); int *args = *bp + 2; printf ("%d %d %d\n",args[0],args[1],args[2]); } int main(int argc, char *argv[]) { some_func(1, 2, 3); return 0; } Использование: c:/Users/avp/src/cc/hashcode $ gcc noargs.c c:/Users/avp/src/cc/hashcode $ ./a xaxa 1 2 3 c:/Users/avp/src/cc/hashcode $ В 64-bit х86 gcc такая штука не пройдет, так как несколько параметров (в данном случае все) передаются через регистры. Вы можете посмотреть ассемблерный код (в файле noargs.s) командой: gcc -S noargs.c @Котик_хочет_кушать видимо ответил вам (я не вчитывался в аргументацию). Update По крайней мере в gcc 5.4.0 (проверял в Ubuntu 16.04.1 LTS) в x86_64 GNU/Linux параметры передаются в функцию в регистрах. Первые 6 целых (включая указатели) в регистрах rdi, rsi, rdx, rcx, r8 и r9, а первые 8 действительныx (float и double) в регистрах с xmm0 по xmm7. Остальные помещаются в стек в обратном порядке. Т.е. параметры, которые находились в списке вызова функции ближе к его началу, окажутся ближе к вершине стека (в памяти с меньшими адресами, поскольку стек растет от старших адресов к младшим). Идея извлечения значений параметров без использования ассемблера состоит в том, что надо "обмануть" компилятор и вызвать без аргументов функцию у которой определен подходящий список аргументов, которые она должна положить в память и вернуть эту память в some_func(). Проблема в том, что вызов (и возврат результата) надо проделать так, чтобы компилятор эти регистры (он же, компилируя some_func() считает, что у нее нет параметров) не попортил. Что касается параметров, которые не поместились в регистры и записаны в стек, то мне удалось достать их только в том случае, когда все функции компилируются без оптимизации. В таком случае gcc адресует каждый фрейм функции (ее локальные переменные) регистром rbp (при входе в функцию он запоминается в стеке и загружается новым значением указателя стека (rsp)) и связывает их в список. (самому жутко интересно, каким образом отладчик находит фреймы для программы с оптимизацией? Кое-что можно прочесть тут, возможно основное там -- gdb has a whole bunch of unwinders, including one that scans the machine code to figure out how the frame was setup. что явно выносит попытки написания корректного примера для оптимизированного кода за рамки этого вопроса) Итак, имеем такую программу (main и some_func) #include #include "getargs64.h" void some_func() { GETARGS64(arg); puts("some_func integer:"); for (int i = 0; i < N_IREGS || (puts(""), 0); i++) printf("%ld ", arg.ir[i]); puts("some_func float:"); for (int i = 0; i < N_FREGS || (puts(""), 0); i++) printf("%f ", arg.fr[i]); if (arg.sa.isa) printf("args in stack: [%ld %ld %ld %lf %lf %lf %ld %ld]\n", arg.sa.isa[0], arg.sa.isa[1], arg.sa.isa[2], arg.sa.fsa[3], arg.sa.fsa[4], arg.sa.fsa[5], arg.sa.isa[6], arg.sa.isa[7]); else puts("can't get args in stack"); //SOME_CODE_HERE; } int main (int ac, char *av[]) { some_func(1, 2, 3, 11.1, 12.1, 13.1, 4, 5, 6, 14.1, 15.1, 16.1, 17.1, 18.1, 7, 8, 9, 17.2, 18.2, 19.2, 7001, 7002, 7001.7, 7002.8); puts("again X 10"); some_func(110.1, 120.1, 130.1, 10, 20, 30, 40, 50, 60, 140.1, 150.1, 160.1, 170.1, 180.1, 70, 80, 90, 170.2, 180.2, 190.2); return puts("End") == EOF; } Для реализации нашего плана пишем вот такой заголовочный файл (getargs.h) #ifndef _GETARGS64_H #define _GETARGS64_H #include #include #define N_IREGS 6 #define N_FREGS 8 uint64_t *get_iregs(); double *get_fregs(); uint64_t *get_stackargs (uint64_t a, ...); struct regargs { uint64_t ir[N_IREGS]; double fr[N_FREGS]; union { uint64_t *isa; double *fsa; } sa; }; // макрос определен gcc, если он вызван с ключами оптимизации #ifndef __OPTIMIZE__ #define STACKARGS(name) name.sa.isa = get_stackargs(1, 2, 3, 4, 5, 6, \ get_stackargs) #else #define STACKARGS(name) name.sa.isa = 0 #endif #define GETARGS64(name) struct regargs name; \ { \ uint64_t *iargs = get_iregs(); \ double *fargs = get_fregs(); \ \ for (int i = 0; i < N_IREGS; i++) \ name.ir[i] = iargs[i]; \ for (int i = 0; i < N_FREGS; i++) \ name.fr[i] = fargs[i]; \ free(iargs); free(fargs); \ STACKARGS(name); \ } #endif с макросом GETARGS64(name), который надо разместить в первой строке some_func(). Этот макрос создает структуру struct regargs с именем name, которая будет заполнена значениями регистров и (если возможно) указателем на оставшиеся параметры some_func в стеке. И наконец, сами функции, извлекающие значения регистров и параметры вызова в стеке: #include #include #include #include #include #define N_IREGS 6 #define N_FREGS 8 uint64_t * get_iregs (uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { uint64_t r[N_IREGS] = {a0, a1, a2, a3, a4, a5}; return memcpy(malloc(N_IREGS * sizeof(uint64_t)), r, N_IREGS * sizeof(uint64_t)); } double * get_fregs (double a0, double a1, double a2, double a3, double a4, double a5, double a6, double a7) { double *fr = malloc(N_FREGS * sizeof(double)); fr[0] = a0; fr[1] = a1; fr[2] = a2; fr[3] = a3; fr[4] = a4; fr[5] = a5; fr[6] = a6; fr[7] = a7; return fr; } #ifndef __OPTIMIZE__ #define ARGS_OFFSET 2 #include #include #include // check memory access by addr, returns 1 if addr readable/writable, 0 otherwise static int is_mem_accessible (void *addr, int writable) { if (addr == 0 || addr == (void *)-1LL) return 0; volatile int rc = 1; sigjmp_buf jmp; // signal handler nested function void sighdr (int sig) { siglongjmp(jmp, 0); } void (* sigbus)(int) = signal(SIGBUS, sighdr), (* sigsegv)(int) = signal(SIGSEGV, sighdr); if (sigsetjmp(jmp, 1)) { // MANDATORY save sigmask rc = 0; errno = EFAULT; } else { volatile char t = *((char *)addr); if (writable) *((char *)addr) = t; } signal(SIGBUS, sigbus); signal(SIGSEGV, sigsegv); return rc; } uint64_t * get_stackargs (uint64_t a, ...) { uint64_t *p; for (p = (uint64_t *)(&p + 1); *p != (uint64_t)get_stackargs; p++); p = (uint64_t *)p[-2]; return is_mem_accessible (p + ARGS_OFFSET, 1) ? p + ARGS_OFFSET : 0; } #else uint64_t * get_stackargs (uint64_t a, ...) { return 0; } #endif Если есть какие-то вопросы, задавайте в комментариях (или чате C/C++), попробуем разобраться.