Страницы

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

понедельник, 15 октября 2018 г.

Является ли асинхронный вызов рекурсией?

Определение рекурсии говорит, что это прямой или косвенный вызов функции из себя.
Т. о. функция go в следующем примере рекурсивна:
function pass(f, i) { return f(i); } function go(i) { if (i < 3) { console.log(i); pass(go, i + 1); } else { console.log(new Error().stack); } } go(0); html .as-console-wrapper { top: 0; max-height: none; }
А теперь заменим функцию pass на асинхронную операцию:
function go(i) { if (i < 3) { console.log(i); setTimeout(go, 0, i + 1); } else { console.log(new Error().stack); } } go(0); html .as-console-wrapper { top: 0; max-height: none; }
Что изменилось? В первом случае стек вызовов содержал цепочку go-pass-go-pass-go-pass-go, а во втором там только одна функция - go. Асинхронная операция помещается в очередь операций и при вызове не вложена в то, что её планировало.
Верно ли такой вызов называть рекурсивным?
Мне очень хочется сказать, что нет, однако вспоминается хвостовая рекурсия, которая тоже компилятором разворачивается в цикл. Вроде как получается, что вложенность по стеку вызовов для рекурсии необязательна. Другой момент - что вызывающая функция не находится в процессе выполнения в момент выполнения вложенного вызова, но и тут хвостовая рекурсия ставит под вопрос необходимость этого факта...


Ответ

