Страницы

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

пятница, 29 ноября 2019 г.

Generic или не Generic

#c_sharp #.net #clr


В обсуждении моего ответа на вопрос возник один спорный момент.

Допустим имеем обобщенный класс и три метода в нем:

class SomeClass
{
    //не обобщенный метод
    public void DoSomething(int x) 
    { 
        Console.WriteLine("True non generic method"); 
    }
    //не обобщенный метод с параметром обобщенного типа класса
    public void DoSomething(T x)
    { 
        Console.WriteLine("Indirectly generic method"); 
    }
    //явно  обобщенный метод
    public void DoSomething(U x)
    { 
        Console.WriteLine("True generic method"); 
    }
}


Собственно вопрос - является ли не обобщенный метод, с параметром обобщенного типа
класса, обобщенным методом?
    


Ответы

Ответ 1



Всё же, как вы догадались, ответ необобщённый (non-generic). Потому что есть определение, что такое обобщённый метод и всё. Метод считается обобщённым, когда у него есть свой параметр типа. Если у него его нет, то это необобщённый метод. У обобщённого типа метод может заимствовать параметр типа для возвращаемого значения и/или для типа параметра (не путайте тип параметра с параметром типа, хотя этот каламбур, наверняка, наводит путаницу), но это не сделает его обобщённым, если у метода нету своего параметра типа. И ваши исследования генерируемого IL совершенно не противоречат мои доводам. Но я бы всё равно не смешивал терминологию языка программирования и детали его реализации. Я не считаю это корректным. Подкину тему для размышлений: иногда невиртуальные методы вызываются как виртуальные. Просто генерируется инструкция callvirt в некоторых случаях для невиртуальных методов и всё. Можно было бы также спросить "Может их следует называть неявно виртуальные?" (Но тут скорей очевидно, что это просто делать реализации). Смущение для разработчиков Но для начинающих (и не только) разработчиков — это, по крайней мере поначалу, странно. К примеру, все привычные члены Dictionary необобщённые: Add(TKey, TValue), ContainsKey(TKey), TryGetValue(TKey, TValue), Remove(TKey) и т.д.. И это многих удивляет, даже иногда тех, кто пользовался годами этим словарём. Тип обобщённый, но методы — нет. У List дела обстоят интересно: у него только один обобщённый метод ConvertAll(Converter), остальные — нет.

Ответ 2



В MSDN такие методы называются не обобщенными методами обобщенного класса, что ни чем не лучше моей формулировки в вопросе. В спецификации такой вариант тоже не рассматривается отдельно. Ок, раз ни где ни чего конкретно не написано спросим у самой CLR, в конце концов именно ей исполнять все что мы понаписали. Итак, начнем с объявления класса .class public auto ansi beforefieldinit ConsoleApplication.SomeClass`1 Интересный момент: `1 - цифры после апострофа означают число параметров типа, но главная задача этой прибавки - расширение имени класса. Таким образом у нас может быть два класса с одинаковым именем и разным количеством обобщающих параметров включая не обобщенный класс без параметров. Объявления методов (тело опускаю, т.к. не важно в данном случае): Не обобщенный метод .method public hidebysig instance void DoSomething ( int32 x //тип указан явно ) cil managed Явно обобщенный метод .method public hidebysig instance void DoSomething ( !!U x //обратите внимание на два восклицательных знака ) cil managed Два восклицательных знака сообщают JITу что конкретный тип нужно искать в обобщающих параметрах метода Не обобщенный метод с параметром обобщенного типа класса .method public hidebysig instance void DoSomething ( !T x //тут только один восклицательный знак ) cil managed Один восклицательный знак сообщает JITу что конкретный тип нужно искать в обобщающих параметрах класса. Ок, уже что-то, теперь взглянем на то, как эти методы вызываются: Не обобщенный метод call instance void class ConsoleApplication.SomeClass`1::DoSomething(int32) тип параметра указывается явно Явно обобщенный метод call instance void class ConsoleApplication.SomeClass`1::DoSomething(!!0) тип параметра указывается ссылкой на параметр с индексом 0 в списке обобщающих параметров метода Не обобщенный метод с параметром обобщенного типа класса call instance void class ConsoleApplication.SomeClass`1::DoSomething(!0) тип параметра указывается ссылкой на параметр с индексом 0 в списке обобщающих параметров класса Получается, что в текущей реализации мы имеем два вида обобщенных методов - явные и неявные. В обоих случаях JITу потребуются дополнительные действия при компиляции данных методов для разрешения типов, т.к. в обоих случаях при вызове указана только ссылка на элемент списка обобщающих параметров. Итого для конкретной реализации компилятора/JIT/VM: Есть два типа методов - обобщенные и не обобщенные. Обобщенные методы могут быть обобщенными явно и неявно. Приоритет при выборе перегруженного метода при прочих равных условиях: не обобщенный неявно обобщенный явно обобщенный В спецификации эту информацию найти можно, но только косвенно, прямых упоминаний нет, видимо понадеялись на логику читающих. PS: Если есть другое объяснение, с удовольствием его прочитаю в вашем ответе.

Указатели и массивы в C++

#c++


Прошу объяснить, в чем разница в следующих листингах:
1)
#include 
using namespace std;