Обычно такое поведение называют асинхронной рекурсией. Между синхронным и асинхронным кодом существует изоморфизм, и асинхронная рекурсия изоморфна обычной рекурсии.
Изоморфизм, к слову, довольно простой, и "на пальцах" проще всего его объяснить на примере обещаний. Если есть код вида
function foo() { // stage1 var promise = stage2async(); stage3sync(); return promise; }
то он его приведет к вот такому виду:
function foo() { // stage1 var promise = stage3async(); stage2sync(); return promise; }
Разумеется, применять его надо ко всем библиотечным функциям, включая базовое API, т.е. это больше теоретическое измышление, а не практическая возможность. Как видно, этот изоморфизм еще и сам себе обратный.
Существование такого изоморфизма, в том числе, означает, что любые проблемы обычной рекурсии могут всплыть и в асинхронной версии. Другое дело, что асинхронные решения обычно меньше подвержены сопутствующим проблемам благодаря тому, что асинхронный поток "дешевле" системного.
Например, переполнение стека
Обычная рекурсия может запросто переполнить стек. Асинхронная рекурсия такому, казалось бы, не подвержена...
...до тех пор пока для возврата значения мы не начинаем собирать "сосиску" из обратных вызовов. Или пока платформа не начнет в целях диагностики отслеживать асинхронный стек вызовов, что может привести к утечке памяти.
Кстати, Google Chrome в своей реализации Promise асинхронный стек вызовов отслеживает - и это даже очень удобно. Пока не возникнет утечка памяти из-за бесконечной асинхронной рекурсии.

Описать не только методы, но и свойства объектов в gitbook

Пишу документацию по lua модулю. В нём фигурирует объект, у которого кроме методов есть ещё и свойства. В свою очередь, плагин theme-api для gitbook позволяет описать только методы. Как, в таком случае, описать свойства объекта в gitbook?


Ответ

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

Приведение типа к dynamic

dynamic instance = new Program();
- Это будет UpCust или DownCast?
Или как я догадываюсь - когда во время выполнения тип переменной instance будет меняться, то это будет динамическое приведение типов? -Т.е динамически в зависимости от того что в dynamic по ходу выполнения программы будет происходить то UpCust то DownCast?
UPD:
dynamic instance = new A;
.Хорошо, возьмем не Program и dynamic, а два класса А и Б и если Б дочерний от А и на строке и если вместо типа dynamic -тип будет тот же что и справа - то привидения не будет, не если в прцесе выполнения вместо dynamic будет например тип B, то это будет DownCast. Вот в чем моя мысль. просто может я не очень удачно взял пример кода с Program - т.к его не к чему приводить, поэтому для данного конкретного случая
dynamic instance = new Program();
Подсказали здравую мысль - У переменной instance будет фактический тип правой части присваивания, приведения типов там не будет. - но если взять шире, абстрактнее - то вот в чем мой вопрос?
Т.е ВОПРОС ПО ПРЕЖНЕМУ В ЦЕЛОМ ОСТАЕТСЯ ОТКРЫТЫМ.


Ответ

Тип dynamic является особым типом, не совпадающим с типом object. Он не является классом, и поэтому не участвует в иерархиях наследования: произвести класс от dynamic нельзя. Я не смог найти его непосредственный базовый тип в спецификации, но его базовым типом как минимум является object
Это означает, что конверсия из dynamic в другие типы и обратно не может являться ни up-, ни downcast'ом (за исключением типа object).
Какая же конверсия происходит при присвоении между dynamic и другими типами? Ответ на это можно найти в спецификации языка: это специальные динамические конверсии, описанные в разделах 6.1.8 (неявная, из другого типа в dynamic), и 6.2.6 (явная, из dynamic в другой тип).
Итак, ответ: это специальные динамические конверсии, не являющиеся ни up-, ни downcast'ом.

Связанный с делегатом объект

На msdn прочитал такой абзац
A delegate is said to be bound to the method it represents. In addition to being bound to the method, a delegate can be bound to an object. The object represents the first parameter of the method, and is passed to the method every time the delegate is invoked. If the method is an instance method, the bound object is passed as the implicit this parameter (Me in Visual Basic); if the method is static, the object is passed as the first formal parameter of the method, and the delegate signature must match the remaining parameters.
Объясните, пожалуйста, что это за объект, почему он представляет первый параметр метода (как это вообще понять?), и как он передается в первый формальный параметр метода, если метод статический?


Ответ

Прекрасный вопрос! Вы попали на один из «пыльных углов», это редко посещаемая область на карте .NET.
Смотрите. У .NET есть два типа делегатов: открытые и замкнутые. Если вы посмотрите на тип Delegate, у него есть свойства Method и Target. Так вот, замкнутые делегаты — это те, которые используют Target, а открытые — те, у которых Target игнорируется.*
Как вы помните, вызов нестатического метода можно представить себе мысленно как вызов функции, у которой добавлен ещё один аргумент, представляющий this. (На этой аналогии базируется понятие методов расширения в C#.)
Разница между открытыми и замкнутыми делегатами на первый взгляд напоминает разницу между статическими и простыми (нестатическими) методами: вызов простого метода привязан к целевому объекту (то есть, объекту, у которого будет этот самый метод вызван, тому, который будет доступен как this внутри метода). Кажется естественным представлять статические методы открытыми, и нестатические — замкнутыми делегатами.
Но эта аналогия обманчива. На самом деле, как статический, так и нестатический метод могут быть «упакованы» как в открытый, так и в замкнутый делегат!
В простом случае, когда замкнутый делегат ссылается на простой метод, у Target'а вызывается Method. Что же происходит, когда простой метод попадает в открытый делегат? А вот что. Для вызова такого делегата нужно указать ещё одним аргументом объект, на котором данный метод будет вызван. При этом список аргументов для вызова делегата увеличится на 1. Таким образом, мы как бы отвязываем метод от объекта.
Тестовый код. Заводим простой класс и простой метод в нём:
class Test { int n; public Test(int n) { this.n = n; } public void Display(int m) { Console.WriteLine($"object {n}, arg {m}"); } }
Для создания замкнутых делегатов можно использовать перегрузки Delegate.CreateDelegate с параметром firstArgument, для замкнутых — без этого аргумента**. Ну или подойдут встроенные в язык средства. Тестируем:
Test t1 = new Test(1), t2 = new Test(2); // создали два экземпляра Type typeT = typeof(Test); // запомнили тип и MethodInfo для метода MethodInfo miDisplay = typeT.GetMethod("Display");
// замкнутый делегат можно создать так: Action closedInstance1 = (Action)Delegate.CreateDelegate( typeof(Action), t1, miDisplay); // более прямой метод встроен в язык: Action closedInstance2 = t2.Display;
// вызываем closedInstance1(10); // object 1, arg 10 closedInstance2(11); // object 2, arg 11
// открытый делегат создаётся так: Action openDelegate1 = (Action)Delegate.CreateDelegate( typeof(Action), miDisplay); // более удобного, встроенного в язык метода создать открытый делегат // из нестатического метода нет openDelegate1(t2, 10);
Окей, теперь случай статического метода. В случае открытого делегата вызов прост: статический метод не требует наличия объекта для своего вызова. А что же происходит в случае замкнутого делегата? А вот что. Первый параметр из списка параметров метода фиксируется: его значение берётся всегда из поля Target нашего делегата. Список параметров укорачивается на 1. Таким образом, мы видим, что происходит замыкание по первому аргументу функции.
Тестируем:
Type typeST = typeof(StaticTest); // запомнили тип и MethodInfo для метода MethodInfo miDisplayEx = typeST.GetMethod("DisplayEx"), miPrintTwo = typeST.GetMethod("PrintTwo");
// открытый делегат можно создать через библиотечную функцию Action openDelegate2 = (Action)Delegate.CreateDelegate( typeof(Action), miDisplayEx); // или прямо через встроенный в язык метод Action openDelegate3 = StaticTest.PrintTwo; // работает и для методов расширения Action openDelegate4 = StaticTest.DisplayEx;
// вызываем openDelegate2(t1, 20); // More: object 1, arg 20 openDelegate3("hello", 3); // x = hello, y = 3 openDelegate4(t2, 21); // More: object 2, arg 21
// замкнутый делегат Action closedDelegate2 = (Action)Delegate.CreateDelegate( typeof(Action), "goodbye", miPrintTwo); // для методов расширения есть удобное средство в языке Action closedDelegate3 = t2.DisplayEx; // а вы знали, что так можно?
// вызываем closedDelegate2(7); // x = goodbye, y = 7 closedDelegate3(30); // More: object 2, arg 30
Замыкание по первому аргументу значимого типа невозможно***.
Суммируя, у нас возможно 4 случая:
замкнутый делегат и простой метод: вызывается метод у фиксированного объекта замкнутый делегат и статический метод: первый аргумент метода фиксируется открытый делегат и простой метод: для вызова нужно указать и объект, и аргументы метода открытый делегат и статический метод: вызывается метод как обычно, объект не нужен

Имеется ли у этого практическое применение? Наверное, в современном C#, где можно легко «тасовать» сигнатуры функций и проводить любые замыкания (а не только по неявному аргументу) при помощи лямбда-функций, это не так уж и необходимо, и может понадобиться лишь для экстремальной оптимизации, чтобы избежать создания лямбд.

Литература:
Schabse Laks, Open Delegates vs. Closed Delegates MSDN, документация на класс Delegate, секция Remarks MSDN, документация к методу Delegate.CreateDelegate, секция Remarks

*У замкнутого делегата может быть Target == null, как ни странно. При его вызове случится, как и имеет смысл ожидать, NullReferenceException
**Или с аргументом, равным null. Чем же отличается для функции Delegate.CreateDelegate замкнутый по null делегат от открытого, ссылающегося на нестатический метод? Количеством аргументов в сигнатуре делегата, которую вы передаёте первым аргументом!
***Причины этого такие же, как и причины невозможности получения делегата из метода расширения для значимого типа (оно ведёт к ошибке CS1113). О внутренних причинах этого написано здесь. Хотя сообщение об ошибке, которое производит Delegate.CreateDelegate, выглядит совершенно непонятно.

Неожиданное поведение фильтров исключений

Искал информацию по фильтрам исключений в C# и наткнулся на эту статью. В статье описывается случай, когда происходит исключение непосредственно внутри фильтра исключений, например:
class Program { public static void Main() { TestExceptionFilters(); } public static void TestExceptionFilters() { try { throw new Exception("Original Exception"); } catch (Exception ex) when (MyCondition()) { Console.WriteLine(ex); } } public static bool MyCondition() { throw new Exception("Condition Exception"); } }
Со следующим комментарием к коду:
Таким образом, мы можем указать условие в фильтре исключений; catch-блок будет исполняться только в том случае, если условие выполнилось. И мы можем использовать bool-функцию в качестве условия. Но что произойдёт, если само условие выбросит исключение? Ожидаемым поведением является следующее: исключение игнорируется, условия считается ложным
Вот уж для меня действительно стало неожиданным, что ожидаемое поведение - это игнорирование исключения. Сразу не поверил, но компилятор ведет себя так, как и ожидают люди: игнорирует исключение в фильтре
Сам вопрос: как так произошло, что игнорировать исключения - это норма? А что если я захочу логгировать ошибку в фильтре (это одно из применений фильтров исключений), а у меня там будет ошибка (нет такого файла, например), то мало того, что я не увижу, что у меня есть ошибка, так еще и не зайду в нужный блок catch, так как "условие будет считаться ложным"


Ответ

Очень похоже что это не норма, а банальная необходимость. И вот почему: в .NET и C# в частности гарантируется выполнение блока finally, независимо от результатов работы кода в блоке try и наличия либо отсутствия обработки возникших исключений в блоках catch. Ваш код порождает исключение в потоке управления конструкции try ... catch ... finally. Если бы оно не было проигнорировано, это привело бы к прерыванию потока управления и невозможности выполнить блок finally. Но выполнение finally гарантировано спецификацией.
Получили явное противоречие. Простой выход из ситуации - игнорировать исключения в потоке управления конструкции try ... catch ... finally, для соблюдения спецификации.
Можно было бы пробросить полученное исключение на уровень выше, а в свойстве InnerException передать исходное исключение, из-за которого все и произошло, но от такого решения будет больше путаницы, чем пользы.
PS: упомянутый поток управления не имеет ни какого отношения к многопоточности

Непосредственно к вопросу не относится, но для общей картины нужно добавить, что есть ряд исключений, для которых не выполняются ни catch, ни finally. Как правило это исключения вызывающие падение домена приложения или самой CLR, причина которых может быть как серьезной внутренней ошибкой приложения (например StackOverflowException), так и внешними проблемами CLR или операционной системы (частный случай OutOf MemoryException).

Рисование на окне другого приложения с помощью WinAPI

Нужно написать на С# программу, которая будет определять над каким окном Windows сейчас находится курсор и обводить это окно рамкой (указывая пользователю, над каким элементом находится курсор, ведь в WinApi, окном является не только само окно, содержащее в себе множество других элементов, но и эти элементы тоже являются окнами).
Handler окна под курсором получить легко:
... Point point; WinApi.GetCursorPos(out point); WinApi.WindowFromPoint(point); ...
Но вот нарисовать что то на окне не выходит, хоть убейте.
public static void drawSelectionRectangle(IntPtr handler) { Rectangle rectangle; WinApi.GetWindowRect(handler, out rectangle);
WinApi.PAINTSTRUCT paintProperties; IntPtr paintContext = WinApi.BeginPaint(handler, out paintProperties);
IntPtr pen = WinApi.CreatePen(WinApi.PenStyle.PS_SOLID, 5, (uint) ColorTranslator.ToWin32(Color.Red)); WinApi.SelectObject(paintContext, pen);
WinApi.Rectangle(paintContext, rectangle.Left, rectangle.Top, rectangle.Right, rectangle.Bottom);
WinApi.ValidateRect(handler, IntPtr.Zero); WinApi.EndPaint(handler, ref paintProperties); }
drawSelectionRectangle(IntPtr handler) вызывал и одиночно (по нажатию кнопки) и в цикле (так как предполагал, что отрисовка может выполнятся за один кадр, после чего, исчезать). Ни так ни так не работает.
Подскажите, пожалуйста, в чем проблема... Скоро курсовой сдавать, а ничего не готово :(


Ответ

Проблема в неверном подходе. Во-первых, забудьте про функцию BeginPaint (вне обработки сообщения WM_PAINT), во-вторых, рисовать надо не в контексте целевого окна, а в контексте его родительского окна (контекст окна позволяет рисовать только в его клиентской области, а рамка-то нам нужна снаружи).
Я предлагаю сделать как-то так:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Runtime.InteropServices;
namespace DrawingTest { public partial class Form1 : Form { [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] static extern bool GetCursorPos(out POINT lpPoint);
[DllImport("user32.dll")] static extern IntPtr WindowFromPoint(POINT p);
[DllImport("user32.dll", SetLastError = true)] static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
[DllImport("user32.dll")] static extern bool ScreenToClient(IntPtr hWnd, ref POINT lpPoint);
[DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)] public static extern IntPtr GetParent(IntPtr hWnd);
public Form1() { InitializeComponent(); }
private void timer1_Tick(object sender, EventArgs e) { //получаем окно в текущей позиции курсора POINT pt; GetCursorPos(out pt); IntPtr hwnd = WindowFromPoint(pt);
//получаем родительское окно IntPtr hwnd_p = GetParent(hwnd);
//получаем границы окна RECT rc; GetWindowRect(hwnd, out rc);
//перевод из экранных координат в клиентские POINT pt1=new POINT(rc.Left,rc.Top), pt2=new POINT(rc.Right,rc.Bottom); ScreenToClient(hwnd_p, ref pt1); ScreenToClient(hwnd_p, ref pt2); RECT rc_client = new RECT(); rc_client.Left = pt1.X-1; rc_client.Top = pt1.Y-1; rc_client.Right = pt2.X; rc_client.Bottom = pt2.Y;
//формируем структуру для GDI+ Rectangle rect = new Rectangle(rc_client.Left, rc_client.Top, rc_client.Right - rc_client.Left, rc_client.Bottom - rc_client.Top);
//получаем контекст окна Graphics g = System.Drawing.Graphics.FromHwnd(hwnd_p); using (g) { //рисуем прямоугольник g.DrawRectangle(Pens.Red, rect); }
} }
[StructLayout(LayoutKind.Sequential)] public struct POINT { public int X; public int Y;
public POINT(int x, int y) { this.X = x; this.Y = y; }
public POINT(System.Drawing.Point pt) : this(pt.X, pt.Y) { }
public static implicit operator System.Drawing.Point(POINT p) { return new System.Drawing.Point(p.X, p.Y); }
public static implicit operator POINT(System.Drawing.Point p) { return new POINT(p.X, p.Y); } }
[StructLayout(LayoutKind.Sequential)] public struct RECT { public int Left; // x position of upper-left corner public int Top; // y position of upper-left corner public int Right; // x position of lower-right corner public int Bottom; // y position of lower-right corner } }

Если целевое окно перекрывается другими окнами, соответствующая часть рамки будет скрыта. Чтобы рисовать рамку поверх всех окон, код рисования нужно изменить, используя контекст рабочего стола:
//получаем окно в текущей позиции курсора POINT pt; GetCursorPos(out pt); IntPtr hwnd = WindowFromPoint(pt);
//получаем границы окна RECT rc; GetWindowRect(hwnd, out rc);
//формируем структуру для GDI+ Rectangle rect = new Rectangle(rc.Left, rc.Top, rc.Right - rc.Left, rc.Bottom - rc.Top);
//получаем контекст рабочего стола Graphics g = System.Drawing.Graphics.FromHwnd((IntPtr)0); using (g) { //рисуем прямоугольник g.DrawRectangle(Pens.Red, rect); }
Иллюстрация различия между этими двумя методами:

Какова причина, по которой меню верстают через ненумерованный список ul?

Какова причина, по которой меню верстают через ненумерованный список ul?
Не знаю "правильно" это или нет, но у меня получилось без всяких проблем сверстать нормально работающее одноуровневое горизонтальное меню в виде таблицы, а с помощью flexbox так и вообще замечательно всё верстается.


Ответ

Сначала для верстки таких меню действительно использовались таблицы потому что не было поддержки многих CSS-фич. Вообще все версталось таблицами. Со временем технологии развивались и таблицы потеряли свою актуальность в разметке страницы из-за того что таблицы сложно поддерживать.
Тогда была два актуальных варианта: обернуть ссылки в див, сделать список. Из этих двух подходов список более правилен с семантической точки зрения потому что меню — это как раз список ссылок.
В стандарте HTML5 появился тег nav, но, несмотря на это, в спецификации рекомендуют использовать список для списка ссылок для упрощения понимания структуры навигации. Как-то так:
nav { border: 1px solid #ccc; } nav ul { display: flex; width: 100%; margin: 0; padding: 0; } nav li { border-right: 1px solid #ccc; flex-grow: 1; padding: 5px; list-style: none; } nav li:last-child { border-right: none; }


Конвертация std::string в std::wstring

Доброго времени суток. Способов несколько знаю, вот к примеру:
std::string str("abcdefg"); std::wstring output(str.begin(), str.end());
Но проблема становится ребром, если в экземпляре класса str используются русские символы. output создаётся не с теми символами, что были в str. Как победить проблему? Заранее спасибо. P.S.: вопрос короткий, не относящийся к главному: под сколько элементов по-умолчанию выделяет класс vector память. Если использовать его конструктор по-умолчанию? Спасибо.


Ответ

Если кодировка CP1251, то как-то так:
#include #include #include
// "костыль", чтобы наш codecvt имел публичный деструктор, // как того требует wstring_convert template struct deletable_facet : Facet { template deletable_facet(Args&& ...args) : Facet(std::forward(args)...) {} ~deletable_facet() {} };
// собственно определяем "конвертер" из узкой в широкую строку и обратно using convert_type = std::codecvt; std::wstring_convert, wchar_t> converter;
int main() { std::string s = "йцукен"; // string в wstring std::wstring ws = converter.from_bytes(str); // обратное преобразование std::string ss = converter.to_bytes(wstr); }
Для UTF-8 нужно только по другому определить convert_type и (опционально) converter
using convert_type = std::codecvt_utf8; std::wstring_convert converter;
То есть в случае, если вы используете предопределенный codecvt_*, можно опустить использование deletable_facet
Документация, на которую я ориентировался.

ОС для ПК с ОЗУ 8 мегабайт [закрыт]

Имеется компьютер начала 90-х, который планируется использовать в качестве "микро"контролера хотелось бы иметь возможность писать скрипты на bash и пользоваться сетью. ОЗУ всего 8 мегабайт, загрузка возможна с hdd.


Ответ

Компьютер x86-совместимый? Есть подборка микро-дистров, многие из которых умещаются в 8Mb оперативной памяти, В частности Fd Linux на базе Red Hat (8Mb RAM) или PiTux (4Mb RAM).
Fd Linux — довольно специфичный дистр: сеть есть, bash есть, но поддержки дисков нет. В PiTux, как известно, нет ни дисков, ни сети из коробки.

Размер пакетов от MS SQL не может быть больше 590 байт?

Здравствуйте! Не так давно обнаружил, что MS SQL сервер отправляет клиентам данные в пакетах размером не более 590 байт, создавая сильную фрагментацию, так как запросы обычно крупные, что отражено на скриншоте Wireshark:

Я пробовал менять опцию max packet size на 1500, но размер пакетов не изменился даже после перезагрузки сервера. Я попытался провести диагностику проблемы и вот что удалось выяснить:
Проблема не в MTU конечных компьютеров - везде значение стоит стандартное в 1500 байт. На пути между сервером и клиентами нет оборудования с MTU меньше 1500 байт. команда ping -l 1472 -f до любого клиента проходит успешно. TCP-соединения устанавливаются с размером окна в 1024 байта (8192 бита).
В связи с этим вопрос - по каким причинам параметр max packet size может не влиять на размер сетевых пакетов, отсылаемых сервером? Мои идеи насчёт неверной настройки оборудования или ОС иссякли. Возможно, проблема в самом MSSQL сервере? Кто-нибудь сталкивался с подобным?
На сервере используется Windows Server 2008 R2 Enterprise.
Клиенты - Windows 7.


Ответ

Я нашёл причину этой фрагментации - это отключенная функция PMTU Discovery в Windows. Если она отключена, протокол TCP будет отправлять пакеты на любые адреса с MTU 576. Цитирую Майкрософт
Если отключить возможность определения PMTU, протокол TCP будет отправлять пакеты с размером MTU 576 байт и со снятым флагом «не фрагментировать».
Проблема была решена изменением значения ключа EnablePMTUDiscovery с 0 на 1 в ветке реестра HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Tcpip\Parameters
После перезагрузки компьютера SQL и все другие приложения стали отсылать пакеты по 1514 байт, несмотря на значение MTU самого интерфейса 1500. Честно говоря, я не совсем понимаю причины подобного поведения PMTU Discovery, но проблему удалось обойти понижением MTU интерфейса до 1486.

Сокрытие виртуальной функции базового класса статической функцией в производном классе

Имеем код:
struct B { virtual void f() {} };
struct D : B { void f(int) { } };
int main() { }
clang выдаёт для него предупреждение:
'D::f' hides overloaded virtual function [-Woverloaded-virtual]
Теперь делаем f(int) статической в классе D
struct B { virtual void f() {} };
struct D : B { static void f(int) { } };
int main() { }
Код компилируется без предупреждений. Однако попытка использовать f() на объекте класса D
int main() { D d; d.f(); }
приводит к ошибке:
too few arguments to function call, expected 1, have 0
Т.е. static f(int) по-прежнему скрывает функцию из B. Проблема решается добавлением using B::f; в класс D и код компилируется успешно. Но возникает вопрос:
Есть ли какие-то объективные причины не выдавать предупреждение при сокрытии функции B::f() статической D::f(int)?


Ответ

Насколько я понимаю, это предупреждение нужно для того, чтобы предотвратить ошибки, связанные с сокрытием виртуальной функции, когда требовалось ее переопределение. Например при опечатке:
struct chart; struct Base{ virtual void* get(char* e); };
struct Derived: public Base{ void* get(chart* e); //Ой };
Ключевое слово override могло бы решить эту проблему, но до тех пор пока оно необязательное, эта диагностика будет жить и периодически срабатывать.
Что касается static. Думаю, разработчики компилятора рассуждали, что раз виртуальные функции статическими быть не могут, то скорее всего программист не планировал ничего переопределять. Соответственно и ошибки, скорее всего, нет.

Оператор присваивания в C++

Есть несколько моментов в описании оператора присваивания (assignment operator) в стандарте языка, которые мне не ясны, и хотелось бы их прояснить.
n4659, 8.18/1:
[...] In all cases, the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression. The right operand is sequenced before the left operand. With respect to an indeterminately-sequenced function call, the operation of a compound assignment is a single evaluation. [Note: Therefore, a function call shall not intervene between the lvalue-to-rvalue conversion and the side effect associated with any single compound assignment operator. —end note]
Вопрос 1. Что такое value computation и выполняется ли оно с точки зрения стандарта для левого и правого операндов в следующем коде:
a = 1;
Вопрос 2. Что имеется в виду, когда говорится о неопределённой упорядоченности вызова функции? Хотелось бы увидеть пример, с участием присваивания и вызова функции в одном выражении, где вызов функции неопределённо упорядочен, относительно чего-либо.
n4659, 8.18/8:
If the value being stored in an object is read via another object that overlaps in any way the storage of the first object, then the overlap shall be exact and the two objects shall have the same type, otherwise the behavior is undefined. [Note: This restriction applies to the relationship between the left and right sides of the assignment operation; it is not a statement about how the target of the assignment may be aliased in general. See 6.10. —end note]
Вопрос 3. Хотелось бы увидеть пример, который попадает под описание приведённой выше цитаты.

Дополнение к вопросу 1.
Наверное, больший смысл имеет поставить вопрос следующим образом: всегда ли с точки зрения стандарта языка выполняется value computation для операндов оператора присваивания? И если нет, то когда выполняется, а когда нет?
Дело то вот в чём. В первой цитате в этом вопросе говорится, что присваивание происходит после того, как произойдёт value computation обоих операндов. А ещё говорится, что правый операнд упорядочен перед левым (т.е. value computations + side effects, ассоциированные с правым операндом, произойдут до value computations + side effects ассоциированных с левым операндом).
Таким образом, если в операторе присваивания всегда происходит value computation левого операнда перед собственно присвоением, то правый операнд всегда упорядочен перед присвоением.


Ответ

«value computation» это процесс вычисления значения выражения, т.е. получение его результата. К примеру, пусть у нас есть int i = 0, тогда вычисленное выражение i++ вернёт 0. Тут важен тот факт, что i++ содержит побочные эффекты (side effects). Так вот, имея вычисленное вышеозначенное выражение, мы не гарантируем, что к нему в то же время были применены требуемые изменения (инкремент). Почему это может быть важно? Возьмём для этого стандарт до 17 года. В нём выражение: i = i++ имеет неопределённое поведение, потому что инкремент для i никак не упорядочен по отношению к присваиванию: он может произойти до или же после.
С другой стороны, если мы возьмём выражение ++i, то в нём побочные эффекты применяются до вычисления выражения, что делает такое выражение i = ++i рабочим и не содержащим неопределённого поведения.

Теперь ко второму вопросу. Давайте предположим у нас есть глобальная переменная: long long global = 0; и функция auto foo() {return global += 1;}. Для примера нам так же понадобится такая функция void ignore(long long, long long) и C++17 (исключительно ради гарантии неопределённой упорядоченности (indeterminately sequenced) вычисления аргументов функции).
Наконец, рассмотрим такой код: ignore(global += BIG_NUMBER, foo()), так вот, в теории, если убрать это правило, то могла бы получится следующая ситуация (на какой-нибудь платформе, где запись long long будет в несколько инструкций): мы начинаем записывать в наш global извне, записываем часть, но тут происходит переключение на код foo (так решил компилятор) и переписывает то, что мы записали. После чего мы продолжаем запись и в нашей переменной global лежит мусор. Т.е. это правило запрещает перемежение (interleaving, overlapping) вычисления выражений, и компилятор не имеет права сделать то, что я описал в предыдущем предложении.
Но это всё теория, потому что стандарт явно запрещает (C++14[intro.object]p13) перемежение всех выражений, за исключением неупорядоченных (unsequenced), поэтому подобные уточнения мне кажутся лишними.

Третья цитата запрещает такое:
int i = 0; char* ptr = reinterpret_cast(&i); // Вот тут проблема *ptr = static_cast(i);
Т.е. слева и справа не должно быть двух объектов, которые указывают на одну область памяти, но имеют разные типы.

По поводу дополнения: тут просто стоит привести цитату из C++17[intro.object]p15:
<...> An expression X is said to be sequenced before an expression Y if every value computation and every side effect associated with the expression X is sequenced before every value computation and every side effect associated with the expression Y <...>
И этого будет достаточно. Согласно правилам, введённым в C++17, правая часть выражения присваивания упорядочена до (sequenced before):
The right operand is sequenced before the left operand
Это является ключом к пониманию: прежде чем мы присвоим новое значение, или даже начнём вычислять то, чему мы там присваиваем, всё, что стоит справа должно отработать. Это правило делает код, i = i++ правильным, хотя до C++17 это было неопределённым поведением.

Каким образом процедурно расставлять объекты в 2D мире?

Имеется конечный 2D мир, состоящий из чанков, каждый из которых содержит в себе 16x16 тайлов. Размер мира - до 32к x 16к тайлов. Для генерации использую шум перлинга. Мир генерируется не весь сразу, а по мере передвижения игрока. Существуют различные биомы, у каждого из которых имеется некоторый набор тайлов. Например: почва тундры, песок пустыни и так далее.
Пример карты биомов:
И необходимо на определённых тайлах расставить объекты (upd: деревья, камни) с какой-то минимальной дистанцией между друг другом, да так, что бы в определённых участках, задаваемых специальной картой плотности (upd: влажность для деревьев, расстояние до гор для камней), эту дистанцию можно было менять. Для этих целей вполне себе может подойти алгоритм Poisson Disk, и я даже его реализовал:
Однако, есть проблема. Для работы алгоритма необходимо задать ширину и высоту рабочей области, а я не могу себе позволить генерировать poisson disk для всей карты, потому что она достаточно большая и на генерацию уйдёт непростительно много времени. Мне же необходимо генерировать маленькие кусочки карты, да так, чтобы при повторной генерации одного куска, результат не изменился, и между данным куском и остальными не было заметно швов. Можно ли модифицировать алгоритм Poisson Disk для решения этой задачи, или необходимо искать другой вариант? Если да, то какой?
UPD: Пример того, чего я хочу добиться
Здесь точки - это деревья. Как видите, в разных местах плотность деревьев различается. Именно в этом примере плотность зависит лишь от биома. Мне бы хотелось иметь возможность привнести некий элемент случайности, как в примере с Poisson Disk выше. Т. е. иметь в одном биоме поляны с малым кол-вом деревьев, густые леса и так далее.
Источник
В источнике алгоритм крайне неэффективный, и автор сам об этом говорит


Ответ

Я несколько месяцев назад реализовывал нечто подобное для одной своей игрушки. На скриншоте карты видны оранжевые точки - деревья:

Они расставлены с разной плотностью. Сам алгоритм имеет сложность, соизмеримую с количеством сгенерированных деревьев. Я не знаю, подойдет ли вам такой способ, или нет, так как деревья генерируются для каждого "чанка" отдельно (per-chunk).
Небольшое предисловие. Я в своем алгоритме различные структуры генерировал по-разному, но что касается ландшафта и деревьев - они генерировались для каждого чанка отдельно.
У каждого чанка был свой seed, по которому этот чанк генерировался. Сид я брал из шума перлина по координатам чанка, к примеру: seed = getPerlin(chunkX, chunkY). Дальше уже используя этот сид я генерировал все структуры на чанке.
А теперь к деревьям. Для начала я выбрал алгоритм, которым можно псевдорандомно расставить деревья. Выбор пал на halton sequence. Тут все просто:
getHalton(index, base) { var result = 0; var f = 1 / base; var i = index; while(i > 0) { result = result + f * (i % base); i = Math.floor(i / base); f = f / base; } return result; }
index - номер в последовательности, base - основание. Для того, чтобы расставить точки в двухмерном пространстве, я использовал основания 2 и 3. Как-то так:
var x = getHalton(index, 2) * chunkSize; var y = getHalton(index, 3) * chunkSize;
Здесь chunkSize - размер чанка (т.к. значения getHalton в интервале [0, 1]). Для того, чтобы получать в зависимости от сида различные вариации расстановки деревьев, я вводил переменную indexShift. Это просто целое число, сгенерированное по seed'у. И мы просто добавляем его к index'у.
Для наглядности введем некую функцию setTree(x, y), которая будет устанавливать деревья в той точке чанка, в которой нам нужно. Тогда генерация деревьев будет выглядеть так:
for (var i = 0; i < treesCount; ++i) { var x = getHalton(i + indexShift, 2) * chunkSize; var y = getHalton(i + indexShift, 3) * chunkSize; setTree(x, y); }
Хорошо. Теперь у нас есть псевдорандомно расставленные деревья. Но этого же нам не хватит, верно?
И тогда на помощь приходит все тот же шум перлина. Так как я использовал в своем проекте шум, который возвращал значения в диапазоне [-1, 1], а я хотел получить значения между [0, 1], то брал я значение вот так:
var value = getPerlin(x * noiseScale, y * noiseScale) * 0.5 + 0.5;
Переменная noiseScale использовалась для настройки масштаба шума. Хорошо, теперь у нас есть значение в диапазоне [0; 1]. Но как это нам поможет? А давайте модифицируем код генерации деревьев вот так!:
for (var i = 0; i < treesCount; ++i) { var x = getHalton(i + indexShift, 2) * chunkSize; var y = getHalton(i + indexShift, 3) * chunkSize; var densityInPoint = getPerlin(x * noiseScale, y * noiseScale) * 0.5 + 0.5; if (i < treesCount * densityInPoint) setTree(x, y); }
Таким образом, мы отсеиваем некоторую часть деревьев в тех местах, где шум перлина имеет низкие значения.
Итак, что мы имеем: некоторый алгоритм. Результат его работы зависит от трех переменных:
treesCount - чем больше, тем больше общая плотность деревьев. noiseScale - чем больше, тем быстрее меняется плотность на единицу расстояния (тем менее плотность однородна). densityInPoint - разные значения дают в целом разный результат.
P.S.: кстати, мне показалось, что линейное значение densityInPoint дает не очень хороший результат, так что я дополнительно возводил его в квадрат.
P.P.S.: на карте видна разная плотность деревьев в разных биомах. Для такого результата я просто задавал разные значения treesCount для каждого биома.
P.P.P.S.: если у вас деревья выставляются в матрицу, то значения x и y должны быть целыми. Тогда просто берите целую их часть:
setTree(Math.floor(x), Math.floor(y));
P.P.P.P.S.: я не утверждаю, что такой вариант решения самый лучший и самый правильный. Однако он гораздо быстрее метода, который вы нашли в статье, и для меня он давал приемлимые результаты.

Java и Kotlin в одном android проекте

Есть довольно большой Android проект, целиком написанный на Java. Сейчас мне очень интересен Kotlin, и я хотел бы продолжать проект на нём. Я знаю, что это возможно, что Kotlin, как Java при компиляции компилируется в байткод.
Однако, интересно, с какими "подводными камнями" можно при этом столкнуться. Например, увеличенное время компиляции, может быть повешение сложности поддержки проекта и т.д.


Ответ

Kotlin (Ко́тлин) — статически типизированный язык программирования, работающий поверх JVM и разрабатываемый компанией JetBrains. В мае 2017 года на конференции Google I/O команда Android объявила об официальной поддержке языка Kotlin для разработки Android-приложений.
Вы испытаете минимальный дискомфорт, а даже некое удовольствие переписывая проект, потому что ребята очень постарались и знали, что люди будут пробовать "переходить", и внедрили некоторые фишки в саму IDE.
В вашем случае это идеальный вариант, та компания на которой работает AndroidStudio, внедряет язык работающий поверх JVM, который может дергать или быть вызванным из самой Java и это видно до компиляции, из этого все и говорят о полной совместимости. У языков есть свои особенности, если вы их знаете у вас не будет проблем с интеграцией. Если вы все таки хотите использовать 2 языка, то система LLVM позволяет это сделать, от этого в теории увеличится время компиляции, других проблем не должно быть.
Конечно многое зависит от сложности проекта, как, сколько, когда вы добавляете внешние библиотеки, хотите ли вы оба языка использовать в 1 классе. Ну здесь скорей проблемы будут от ваших умений и знаний и на сколько вы хорошо знаете этот проект, чтоб не потерять зависимости или свойства инициализации и тд. Я переписывал небольшой проект (в продакшн) по требования заказчика, как раз почти сразу после анонса оф поддержки, были проблемы с инструментал тестированием под Espresso, с Dagger2, я плевался на Kotlin мол всё работало как так? В итоге обычная проблема multidex с параллельными ссылками, которые сейчас вроде как частично(полностью) решены изменением команд в расширениях скрипта Gradle, да,да именно те команды Impl и annotationProcessor и с языком это не было никак связано. Я доделал проект и он стал другим, не могу сказать что лучше или хуже просто другим. Я нейтрален к другим языкам, хоть постоянно использую их, мне нравится читаймость хорошо написанного куска Java в Android с DI, но мне и нравятся некоторые фишки Kotlin, но одно скажу точно, переводить проект полностью с 1 в другой это трата денег для заказчика. На данный момент, не вижу смысла, кроме как самообучения.

Есть ли защита от декомпиляции андроид-приложения?

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


Ответ

Первое и самое главное что необходимо понять, говоря о защите программ,это то, что 100% защиты не существует. Как говорится, "если захотят и бронированные двери не помогут".
Можно использовать сторонние решения в виде, например, различных обфускаторов. Можно написать свою защиту, как, например, это сделал один из авторов на хабре. Но в любом случае, ничто не даст гарантии, что код Вашего приложения рано или поздно не восстановят.

В чем смысл вложенных друг в друга инструкций synchronized?

Встретил вот такой пример (стр 422)
class Test { public static void main(String[] args) { Test t = new Test(); synchronized(t) { synchronized(t) { System.out.printIn("made it!"); } } } }
Далее, видимо, объяснение на мой вопрос, но для меня не вполне очевидное.
Вывод этой программы имеет вид
made it! Обратите внимание, что эта программа оказалась бы в состоянии взаимоблокировки, если бы один поток не мог многократно блокировать монитор.
Инструкция synchronized понятна, захватывает блокировку, выполняет тело инструкции, освобождает блокировку.
А в чем смысл вложенных друг в друга инструкций synchronized? Как происходит их (инструкций) взаимодействие между собой?


Ответ

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

Круговое раскрытия фиксированного бургер менью

Всем привет.
Была задачка сделать такой бургер менью.
У меня почти все готово но вот никак не получаетсья сделать круговое раскрытия этого менью. Как показано на ссылке.У меня весь менью скрываетсья и раскрываетсья только в хедере.И не круговой.Вот codepen ссылка.
Заранее прошу извинить если CSS кода слишком много больше сократить свой код я не смог потому что по другому не получилось бы показать суть проблемы.Если кто та может работать с SCSS то в codepen ссылке которой я указал наверху там весь SCSS можете там изменить и отправить ссылку суда.
const toggleAnimation = () => { $('#droplet').toggleClass('active'); $('#menu').toggleClass('active'); $('#burger').toggleClass('active'); } let below = false; $(window).scroll(() => { const Ypos = $(window).scrollTop(); if(Ypos > 50 && !below) { below = true; toggleAnimation(); } else if(Ypos < 50 && below) { below = false; toggleAnimation(); } }); $(".menu-trigger").click(function() { $(this).toggleClass('actived'); $("ul.menu li").slideToggle('fast'); }) body { margin: 0; padding: 0; height: 1000px; } .header { height: 47px; position: relative; width: 100%; z-index: 5000; font-family: "Poppins", Arial, Helvetica, sans-serif; font-weight: normal; letter-spacing: 3.4px; text-align: center; } @media only screen and (max-width: 992px) { .header { font-size: 14px; } } .header h1 { margin: 0; margin-left: 60px; } .header h1 a { float: left; line-height: 1.5; width: auto; max-height: 47px; color: #fff; } .header h1 a img { width: auto; height: 47px; } .header nav { width: 60%; margin: 0 auto; position: relative; z-index: 5000; } @media only screen and (max-width: 992px) { .header nav { width: 70%; } } .header nav .menu { position: relative; display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; text-decoration: none; -ms-flex-pack: distribute; justify-content: space-around; list-style-type: none; margin: 0; height: 47px; line-height: 3; } .header nav .menu li a { text-decoration: none; color: #fff; } .header nav .menu #artistbtn a .fas { left: 8%; } .header nav .menu #mediabtn { position: relative; } .header nav .menu #mediabtn a .fas { right: 37.5%; } .actived span { background-color: red !important; -webkit-transition: background-color 0.2s ease-in-out; -o-transition: background-color 0.2s ease-in-out; transition: background-color 0.2s ease-in-out; } .actived span:after { top: -1px !important; -webkit-transform: rotate(45deg); -ms-transform: rotate(45deg); transform: rotate(45deg); -webkit-transition: 0.5s ease-in-out; -o-transition: 0.5s ease-in-out; transition: 0.5s ease-in-out; } .actived span:before { top: -1px !important; -webkit-transform: rotate(-45deg); -ms-transform: rotate(-45deg); transform: rotate(-45deg); -webkit-transition: 0.5s ease-in-out; -o-transition: 0.5s ease-in-out; transition: 0.5s ease-in-out; } .menu-trigger { width: 40px; height: 40px; border-radius: 50%; background: #444444; position: fixed; /*Opacity start*/ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; filter: alpha(opacity=80); -moz-opacity: 0.80; -khtml-opacity: 0.8; opacity: 0.8; /*Opacity end*/ top: 3px; right: 30px; z-index: 5001; display: none; } @media only screen and (max-width: 768px) { .menu-trigger { display: block; } } .menu-trigger span { top: 45%; left: 25%; -webkit-transition: background-color 0.2s ease-in-out; -o-transition: background-color 0.2s ease-in-out; transition: background-color 0.2s ease-in-out; -webkit-transform: translate(-50%, -50%); -ms-transform: translate(-50%, -50%); transform: translate(-50%, -50%); } .menu-trigger span:after { top: 6px; -webkit-transition: 0.5s ease-in-out; -o-transition: 0.5s ease-in-out; transition: 0.5s ease-in-out; left: 0%; } .menu-trigger span:before { top: -6px; -webkit-transition: 0.5s ease-in-out; -o-transition: 0.5s ease-in-out; transition: 0.5s ease-in-out; left: 0%; } .menu-trigger span, .menu-trigger span:after, .menu-trigger span:before { width: 20px; height: 2px; background-color: #aaa; position: absolute; content: ''; } .header { background-color: #191919; /*Opacity start*/ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; filter: alpha(opacity=80); -moz-opacity: 0.80; -khtml-opacity: 0.8; opacity: 0.8; /*Opacity end*/ } .header nav .menu #artistbtn a .fas, .header nav .menu #mediabtn a .fas { position: absolute; top: 55%; color: #c9ac8c; line-height: 20px; } #burger { -webkit-transform: rotate(0deg); -ms-transform: rotate(0deg); transform: rotate(0deg); } #droplet.active { -webkit-animation: droplet 0.5s forwards; animation: droplet 0.5s forwards; } @keyframes droplet { 0% { top: 10px; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-left-radius: 0px; border-bottom-right-radius: 0px; -webkit-transform: rotate(0deg); -ms-transform: rotate(0deg); transform: rotate(0deg); } 25% { top: 10px; border-top-left-radius: 0px; border-top-right-radius: 40px; border-bottom-left-radius: 40px; border-bottom-right-radius: 30px; } 30% { -webkit-transform: rotate(45deg); -ms-transform: rotate(45deg); transform: rotate(45deg); } 60% { top: 34px; } 100% { top: 15px; border-top-left-radius: 37px; border-top-right-radius: 37.5px; border-bottom-left-radius: 37.5px; border-bottom-right-radius: 37.5px; -webkit-transform: rotate(45deg); -ms-transform: rotate(45deg); transform: rotate(45deg); } } .header.active { animation: burger 0.5s forwards; } @keyframes burger { 0% { -webkit-transform: rotate(0deg); -ms-transform: rotate(0deg); transform: rotate(0deg); opacity: 0; } 30% { -webkit-transform: rotate(-45deg); -ms-transform: rotate(-45deg); transform: rotate(-45deg); } 20% { opacity: 0; } 100% { -webkit-transform: rotate(-45deg); -ms-transform: rotate(-45deg); transform: rotate(-45deg); opacity: 1; } } #burger.active { -webkit-animation: burger 0.5s forwards; animation: burger 0.5s forwards; } @keyframes burger { 0% { transform: rotate(0deg); opacity: 0; } 30% { transform: rotate(-45deg); } 20% { opacity: 0; } 100% { transform: rotate(-45deg); opacity: 1; } } .header.active > nav { animation: menuCover 0.2s forwards; } .header.active { animation: menu 0.2s forwards; } @keyframes menu { 0% { opacity: 1; } 80% { opacity: 1; } 100% { opacity: 0; } } @keyframes menuCover { 0% { border-radius: 0px; top: 98px; height: 0px; } 75% { border-radius: 500px / 50px; top: 25px; } 100% { border-radius: 0px; top: 0px; height: 100px; opacity: 0; } }


Помогите очень прошу.


Ответ

Круговое раскрытие можно сделать через clip-path.
$('.menu-btn').click(function(){ $('.header__nav').toggleClass('header__nav_fixed'); }) *{ box-sizing:border-box; } body{ margin:0; background-color:#555; } .menu-btn{ position:relative; z-index:2; margin:0 0 0 auto; } header{ display:flex; align-items:center; justify-content:space-between; padding:10px 15px; background-color:#fff; } .header__nav{ display:flex; flex-wrap:wrap; align-items:baseline; list-style:none; margin:0; padding:0; } .header__nav li{ margin:0 10px 0 0; } .header__nav_fixed{ position:fixed; top:0; left:0; width:100%; height:100%; padding:30px; display:block; background-color:#fff; text-align:center; animation:circle 2s linear; animation-fill-mode:forwards; clip-path:circle(20px at calc(100% - 20px) 20px); } .header__nav_fixed li{ margin:0 0 10px; } @keyframes circle{ to{ clip-path:circle(1000vw at calc(100% - 20px) 20px); } }


Какой тип данных правильно использовать как логический в C?

Какой тип данных правильно использовать как логический в C?


Ответ

Если используется C99, то стоит задействовать его возможности, подключив заголовок stdbool.h. Например, так: #include #include
#define BOOL(b) b ? "true" : "false"
void main() { bool t = true, f = false;
printf("t = %s, f = %s
", BOOL(t), BOOL(f)); } Если C99 не используется, то предопределенного логического типа нет. Обычно используют макроопределения #define TRUE 1 #define FALSE 0 или перечисления typedef enum { FALSE = 0, TRUE = 1 } bool; при этом используемым типом данных может быть любой подходящий целочисленный тип.

Проблема с размещением сокета PostgreSQL в Unix

По умолчанию в Ubuntu сокет бд-сервера, очевидно, должен хранится в папке /var/run/postgresql, однако при каждой установке он у меня оказывается в /tmp. Поэтому при обращения большинства приложений (например Rails) к серверу появляется вот такая ошибка: could not connect to server: No such file or directory Is the server running locally and accepting connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"? как мне переместить сокет в нужную папку, или переназначить системные переменные чтобы искали его в другой папке?


Ответ

Проблема решена: в файле $PGDATA/postgresql.conf раскоментировать и инициализировать строку unix_socket_directory = '/var/run/postgresql/' + перезапуск сервера.

Android Studio Intelij при старте грузит проц

Здравствуйте, до этого пользовался Eclips + плагин Андроидовский . Сейчас решил поставить Android Studio от IntleliJ. Поставил. При запуске ноуту аж плохо стало, резкий скачок процессор на 100%. Это длится около минуты иногда больше, когда идет индексирование и так далее. Иногда потом еще во время работы скачки есть. ОС - Debian LXDE. Laptop - Fujitsu AH552 (core i7, 8gb RAM ). При запуске эклипса такого никогда не был. Что это так грузи и можно ли его как-то угомонить?


Ответ

Примерно такая же проблема была. Но у меня оперативку съедал всю. После эклипс крайне непривычно, вообще все по другому. Попробуйте создать проект под последнюю версию андроида, и пусть прогрузится полностью. Так же обновите среду. И приготовьтесь к тому, что грейдл будет по-долгу грузиться перед деплоем на девайс апкшника. У меня решилось тем, что после обновления эклипс я психанул ( проект перестал работать), и я выдержал все загрузки/подгрузки/обновления studio. Тем не менее, сейчас когда запускаю в начале дня среду, она грузится минуты 2 или если новый проект открываю. Кстати ноут у меня послабее вашего будет(i3,4Gb RAM). Желаю терпения

Алгоритм поиска Кнута Морриса

Нужно было сделать поиск фразы в книге и возвратить кол-во совпадений и время поиска. Все алгоритмы кроме Кнута получились. Алгоритм Кнута тоже получился,но преподаватель говорит, что он медленно работает. Вот функция: int knuth_morris(char *text, char *pattern, int pos) { int i, j; int result = -1; int *T = NULL;
if (pattern[0] == '\0') return 0;
/* Вычисление префикс-функции */ T = (int *) malloc((strlen(pattern)+1) * sizeof (int)); T[0] = -1; for (i=0; pattern[i] != '\0'; i++) { T[i+1] = T[i] + 1; while (T[i+1] > 0 && pattern[i] != pattern[T[i+1]-1]) T[i+1] = T[T[i+1]-1] + 1; }
/* Поиск */ j=0; for (i=pos; text[i] != '\0'; ) { if (j < 0 || text[i] == pattern[j]) { ++i, ++j; if (pattern[j] == '\0') { result = i-j; break; } } else j = T[j]; }
free(T); return result; } И использую её так: t1 = omp_get_wtime(); while(pos!=-1) { pos = knuth_morris(book_text, substr, pos+sz_substr); count_finds++; } t2 = omp_get_wtime(); printf("Knuth-Morris-Pratt Algoritm: %d matches. Time: %lf
", count_finds-1, t2-t1); Я пробовал также без цикла делать, считать совпадения внутри knuth_morris. Время почти не отличилось. Что не так с алгоритмом? Подскажите пожалуйста.


Ответ

В общем разобрался. Во-первых, в моём алгоритме вычисляется префикс функция лишние разы, поэтому нужно делать без цикла. Много скорости не прибавит, если маленький текст и большая поисковая строка. Но когда много исходного текста, алгоритм Кнута обходит наивный алгоритм во много раз. Итак, вот исправленная функция: int knuth_morris(char *text, char *pattern) { int *T = NULL; int i, j; int result = -1; int count_finds = 0;
if (pattern[0] == '\0') return 0;
/* Вычисление префикс-функции */ T = (int *) malloc((strlen(pattern)+1) * sizeof *T); T[0] = -1; for (i=0; pattern[i] != '\0'; i++) { T[i+1] = T[i] + 1; while (T[i+1] > 0 && pattern[i] != pattern[T[i+1]-1]) T[i+1] = T[T[i+1]-1] + 1; }
/* Поиск */ j=0; for (i=0; text[i] != '\0'; ) { if (j < 0 || text[i] == pattern[j]) { ++i, ++j; if (pattern[j] == '\0') { count_finds++; /*result = i-j; break; */ } } else j = T[j]; }
free(T); return count_finds; } И её использование: t1 = omp_get_wtime(); count_finds = knuth_morris(book_text, substr); t2 = omp_get_wtime(); printf("Knuth-Morris-Pratt Algoritm: %d matches. Time: %lf
", count_finds, t2-t1);

Установка последней версии Git на Ubuntu

Ubuntu 14.04. Устанавливаю Git:
sudo apt-get install git
git --version показывает, что установлена 1.9.0. При этом последний релиз уже 2.6.0 и я хочу установить его. Как это сделать?


Ответ

Для этого есть специальный персональный архив пакетов (PPA)
add-apt-repository ppa:git-core/ppa apt-get update
# установить, если ещё нет apt-get install git
# если уже установлен, то обновить apt-get install --only-upgrade git

Есть ListView. Он заполняется через адаптер. Нужно изменить ширину конкретного элемента

Есть ListView. Он заполняется через адаптер. При нажатие на каждый элемент ListView появляется новая активити где выбираем ширину и фон кнопки, на которую нажали. Я передаю данные через синглтон в адаптер . Не получается изменить ширину элемента ListVIew, кнопка никак не реагирует на setWidth. Фон же через setBackgroundColor успешно изменяется. Подскажите как быть?
list_item.xml



android:layout_alignParentTop="true" android:layout_toRightOf="@+id/HelpButton" android:layout_toEndOf="@+id/HelpButton" />

CustomAdapter.java
public class CustomAdapter extends ArrayAdapter { private int myColor, myWidth; private String myItem; private final Context context; private final String[] values;
public CustomAdapter(Context context, String[] values) { super(context, R.layout.list_item, values); this.context = context; this.values = values;
}
@Override public View getView(int position, View convertView, ViewGroup parent) { // return super.getView(position, convertView, parent); LayoutInflater inflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(R.layout.list_item, parent, false); TextView textView = (TextView) rowView.findViewById(R.id.ColorTextButton); ImageView imageView = (ImageView) rowView.findViewById(R.id.imageViewIcon); TextView textView2= (TextView) rowView.findViewById(R.id.HelpButton); textView.setText(values[position]); myColor=DataProvider.getInstance().getColor(); myWidth=DataProvider.getInstance().getWeight(); myItem=DataProvider.getInstance().getItem(); String s = values[position]; System.out.println(s); if (s.equals("Sunday")) { imageView.setImageResource(R.drawable.arrow); if (s.equals(myItem)){ textView.setWidth(MyWidth);
textView.setBackgroundColor(myColor);
} }
return rowView; } }
ButtonListActivity.java
public class ButtonListActivity extends ListActivity {
TextView textView; int Proportion, BackColor; String[] DayOfWeek = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
/** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // setContentView(R.layout.activity_main); // ListView myListView = (ListView)findViewById(R.id.listView); ListView listView = getListView(); listView.setTextFilterEnabled(true);
CustomAdapter forData= new CustomAdapter(this,DayOfWeek); setListAdapter(new CustomAdapter(this,DayOfWeek));

}

protected void onListItemClick(ListView l, View v, int position, long id) { String item = (String) getListAdapter().getItem(position); DataProvider.getInstance().putItem(item); Intent intent = new Intent(ButtonListActivity.this,SeekBarWork.class);
startActivity(intent);
// Toast.makeText(this, item + " selected", Toast.LENGTH_LONG).show(); }
}
SeekBarWork.java
public class SeekBarWork extends ActionBarActivity implements SeekBar.OnSeekBarChangeListener {
RelativeLayout myscreen; // SEEKBAR SeekBar rsb, gsb, bsb; // SEEKBAR int red, green, blue,backgroundColor,prop; //SEEKBAR Button gotoscreen1; TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_choise_color); gotoscreen1=(Button)findViewById(R.id.ColorDone); myscreen = (RelativeLayout) findViewById(R.id.mylayout); //SEEKBAR rsb = (SeekBar) findViewById(R.id.RedSeekBar); //SEEKBAR gsb = (SeekBar) findViewById(R.id.GreenSeekBar);//SEEKBAR bsb = (SeekBar) findViewById(R.id.BlueSeekBar); //SEEKBAR rsb.setOnSeekBarChangeListener(this); //SEEKBAR gsb.setOnSeekBarChangeListener(this); //SEEKBAR bsb.setOnSeekBarChangeListener(this); //SEEKBAR textView =(TextView)findViewById(R.id.textView1);
} public int updatebackground(){ //SEEKBAR int colorr; red = rsb.getProgress(); green = gsb.getProgress(); blue = bsb.getProgress(); colorr=0xff000000 + red * 0x10000 + green * 0x100 + blue; // myscreen.setBackgroundColor(colorr);
return colorr; } public int max(){ int maxNum;
if (red>=green) { if (red>=blue) maxNum = red; else maxNum=blue; } else if (green>=blue) maxNum=green; else maxNum=blue; if (maxNum==0) maxNum=1; return maxNum; } public int min(){ int minNum;
if (red<=green) { if (red<=blue) minNum = red; else minNum=blue; } else if (green<=blue) minNum=green; else minNum=blue; if (minNum==0) minNum=1; return minNum; } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { backgroundColor=(updatebackground());

// str=Integer.toString(s); // gotoscreen1.setText(str);
}
@Override public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override public void onStopTrackingTouch(SeekBar seekBar) {
}
public void onEnterButton(View view) { Intent intent = new Intent(SeekBarWork.this,ButtonListActivity.class); prop=(int)(((min()*100)/max())); DataProvider.getInstance().setData(prop,backgroundColor); startActivity(intent);
} }


Ответ

Попробуйте вместо
textView.setWidth(MyWidth);
использовать LayoutParams
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams)textView.getLayoutParams(); params.width = MyWidth; textView.setLayoutParams(params);

Как получить имя пользователя C#

Можно ли как-нибудь получить имя пользователя под которым был произведен вход в систему, если же приложение запускается под другим именем?
Т.е. входит в систему(логинится) пользователь ivanov, запускает мое приложение используя учетные данные(логин и пароль) пользователя petrov. Можно ли как-то узнать что в системе именно ivanov.
Environment.UserName
и
System.Security.Principal.WindowsIdentity.GetCurrent().Name
дают одинаковый результат - petrov.


Ответ

Вот пример, который получает логин пользователя который зашел в систему.
private static string _username;
static void Main(string[] args) { foreach (var p in Process.GetProcessesByName("explorer")) { _username = GetProcessOwner(p.Id); }
// remove the domain part from the username var usernameParts = _username.Split('\\');
_username = usernameParts[usernameParts.Length - 1];
Console.WriteLine(_username); Console.ReadLine(); }
public static string GetProcessOwner(int processId) { var query = "Select * From Win32_Process Where ProcessID = " + processId; ManagementObjectCollection processList;
using (var searcher = new ManagementObjectSearcher(query)) { processList = searcher.Get(); }
foreach (var mo in processList.OfType()) { object[] argList = { string.Empty, string.Empty }; var returnVal = Convert.ToInt32(mo.InvokeMethod("GetOwner", argList));
if (returnVal == 0) { // return DOMAIN\user return argList[1] + "\\" + argList[0]; } }
return "NO OWNER"; }

Циклическая прокрутка шкалы

Не могу разобраться, как можно реализовать циклическую прокрутку шкалы. Рисую ее в customview:
public void onDraw(Canvas canvas){ startingPoint = mainPoint; counter = 0; for (int i = 1;; i++) { if (startingPoint > screenSize) { break; }
if(i % 4 == 0) { size = scaleHeight / 4; counter = counter + 1; } else { if(i % 2 == 0) { size = scaleHeight / 8; } else { size = scaleHeight / 16; } } canvas.drawLine(startingPoint, endPoint - size, startingPoint, endPoint, rulerPaint);
if (i % 4 == 0) { String c = Integer.toString(counter); canvas.drawText(c, startingPoint, endPoint - (size + 20), textPaint); }
startingPoint = startingPoint + pxmm; }
Ограничиваю ее до одиннадцати длинных делений:
Но мне нужна бесконечная шкала:

Допустим, можно убрать проверку значения counter в onDraw методе, но как это скажется на производительности? Даже если ставлю его значение 1000, уже заметны "подлагивания" при скроллинге. Вот мой onScroll:
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { mainPoint = mainPoint - distanceX; invalidate(); return true; }
И onMeasure:
protected void onMeasure(int w, int h) { DisplayMetrics metrics = new DisplayMetrics(); ((Activity)getContext()).getWindowManager().getDefaultDisplay().getMetrics(metrics); h = metrics.heightPixels / 5; w = metrics.widthPixels;
setMeasuredDimension(w, h);
scaleHeight = h; scaleWidth = w; screenSize = w; endPoint = h; midScreenPoint = w / 2; pxmm = screenSize / 28; }
Каким образом реализуются подобные задачи?


Ответ

Надо рисовать шкалу от начала экрана до его конца. Т.е. цикл должен быть не по шкале, а по ширине экрана. А в цикле уже вести счетчик по шкале, как только он достигнет 11, обнулять.
Такой подход позволит легко рисовать и шкалу со смещением, просто присваиваете счетчику шкалы это смещение вначале и готово. Скроллинг будет не нужным.

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

По матрице смежности нужно построить матрицу достижимости. Использую Алгоритм Флойда — Уоршелла:
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) Где ошибка?


Ответ

Никакой ошибки нет.
Посмотрите внимательно на граф: Из узлов 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
Если вам для задачи этой информации не требуется, то после работы алгоритма можно пробежаться по диагональным элементам и проставить единички.

Разница в использовании git cmd и git bash под windows

Установил Git for Windows с сайта https://git-scm.com/ После установки есть возможность запускать две консоли - git cmd и git bash Я так понимаю, что git cmd - командная строка windows, а git bash - командная строка linux. Первые шаги при использовании не выявили существенных отличий в работе обоих. Заметил лишь, что в git bash есть удобное и достаточно приятное выделение цветом, а также подсказки для команд при двойном нажатии Tab. Наверняка есть более глобальные отличия между git cmd и git bash, которые заставят любителя cmd запускать bash. Вопрос: какие? Ибо тогда зачем в сборку для windows добавлять альтернативу cmd?


Ответ

Не бывает git cmd или git bash. Есть только сервисные утилиты, а точнее одна сервисная утилита с разными именами: git-cmd.exe и git-bash.exe
Оба эти exe-шника делают
Инициализация переменных окружения (PATH, и пр.) Запуск терминала.
Разница между ними только одна - по умолчанию git-bash.exe запускает терминал mintty с bash внутри. git-cmd.exe запускает стандартный терминал Windows с cmd.exe. Более того, git-cmd.exe имеет ключик --command=... с помощью которого можно запустить bash вместо cmd при желании.
git.exe это самостоятельная программа рядом с которой лежат все необходимые утилиты из пакета msys (например ls, vim, sed, и прочая), а недостающие утилиты можно "доставить" с помощью pacman. Предполагаемая проблема с "путями" не имеет оснований - все команды выполняет сам гит. git add **/*.cpp будет обрабатываться самим гитом.
Реальные различия:
Из командной строки cmd.exe несколько меняется синтаксис, т.к. ^ это управляющий символ cmd.exe. Например, вместо git.exe rebase -i 2385397^1 нужно писать git.exe rebase -i 2385397^^1 Маски файлов, вроде вышеописанного git add *.cpp не "разворачиваются" в список файлов, то есть аргументы передаются без изменений и git самостоятельно выполняет поиск подходящих файлов. В итоге мы имеем ошибочное поведение когда git add *.cpp добавляет файлы из подкаталогов. В консоли cmd.exe (если только она не в ConEmu запущена) нельзя использовать 256 цветов в Vim.
Вот наверное и всё.

“Прилипающие” заголовки ленты Instargam

Если прокручивать ленту инстаграма заголовки каждого item в списке будут как бы "прилипать" на место toolbar. Если Вы пользовались instagram, то поймете как это выглядит. Интересно как реализовать подобный функционал для списка? Может есть какие то библиотеки имеющие подобный функционал?


Ответ

Есть библиотеки, реализующие данный функционал:
Один https://github.com/timehop/sticky-headers-recyclerview
Два https://github.com/TonicArtos/StickyGridHeaders
Три https://github.com/emilsjolander/StickyListHeaders

Ограничение количества строк с IN

Есть SQL запрос:
SELECT * FROM technologies WHERE technology_category_id IN (1,2,3)
Можно ли одним запросом вывести по шесть строк для каждого id, которые внутри IN? LIMIT ограничивает общее количество результатов.


Ответ

Можно использовать партиционирование
select * from ( select *, row_number() over (partition by technology_category_id order by technology_category_id) num from technologies where technology_category_id in (1,2,3) ) tech where num <= 6
В order by задайте нужный вам порядок сортировки, если хотите выводить определенные шесть записей.

Принимать звонки с телефона на компьютер [закрыт]

Мне нужно подключить SIM-карту к компьютеру так, чтобы можно было принимать звонки и сообщения.
Что для этого нужно и чем пользоваться для написания принимающего звонки приложения на C#?


Ответ

Нужен телефон с полноценной поддержкой АТ команд или gsm (3g) модем. Подключаете и уже управляете с помощью команд. Почитайте про АТ команды

Когда можно нарушать правило о strict-aliasing?

Я знаю, что strict-aliasing - это запрет создания двух и более указателей на одну область памяти как одинаковых типов, так и различных. Действительно, такой код будет иметь неопределённое поведение. Но до недавнего времени (комментарий gbg) я думал, что union помогает использовать поля разных типов, находящихся на одинаковом смещении, без опасности неопределённого поведения. Но, как оказалось, Керниган и Ричи пишут:
Извлекать можно данные только того типа, какие были помещены в объединение при последнем обращении к переменной.
Тогда мне стало интересно, как в библиотечных функциях реализовано побайтное чтение и запись и последующее преобразование этих байт в базовые типы: int, char, float и др. Вот, например, фрагмент исходного кода Qt:
QDataStream &QDataStream::operator>>(float &f) { if (version() >= QDataStream::Qt_4_6 && floatingPointPrecision() == QDataStream::DoublePrecision) { double d; *this >> d; f = d; return *this; } f = 0.0f; CHECK_STREAM_PRECOND(*this) if (dev->read((char *)&f, 4) != 4) { f = 0.0f; setStatus(ReadPastEnd); } else { if (!noswap) { union { float val1; quint32 val2; } x; x.val2 = qbswap(*reinterpret_cast(&f)); f = x.val1; } } return *this; }
Здесь есть как преобразование указателя к другому типу с записью по данному адресу, так чтение из поля объединения, отличного от того, в которое производилась запись. Странно. Почему разработчикам Qt можно нарушать strict-aliasing, а мне нельзя?
UPD. Отдельно хотелось бы узнать, нарушает ли string-aliasing приведение типа void* к любому типу при вызове функции malloc
p.s. Хотелось бы услышать @gbg, @Abyx, так как на их ответы я ссылаюсь.


Ответ

Начать надо с того что вы цитируете Кернигана и Ричи из их книги про C из прошлого столетия, а приводите пример кода и спрашиваете про современные С и C++. Сегодняшние стандарты C99 и C11 разрешают использовать union так как это делают разработчики Qt
If the member used to access the contents of a union object is not the same as the member last used to store a value in the object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new type as described in 6.2.6 (a process sometimes called "type punning"). This might be a trap representation.
Неопределённое поведение возникает если вы делаете какие-то предположения об устройстве каких-то типов, которое не всегда может быть верным (яблоко и апельсин часто одного размера, но нельзя сказать что это верно вообще для всех яблок).
В данном случае неопределенного поведения не может быть потому что float и quint32 всегда и везде занимают 32 бита. Разработчики Qt заранее позаботились об этом.
Другой вопрос что по стандарту C++ компиляторы не так чтобы прямо обязаны делать всё то, что явно обязаны делать по стандарту C. Вопрос широкий.
Например, по мнению gcc так делать в принципе можно если обращаться к полям по значению. Если же вы надумаете обратиться к значению по ссылке на поле, то ждите беды.

алгоритм Тайного Санты (SSA - Secret Santa Algorithm)

Готовлю новогоднюю вечеринку с друзьями, решили сыграть в тайного санту. Но до нового года не встречаемся, поэтому жеребьевка удаленно. Захотелось поиграться с кодом, вышел вот такой:
Secret Santa Algorithm
Цель - рандомно раскидать кто кому дарит подарок, так, чтобы не вышло ситуации, что человек дарит самому себе)
Смущает наличие костыля
if (j + 1 == guests.get(j))
Как думаете, что можно улучшить? Ожидается лаконичный, простой, не зависающий на большом количестве участников, выполняющий свою задачу алгоритм.
import java.util.*;
class Rextester {
public static void main(String args[]) { int GUESTS_NUMBER = 10; List guests = new ArrayList<>(); for (int i = 0; i < GUESTS_NUMBER;) { guests.add(++i); } boolean shuffled = false; outer: while (!shuffled) { Collections.shuffle(guests); shuffled = true; for (int j = 0; j < guests.size(); j++) { if (j + 1 == guests.get(j)) { shuffled = false; continue outer; } } } for (int j = 0; j < guests.size(); j++) System.out.println(j + 1 + " gives a gift to -> " + guests.get(j)); } }


Ответ

А почему бы не сделать так?
Перенумеровать участников, пусть их n Сгенерировать коллекцию { 1, 2, ..., n } и перетасовать её (Collections.shuffle) Участник в первым номером в полученной коллекции делает подарок второму участнику в коллекции, второй — третьему, третий — четвёртому, ..., последний — первому.
Алгоритм генерирует просто цикл длины n. Особенность — никогда не будут сгенерированы несколько коротких циклов, всегда только один длинный.

Проверить подключение к интернету

Как при запуске Activity проверять подключение к интернету и если подключение отсутствует, то выводить сообщение?


Ответ

Актуальная проверка на сегодняшний день:
import android.annotation.TargetApi import android.arch.lifecycle.LiveData import android.content.BroadcastReceiver import android.content.Context import android.content.Context.CONNECTIVITY_SERVICE import android.content.Intent import android.net.ConnectivityManager import android.net.Network import android.net.NetworkInfo import android.net.NetworkRequest import android.os.Build import android.support.annotation.RequiresApi
class ConnectionLiveData(val context: Context) : LiveData() {
private var connectivityManager: ConnectivityManager = context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
override fun onActive() { super.onActive()
updateConnection()
when { Build.VERSION.SDK_INT >= Build.VERSION_CODES.N -> { connectivityManager.registerDefaultNetworkCallback(networkCallback) } Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP -> { lollipopNetworkAvailableRequest() } else -> { if(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP){ context.registerReceiver(networkReceiver, IntentFilter("android.net.conn.CONNECTIVITY_CHANGE")) //android.net.ConnectivityManager.CONNECTIVITY_ACTION } } } }
override fun onInactive() { super.onInactive() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { connectivityManager.unregisterNetworkCallback(networkCallback) } else { context.unregisterReceiver(networkReceiver) } }
@TargetApi(Build.VERSION_CODES.LOLLIPOP) private fun lollipopNetworkAvailableRequest(){ val builder = NetworkRequest.Builder() .addTransportType(android.net.NetworkCapabilities.TRANSPORT_CELLULAR) .addTransportType(android.net.NetworkCapabilities.TRANSPORT_WIFI)
connectivityManager.registerNetworkCallback(builder.build(), networkCallback) }
private val networkCallback = @RequiresApi(Build.VERSION_CODES.LOLLIPOP) object : ConnectivityManager.NetworkCallback() { override fun onAvailable(network: Network?) { postValue(true) }
override fun onLost(network: Network?) { postValue(false) } }
private val networkReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { updateConnection() } }
private fun updateConnection() { val activeNetwork: NetworkInfo? = connectivityManager.activeNetworkInfo postValue(activeNetwork?.isConnected) } }
Вызов:
val connectionLiveData = ConnectionLiveData(this) connectionLiveData.observe(this, Observer { isConnected -> isConnected?.let { //что-то } })

Deprecated
import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.net.ConnectivityManager; import android.net.NetworkInfo;
public class ConnectivityReceiver extends BroadcastReceiver {
public static ConnectivityReceiverListener connectivityReceiverListener;
public ConnectivityReceiver() { super(); }
@Override public void onReceive(Context context, Intent arg1) { NetworkInfo activeNetwork = ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo(); boolean isConnected = activeNetwork != null && activeNetwork.isConnectedOrConnecting(); if (connectivityReceiverListener != null) connectivityReceiverListener.onNetworkConnectionChanged(isConnected); }
public static boolean isConnected() { NetworkInfo activeNetwork = ((ConnectivityManager) MyApplication.getInstance().getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo(); return activeNetwork != null && activeNetwork.isConnectedOrConnecting(); }
public interface ConnectivityReceiverListener { void onNetworkConnectionChanged(boolean isConnected); } } 2.
import android.app.Application;
public class MyApplication extends Application {
private static MyApplication mInstance;
@Override public void onCreate() { super.onCreate(); mInstance = this; }
public static synchronized MyApplication getInstance() { return mInstance; }
public void setConnectivityListener(ConnectivityReceiver.ConnectivityReceiverListener listener) { ConnectivityReceiver.connectivityReceiverListener = listener; } } 3.


...


4.
private void checkConnection() { boolean isConnected = ConnectivityReceiver.isConnected(); showSnack(isConnected); }
private void showSnack(boolean isConnected) { String message; if (isConnected) message = "Good! Connected to Internet"; else message = "Sorry! Not connected to internet"; Snackbar snackbar = Snackbar.make(findViewById(R.id.fab), message, Snackbar.LENGTH_LONG).show(); }
@Override protected void onResume() { super.onResume(); MyApplication.getInstance().setConnectivityListener(this); }
@Override public void onNetworkConnectionChanged(boolean isConnected) { showSnack(isConnected); }

Верстка интерактивной карты

Каким инструментарием стоит воспользоваться для верстки интерактивной карты с текстовыми подписями?
Формальные требования к вариантам:
при наведении на определенный регион, он должен подсветиться.


Ответ

В топике Как реализовать отрисовку произвольных регионов фигуры? было подобное обсуждение. Там можно посмотреть с помощью каких инструментов можно нарисовать координаты и как их применить например на SVG.
Помимо этого, если не хочется связываться с SVG, то можно сделать это через html aтрибут coords, вычислив координаты как написано в теме по ссылке выше и применить какой-нибудь плагин для подсветки, например maphilight.js (https://github.com/kemayo/maphilight) или подобный.
Также для совсем простой карты можно даже js не использовать, хотя это будет сложновато вымерить:
body { color: #000; background: #fff; font-family: verdana, tahoma, sans-serif; } #map { border: 1px solid black; width: 401px; height: 328px; background: url(https://i.stack.imgur.com/XuHcv.gif) left bottom no-repeat; position: relative; } ul { list-style: none; margin: 0; padding: 0; } ul li { position: absolute; /* чтобы элементы li не "мешали" в IE6 */ } a:link, a:visited, a:hover { text-decoration: none; } a span { display: none; font: normal x-small/1.4em verdana, tahoma, sans-serif; width: 395px; color: #fff; background: #000; padding: 0 4px 2px 4px; } a:hover span { position: absolute; left: 0; top: 0; display: block; } #brest a { position: absolute; width: 176px; height: 133px; left: 7px; top: 185px; } #brest a:hover { background: url(https://i.stack.imgur.com/XuHcv.gif) -137px 0 no-repeat; display: block; } #brest a:hover span { margin-top: 144px; margin-left: -8px; } #vitebsk a { position: absolute; width: 205px; height: 129px; left: 129px; top: 0; } #vitebsk a:hover { background: url(https://i.stack.imgur.com/XuHcv.gif) -542px 0 no-repeat; display: block; } #vitebsk a:hover span { margin-top: 329px; margin-left: -130px; } #gomel a { position: absolute; width: 201px; height: 145px; left: 170px; top: 183px; } #gomel a:hover { background: url(https://i.stack.imgur.com/XuHcv.gif) -330px 0 no-repeat; display: block; } #gomel a:hover span { margin-top: 146px; margin-left: -171px; } #grodno a { position: absolute; width: 119px; height: 156px; left: 24px; top: 82px; } #grodno a:hover { background: url(https://i.stack.imgur.com/XuHcv.gif) 0 0 no-repeat; display: block; } #grodno a:hover span { margin-top: 247px; margin-left: -25px; } #minsk a { position: absolute; width: 156px; height: 185px; left: 116px; top: 84px; } #minsk a:hover { background: url(https://i.stack.imgur.com/XuHcv.gif) -987px 0 no-repeat; display: block; } #minsk a:hover span { margin-top: 245px; margin-left: -117px; } #mogilev a { position: absolute; width: 204px; height: 129px; left: 197px; top: 110px; } #mogilev a:hover { background: url(https://i.stack.imgur.com/XuHcv.gif) -762px 0 no-repeat; display: block; } #mogilev a:hover span { margin-top: 219px; margin-left: -198px; }


JavaScript: стоит ли избегать стиля написания кода “функция в функции”?

Допустим, у нас есть JavaScript приложение с большим количеством виджетов. Рассмотрим на примере шапки. Функция initHeader() собирает все нужные для дальнейшей работы DOM-объекты внутри шапки, добавляет обработчики событий, вообщем готовит к её использованию. Соответствует ли приведённый ниже шаблон кода "правилам хорошего тона" написания кода?
function initHeader(){
// объекты виджета, с которыми будем работать в функциях var var1 = $('#div1'); var var2 = $('#div2'); // ...
// вспомогательные функции
function function1(){ // ... }
function function2(){ // ... }
function function3(){ // ... } }
Особенность этого шаблона в том, что все вспомогательные функции находятся внутри основной.
Зачем такая структура? Согласно книге Роберта Мартина "Чистый код", в идеале у функции не должно быть аргументов вообще, а если аргументы неизбежны, то должен быть один, максимум два аргумента. Но в нашем случае, внутри вспомогательных функций может быть нужно большое количество переменных: jQuery-объекты, какие-то константы, рабочие переменные наподобие времени анимации, какие-то введённые данные и так далее. Если написать все вспомогательные функции вне основной, то тогда придётся всё это передавать в виде аргументов, и в два аргумента уложиться едва ли получится. Такая структура, которую я показал, избавляет нас от необходимости передавать аргументы, при этом все переменные, будучи объявленными внутри initHeader(), за пределами данной функции недоступны.
Второй плюс данного подхода в том, что он позволяет единыжды собрать все DOM-объекты через функцию jQeury(), а потом уже обращаться к ним через имена переменных (если это корректно называть кэшированием DOM-элементов, то можно сказать, что данный шаблон кода создаёт условия для обеспечения кэширования DOM-элементов).
И ещё: это следует из названия функции, но всё же уточню, что сама функция initHeader() вызывается только один раз (это можно сделать в HTML-коде сразу после закрывающего , например), а внутренние функции уже могут быть вызваны сколько угодно раз.
Приведите, пожалуйста, аргументы за и/или против такого подхода.


Ответ

В Javascript этот стиль используется для ограничения области видимости вложенных таким образом функций, с целью предоставлять только необходимые внешние интерфейсы (с аналогичной целью, как приватные методы в C++). См. например, паттерн модуль, фасад. Таким образом, ничего плохого тут не сделано, только в исходном примере паттерн недоразвит.
Замечу, что нет необходимости добавлять собранные скрипты в header, лучше это сделать в конце body, чтобы на момент их загрузки все элементы разметки уже были обработаны.
А условие загрузки определённой части кода (модуля) должно быть независимо от факта старта скрипта, управляться специальной сущностью - например инициализацией виджета или роутингом.
Что касается упомянутой в вопросе рекомендации использовать один или менее аргументов у функции - надо понимать, что в контексте Javascript это просто забавно звучит и, вероятно, должно пониматься как-то иначе. Всё-таки, это мультипарадигменный, в т.ч. функциональный, язык программирования. Т.е. да, конечно, вместо множества аргументов лучше использовать каррированные функции и их композиции. Далее, с озвученной в вопросе целью, передаётся один аргумент - объект, что, вместе с сахаром ES6-деструктуризации и той или иной системой проверки типов, даёт самые гибкие средства для передачи групп сущностей в/из функции.