int main() {
    int n;
    cin >> n;
    int a[n];
    for (int i=0; i> a[i];
    cout << endl;
    for (int i=0; i
using namespace std;

int main() {
    int n;
    cin >> n;
    int * a = new int[n];
    for (int i=0; i> a[i];
    cout << endl;
    for (int i=0; i


Ответы

Ответ 1



Ни один из стандартов C++, даже C++11, не включает в себя variable length arrays, просто некоторые компиляторы поддерживают их в качестве расширений. Такая же история с типом long long, правда его все-таки включили в C++11. Чтобы заставить g++ ругаться на такие нестандартные расширения нужно добавить опцию -pedantic: $ cat test.cpp #include int main() { long long int a; std::cin >> a; int b[a]; } $ g++ test.cpp -pedantic test.cpp: In function ‘int main()’: test.cpp:4:5: warning: ISO C++ 1998 does not support ‘long long’ [-Wlong-long] test.cpp:6:12: warning: ISO C++ forbids variable length array ‘b’ [-Wvla] $ g++ test.cpp --std=c++0x -pedantic test.cpp: In function ‘int main()’: test.cpp:6:12: warning: ISO C++ forbids variable length array ‘b’ [-Wvla]

Ответ 2



В первом случае массив размещается в стеке, а во втором в куче (динамической памяти). Основное отличие в том, что в стеке ограничен размер массива. Так например при n = 100000 программа работает, а при n = 1000000 уже нет (валится). Для кучи возможный размер значительно больше. 250000000 работает, а 500000000 - уже нет. Собственно у меня Windows 7 (64-bit), но MinGW 32-разрядный gcc (GCC) 3.4.5 (mingw-vista special r3)

Ответ 3



Под рукой черновик стандарта ISO/IEC JTC1 SC22 WG21 N3092, там указано: 8.3.4 Arrays [dcl.array] In a declaration T D where D has the form D1 [ constant-expression_opt ] attribute-specifier_opt (...) If the constant-expression (5.19) is present, it shall be an integral constant expression and its value shall be greater than zero. Откуда следует, что это поведение не соответствует стандарту. Возможно, в более поздних версиях стандарта это уже не так. UPD: Проверил по последней версии черновика стандарта, аналогично.

Ответ 4



А действительно - почему? Значит, в современных версиях компиляторов происходит динамическое создание массивов, объявленных как a[x]. Надо копаться в современных спецификациях. Хороший вопрос.

Как по фэн-шую получить путь к AppData для пользователя отличного от текущего?

#c_sharp #windows #.net


Существует замечательный метод достать путь к папке AppData текущего пользователя:
string applicationDataPath = Environment.GetFolderPath(
    Environment.SpecialFolder.ApplicationData);

Метод хорош тем, что не зависит от реального расположения папки пользователя, версии
ОС и тому подобного.
Собственно, хотелось бы увидеть способ достать путь к AppData не только для текущего
пользователя, но для любого, если у меня есть строка логина этого пользователя.    


Ответы

Ответ 1



Задача оказалась сложнее, чем предполагалось :-) Для начала, вам нужно воспользоваться P/Invoke, встроенного в .NET пути нет. Например, вы можете воспользоваться функцией SHGetKnownFolderPath с подходящим KNOWNFOLDERID. Подключается она вот так. Вам понадобится AccessToken, который можно получить при помощи функции LogonUser как описано в примере тут: public class FolderDemo { [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken); [DllImport("kernel32.dll", CharSet = CharSet.Auto)] public extern static bool CloseHandle(IntPtr handle); [DllImport("shell32.dll")] public static extern int SHGetKnownFolderPath( [MarshalAs(UnmanagedType.LPStruct)] Guid rfid, uint dwFlags, IntPtr hToken, out IntPtr pszPath); // If you incorporate this code into a DLL, be sure to demand FullTrust. [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")] public static void Main(string[] args) { // Get the user token for the specified user, domain, and password // using the unmanaged LogonUser method. // The local machine name can be used for the domain name to // impersonate a user on this machine. string domainName = ...; string userName = ...; string userPassword = ...; const int LOGON32_PROVIDER_DEFAULT = 0; //This parameter causes LogonUser to create a primary token. const int LOGON32_LOGON_INTERACTIVE = 2; IntPtr handle; // Call LogonUser to obtain a handle to an access token. bool returnValue = LogonUser(userName, domainName, userPassword, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, out handle); if (!returnValue) return; try { // судя по всему, можно объявить SHGetKnownFolderPath // поинтеллектуальнее, чтобы не понадобилось маршалирование вручную IntPtr pPath; if (SHGetKnownFolderPath(KnownFolder.LocalAppData, 0, handle, out pPath) == 0) { string path = Marshal.PtrToStringUni(pPath); Marshal.FreeCoTaskMem(pPath); // тут строку можно использовать } } finally { // обязательно! а то останемся с правами другого юзера CloseHandle(handle); } } } Класс KnownFolder смотрите тут, интересующая вас часть такая: public static class KnownFolder { public static readonly Guid LocalAppData = new Guid("F1B32785-6FBA-4FCF-9D55-7B8E7F157091"); public static readonly Guid LocalAppDataLow = new Guid("A520A1A4-1780-4FF6-BD18-167343C5AF16"); public static readonly Guid RoamingAppData = new Guid("3EB685DB-65F9-4CF6-A03A-E3EF65729F3D"); // ... } Кажется, можно переопределить SHGetKnownFolderPath так, чтобы не вызывать вручную FreeCoTaskMem: [DllImport("shell32.dll")] public static extern int SHGetKnownFolderPath( [MarshalAs(UnmanagedType.LPStruct)] Guid rfid, uint dwFlags, IntPtr hToken, out String path); ... string path; if (SHGetKnownFolderPath(KnownFolder.LocalAppData, 0, handle, out path) == 0) { // тут строку можно использовать } Но я не уверен на 100%, interop всегда полон неожиданностей.

Debian \ Android: Трансляция веб камеры

#android #linux #debian


Здравствуйте, есть мини компьютер cubieboard 1 и цель: транслировать с него веб камеру
(которая подключена к нему через USB).  

Варианты моих решений:  


Ставим Debian, устанавливаем ffmpeg и транслируем  
Ставим Android, устанавливаем любое приложения для трансляции (я использовал IP Webcam)  


Я думал что первый вариант будет лучше, так как:


ОС без графической оболочки - меньше кушает
Нету всяких приложений которые бы тоже кушали ресурсы


А оказалось всё на оборот, на android трансляция работала почти идеально (задержка
около 0.2с.) с хорошим разрешением.  

А на Debian при разрешении 320X240 были ужасные падения FPS + задержка около 2с и
ЦП загружен на 100%. А при разрешении 640X480 она вообще не работала. 

Возможно чего-то я недопонимаю, но как так возможно?
    


Ответы

Ответ 1



Аппаратный кодек - ответ на ваш вопрос. Возьмите камеру с аппаратным mjpeg и mjpeg-streamer. Роутер с 200МГц процессором вещал камеру Логитеч и был загружен при этом на 7-10%. В тоже время в ARM проццесорах есть свой кодек, которым пользуется Андроид. Дебиан не может задействовать эти возможности по нескольким причинам: лицензионная чистота, отсутствие программиста который напишет этот модуль и упакует для Убунты (потому в репозитории Дебиана врятли возьмут).

Ответ 2



Задержки, они же лаги, могут происходить по многим причинам. В Вашем случае это проблеме с перекодированием. Устройство слабенькое, в риалтайме сложно управится. ffmpeg может немножко кешировать (ну как минимум на один кадр будет отставать - картинку нужно в буфер поместить, потом отправить) собственно сама сеть. Тут без коментариев. проигрыватель. Плееры также любят кешировать, потому что данные в сети не идут часто равномерным потоком и небольшая пауза вносит задержку. Потом либо нужно ускорять воспроизведение, либо придерживать задержку (которая будет накапливаться). Небольшой буфер решает проблему. Теперь перейдем к командам. ffmpeg -f v4l2 -r 25 -s 640x480 -i /dev/video0 -fflags nobuffer -c:v copy -f sdl - Тут нужно немножко уточнить. -f v4l2 -r 25 -s 640x480 -i /dev/video0 - тут все просто, выбрали формат, источник и кол-во кадров в секунду. Это выбирается по личным предпочтениям и возможностям камеры. -fflags nobuffer - говорит ffmpeg не буферизировать. Может помочь немножко убрать задержку. -c:v copy - это важная строка. Она говорит "не пытайся перекодировать видео. Просто копируй. -f sdl - - это не будет работать, если нет gui. Эта опция говорит "выводи на экран". Она хороша для тестов, но не для реальной работы. Так как хочется минимум задержек, можно попробовать использовать udp (тогда нужна где то такая конструкция -f mpegts udp://127.0.0.1:8080) или другие удобные протоколы.

Ответ 3



Попробуйте mjeg_streamer вместо ffmpeg. На нем проще подобрать подходящий fps и разрешение экрана. Ну и камера dns 1302w - не самый лучший выбор для таких задач. Вполне возможно, что в android где-то чтение с дешевых китайских видеокамер реализовано лучше Не помешало бы сравнить версии ядер.

Событие изменения содержимого тега

#javascript #html #dom #события


Есть ли событие в JavaScript, возникающее при изменении содержимого тега? Например,
при изменении содержимого 


Ответы

Ответ 1



Прослушать можно любой тег. Для прослушки изменения в поддереве нужно использовать DOMSubtreeModified. В примере ниже есть 2 параграфа

- demo1 и demo2. На document установлено событие клика - такое, что при клике на любой участок страницы, содержимое demo1 меняется на demo1 -> Hello World!. Другой слушатель ждёт изменение в поддереве demo1, и когда событие наступает, содержимое demo2 меняется на demo1 Changed! document.getElementById("demo1").addEventListener("DOMSubtreeModified", function() { document.getElementById("demo2").innerHTML = "demo1 Changed!" }); document.addEventListener("click", function() { document.getElementById("demo1").innerHTML = "demo1 -> Hello World!"; });

demo1



Ответ 2



Введение Была попытка сделать события на изменения элементов в DOM, да всплыла, уж очень оно сильно влияло на производительность. Называются они в литературе Mutation events: DOMAttrModified - если изменился атрибут у элемента DOM; DOMAttributeNameChanged - если изменилось имя атрибута у элемента DOM; DOMCharacterDataModified - если изменился какой-либо текст, будь то обычный 'text node' или простой комментарий у элемента DOM; DOMElementNameChanged - если изменилось имя у элемента DOM; DOMNodeInserted - если у элемента DOM произошла вставка нового элемента; DOMNodeInsertedIntoDocument - если произошла вставка нового элемента в document; DOMNodeRemoved - если у элемента DOM произошло удаление какого-либо элемента; DOMNodeRemovedFromDocument - если произошло удаление какого-либо элемента в document; DOMSubtreeModified - если произошло какое-либо изменение в document. Все они 'deprecated' и крайне не рекомендуются к использованию. Тем более, далеко не факт, что они будут работать в Вашем браузере, а проверить наличие этого события не такая уж тривиальная задача, так как атрибутов у элементов с одноименным названием нет. Альтернатива 0, для внимательных Как альтернатива примем тот факт, что всегда можно создавать свои события, всплывающие при изменениях. То есть можно сделать вот так: придумываем название события, например, DOM:changed; создаем функции-обработчики и подписываем их на эти события; находим все функции, которые могут потенциально изменить DOM; при любых, интересующих Вас, изменениях в DOM в этих функциях вызываем пользовательское событие. У этого метода потрясающие возможности в управлении уведомлениями, всегда можно выбрать, что вызывает событие, аккуратно их фильтровать и создавать удобную атмосферу для их вызова. Но всегда есть возможность забыть добавить это в код, надо быть внимательным и тщательно всё затестировать. Альтернатива 1, простая Можно придумать ещё два способа это сделать. Первый, в лоб, не совсем корректный, но имеет место жить на небольших данных, далее я расскажу о правильном: берём эталонный .innerHTML у Вашего div'а и сохраняем его куда угодно, например, в sessionStorage каждые n секунд сравниваем текущий .innerHTML с эталонным, если не равны, значит произошли какие-либо изменения и вызываем callback. Можно красиво обернуть в промис. Можно даже вместо вызова callback вызывать свое пользовательское событие. Способ хорош, быстр, если div не многокилометровый. Самое главное - весьма и весьма кроссбраузерный, заработает даже в ie7. Но вот если div будет большим, то это может вызывать тормоза. Альтернатива 2, то, что надо Второй способ правильный. После неудачной попытки со специальными событиями, придумали, оно действительно работает в хороших браузерах, MutationObserver(), а если нет, то его полифилл. Как его использовать? А вот так. Сначала создадим объект MutationObserver: var observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { console.dir(mutation); //объект с изменениями }); }); В данном случае mutations - массив MutationRecord, каждый элемент которого содержит следующие поля: type - все измененные атрибуты или characterData (текстовый элемент или комментарий) или childList в зависимости от типа мутации; target - элемент дерева, в котором произошло изменение; addedNodes - NodeList, содержащий добавленные элементы дерева, логично, что он будет пуст (*.length == 0), если таких не будет. removedNodes - NodeList, содержащий удаленные элементы дерева, он тоже будет пуст, если таких иметься не будет; previousSibling - предыдущий сосед по дереву добавленного или удаленного элемента или null; nextSibling - следующий сосед по дереву добавленного или удаленного элемента или null; attributeName - имя измененного атрибута или null; oldValue - старые значения, до изменения, но в случае, если изменение произошло в childList - null. Затем, чтобы эта прелесть заработала, скажем интерпретатору "начать слежение": observer.observe( elem, { childList: true, attributes: true, subtree: true, characterData: true, attributeOldValue: true, characterDataOldValue: true, attributeFilter: true } ); Первым аргументом мы передаем объект DOM, за которым нужно следить, вторым - то, как мы будем следить: childList - следим за вставкой/удалением элементов в childList; attributes - следим за изменением атрибутов characterData - следим за изменением characterData (текстовые элементы или комментарии) attributeOldValue - запоминаем значение атрибута до изменения characterDataOldValue - запоминаем значение characterData до изменения attributeFilter - Array тех атрибутов, за которыми будем следить Для того, чтобы остановить слежение можно вызвать метод observer.disconnect();. Предостережение Не советую изменять элемент DOM, за которым мы следим, может получиться весьма пикантная ситуация. Забытая на потом последняя альтернатива Стоит упомянуть, что существует стандартное событие, полностью кроссбраузерное и работающее - onchange. Работает оно в элементах ,