Добрый день. По большей части прошли мимо, но аргументы, чтобы пройти мимо, у нас есть. Сейчас коротко перечислю.
Во-первых, всё упирается в объём работы. По сути, нужно портировать (не важно, единовременно или в автоматическом режиме) десятки миллионов строк наших проектов на Haxe для того, чтобы им можно было пользоваться. Вряд ли это проще, чем в случае с прямым портом на C++, но во втором случае мы хотя бы имеем на выходе готовый продукт и полный контроль над тем, как он выглядит.
Во-вторых, наш портируемый код написан не в абстрактном окружении, а вполне конкретном — дотнете. Если где-либо в API торчит System::Collections::Generic::IEnumerable<System::String> — то это значит System::Collections::Generic::IEnumerable<System::String>, а не QList, не std::list<std::string>::iterator, не const char**, и не что-либо ещё из плюсовых концепций. А имнно: этот IEnumerable будет использоваться внутри foreach, в LINQ-выражениях, он будет сохраняться в переменные и возвращаться из функций, конвертироваться в object и извлекаться обратно, проверяться на приводимость к другим интерфейсам и типам, на равенство null, и так далее. То есть, если платформа, на которую мы перетаскиваем свой код, не будет соответствовать какому-либо из этих поведений, код придётся править, и весьма существенно. Аналогично, String должен следовать поведению дотнетовского String, иначе будут проблемы. В этом смысле оторваться от дотнета далеко не так просто, как кажется, особенно при поддержании развивающихся проектов, которые продолжают ежемесячно дорабатываться программистами, работающими в парадигме C#.
Сразу на C++ транслировать, наверное, не собрались бы, но перед глазами был успешный пример с Java, а на поддержку Java сподвигнуться было куда проще. Плюс, у руководства был интерес к рынку решений для C++, на котором библиотек нашего уровня ещё меньше, чем на .Net и Java.
Большое спасибо за отсылку к «правильной» микросхеме. В следующий раз обязательно воспользуюсь. Про способ разводки читаю — совершенно новая для меня информация.
Не до конца понял чем это лучше чем переключение обычными малогабаритными on-off-on тумблерами.
Функционально — примерно то же самое, сам думал о таком варианте. С другой стороны, блок тумблеров (а тут нужно минимум три контактных группы на каждый канал — два провода данных и питание) будет заведомо больше, чем мой пульт. Плюс, нужно будет вручную выдерживать паузу между отключением от одного источника и подключением к другому (иначе при быстром щелчке переключение может не произойти). Ну, и не будет автоматики — у меня контроллер понимает, что, если подключен только телефон, нужно отдать ему звуковую карту, а остальные устройства вырубить.
Чтобы уменьшить потери, можно использовать диоды Шоттки или транзисторы, но электромагнитные реле обычно справляются с задачей надёжнее.
Спасибо за информацию.
Обмотку реле нужно не забывать шунтировать диодом в обратном направлении — иначе то что им управляет может выйти из строя в момент отключения из-за энергии накопленной в магнитном поле обмотки.
Не знал, спасибо. Если возникнут проблемы — добавлю в следующей версии платы.
Чтобы быстрее сбросить устройство, можно попытаться в отключённом состоянии замкнуть контакты питания и землю
Да, можно будет попробовать в будущем. Не пришло в голову — пытался эмулировать то, что происходит при переключении разъёмов.
Многие мониторы с поддержкой нескольких входов, даже старые, поддерживают управление ими.
Не знал, спасибо. Надо будет про это почитать. В моём случае это не требуется (бывает, что главный монитор отдан телефону, а на боковых в это время что-то делаю на компьютере мышью), но приятно иметь возможности.
При нехватке выводов рекомендуется использовать I2C расширители вроде PCF8574.
Ещё раз спасибо.
В этом случае рекомендуется уменьшинть номинал резисторов
Записал. Пока ничего такого не замечал, но мало ли.
у самой ардуины мощности на питание такого количества реле не хватит
У меня получилось четыре реле, подключенных к ножкам контроллера, и два светодиода. До 200 мА всё вместе не дотягивает.
Честно говоря, я не совсем понимаю, где по правилам Хабра проходит граница между простым упоминанием бренда и рекламой. Корейцы к делу напрямую не относятся, вот и вынес за скобки.
А главное — не очень понятно, кто и по какому принципу должен решать, когда то, что в шарповом коде было обычным полем, вдруг станет ленивым свойством. Сложность анализа, навскидку, зашкалит.
У стандартного shared_ptr в нашем случае сразу несколько проблем. Во-первых, даже при использовании enable_shared_from_this нельзя дёргать shared_from_this на этапе работы конструктора (что для нас критично), в отличие от intrusive_ptr. Во-вторых, если бы было даже можно, то нельзя было бы манипулировать значением shared_count, чтобы избегать удаления конструируемого объекта (как описано выше в статье). В-третьих, проблемы с конверсией this в shared_ptr — при наследовании Object от enable_shared_from_this придётся каждый раз делать dynamic_pointer_cast во всех дочерних классах, что существенно замедляет работу. Привожу ниже код, который демонстрирует, как выглядел бы пример из раздела «Вид умных указателей» в семантике make_shared — можно легко убедиться, что он не работает.
#include <iostream>
#include <memory>
using namespace std;
class Object : public enable_shared_from_this<Object>
{
public:
Object()
{}
virtual ~Object()
{}
};
class Node;
class Document : virtual public Object
{
shared_ptr<Node> m_root;
public:
virtual ~Document()
{}
Document()
{
m_root = make_shared<Node>(dynamic_pointer_cast<Document>(shared_from_this()));
}
};
class Node : virtual public Object
{
weak_ptr<Document> m_document;
public:
virtual ~Node()
{}
Node(shared_ptr<Document> document)
{
m_document = document;
}
};
int main(int, char*[])
{
auto doc = make_shared<Document>();
return 0;
}
Нет, в том, что касается синхронизации потоков, у нас особой работы проделано не было. Дело в том, что наши библиотеки по большей части однопоточные, т. к. предоставляют, в сновном, сервис по работе с файловыми форматами. Потребности отдельных потоков, которые занимаются обновлением кэшей или т. п., не выходят за рамки типовых реализаций Thread или ThreadPool. Тот же паттерн async-await мы пока не транслировали (т. к. он не поддерживается синтаксическим анализатором).
Атрибут можно поставить на метод, указав имя переменной/параметра/лямбды/номер строки/и т. д. в качестве аргумента атрибута. Хотя, конечно, не так красиво. А вообще, насколько я помню, задачу удалось решить в полностью автоматическом режиме, вынося все неоднозначные переменные на кучу, как это делает компилятор C#.
Это да. Тут ИМХО всё зависит от реализации. Наверняка те же алгоритмы, написанные изначально на C++ и оптмизированные под него же, будут бегать быстрее, чем версии, переведённые с C# и, скорее всего, быстрее, чем реализации на нативном C#. Но нам, увы, приходится транслировать код, а не алгоритмы, т. к. отделить одно от другого не получается.
Поддерживаемая версия C# у нас ограничена синтаксическим анализатором, который мы используем. Сейчас у нас там NRefactory (это 5-ая версия), после перехода на Roslyn (работа идёт) будет актуальная версия.
Лямбды транслируются в лямбды же, продление времени жизни переменных мы сейчас прикручиваем на этапе кодогенерации. LINQ транслируется, получается функционально эквивалентный код. Span в наших продуктах не использовался, но, если будет, я не вижу с первого взгляда особых проблем с тем, чтобы эмулировать его через aligned_storage. Из того, что мы пока не прожевали, на ум приходят yeild (хотя на уровне кодогенерации это можно сделать, просто не было настолько нужно) и виртуальные обобщённые функции (т. к. дженерики мы транслируем в шаблоны, виртуальность под запретом).
Проверка утечек на C++ делается циклическим запуском тестов через --gtest_repeat, API, не покрытого тестами, у нас нет, хотя могут оставаться отдельные ветки. Аннотации против утечек расставляют программисты C++ при подготвоке очередного релиза, они же проверяют потребление памяти.
По поводу использования mono — в прошлой статье я писал о том, что C++-версия делалась, в основном, по анналогии с Java-версией, для которой данный подход сработал примерно в то время, когда mono ещё был достаточно сырым фреймворком. Кроме того, непосредственный проброс API не избавит от необходимости решать задачи согласования с плюсовым кодом (обёртки над System.IO.Stream для использования с iostream, итераторы для IEnumerable, синтаксический сахар для работы со строками, и т. д.). Хотя, разумеется, в чём-то это сократило бы объём работы, но зато сделало бы использование продукта более сложным для клиентов.
Добрый день. Возможно, Вы в чём-то правы, наши «порты» для Python действительно работают по похожей схеме. С другой стороны, при наличии большого числа мелких объектов (а это как раз наш случай) маршаллинг вызовов съедает очень много ресурсов, чего, конечно же, хотелось бы избежать.
Одной из наших целей было получить возможно более простое решение для пользователя, который может просто подключить плюсовую библиотеку к плюсовому приложению и работать. Мост между CLR (не важно, mono или нативным) — это усложнение данной схемы, хотя, возможно, и оправданное.
Спасибо за комментарии. У нас была обратная ситуация как раз с Bohem GC, было больше проблем, чем с умными указателями, и разработку в конце концов свернули. К сожалению, я этим направлением не занимался, не смогу подробнее рассказать, в чём там был затык.
Одна из принципиальных проблем с использованием сборщиков мусора в нашем случае — в том, что мы поставляем библиотеки, а не приложения. Произвольный клиентский код будет не слишком рад, если его вдруг начнут собирать.
Добрый день. На самом деле — нет. Дело в том, что в C# на операциях создания-удаления куча банально быстрее (не в последнюю очередь из-за компактификации), к тому же, у нас копирование указателей — это вызов, а не одна операция копирования числа. Возможно, на каком-то коде ситуация будет иной, но наши C#-программисты оптимизируют свой код именно для .Net с GC, так что при переходе на плюсы чуда не происходит. Наши усилия обычно направлены на то, чтобы, наоборот, избежать проседания производительности.
Любому модулю доступно ограниченное подмножество API, исходя из общих соображений о том, какой API ему нужен для его работы. При этом информации о том, какой именно модуль предоставил этот API, у него нет, и заточка под конкретный стек невозможна.
Например, модуль, потребляющий видеопоток (окно видеоплейера, программа захвата видео, отрисовщик стикеров с ушами животных), может работать с любым источником (файл, видеохостинг, видеозвонок, web-камера и т. д.), не зная, откуда пришло видео. При этом доступ к произвольному API (сеть, сканирование диска, перехват данных других программ и т. п.) для него закрыт.
С юридической стороны — соглашение о струдничестве, которое написано вокруг того, чтобы не тянуть одеяло на себя, а вместе строить удобную для пользователя, прозрачную, насквозь совместимую инфраструктуру (пример такого подхода с возможными санкциями и исключением из магазина даёт Apple).
С технической стороны — то, что ядро ОС отслеживает, откуда пришёл вызов, и не даёт произвольному модулю использовать произвольный API. Например, GUI-компонент видеоплейера не имеет доступа к сетевому стеку. Плюс, в данной ОС нет скозного API, который бы мог вызвать кто угодно — всё строится вокруг вызова API модулей, ссылки на которые есть у контекста, а кому попало они, опять же, не передаются, т. к. весь API утверждается на уровне ОС.
В-третьих — несоразмерность усилий. П. 2 показывает, что ОС заточена для другого, а то, что Вы описали, куда проще и легетимнее реализовать на Windows или Android.
Поддерживать никто никого не должен, в т. ч. наша ОС — тех разработчиков, которые используют её не в тех целях, для которых она создаётся.
Во-первых, всё упирается в объём работы. По сути, нужно портировать (не важно, единовременно или в автоматическом режиме) десятки миллионов строк наших проектов на Haxe для того, чтобы им можно было пользоваться. Вряд ли это проще, чем в случае с прямым портом на C++, но во втором случае мы хотя бы имеем на выходе готовый продукт и полный контроль над тем, как он выглядит.
Во-вторых, наш портируемый код написан не в абстрактном окружении, а вполне конкретном — дотнете. Если где-либо в API торчит System::Collections::Generic::IEnumerable<System::String> — то это значит System::Collections::Generic::IEnumerable<System::String>, а не QList, не std::list<std::string>::iterator, не const char**, и не что-либо ещё из плюсовых концепций. А имнно: этот IEnumerable будет использоваться внутри foreach, в LINQ-выражениях, он будет сохраняться в переменные и возвращаться из функций, конвертироваться в object и извлекаться обратно, проверяться на приводимость к другим интерфейсам и типам, на равенство null, и так далее. То есть, если платформа, на которую мы перетаскиваем свой код, не будет соответствовать какому-либо из этих поведений, код придётся править, и весьма существенно. Аналогично, String должен следовать поведению дотнетовского String, иначе будут проблемы. В этом смысле оторваться от дотнета далеко не так просто, как кажется, особенно при поддержании развивающихся проектов, которые продолжают ежемесячно дорабатываться программистами, работающими в парадигме C#.
Функционально — примерно то же самое, сам думал о таком варианте. С другой стороны, блок тумблеров (а тут нужно минимум три контактных группы на каждый канал — два провода данных и питание) будет заведомо больше, чем мой пульт. Плюс, нужно будет вручную выдерживать паузу между отключением от одного источника и подключением к другому (иначе при быстром щелчке переключение может не произойти). Ну, и не будет автоматики — у меня контроллер понимает, что, если подключен только телефон, нужно отдать ему звуковую карту, а остальные устройства вырубить.
Спасибо за информацию.
Не знал, спасибо. Если возникнут проблемы — добавлю в следующей версии платы.
Да, можно будет попробовать в будущем. Не пришло в голову — пытался эмулировать то, что происходит при переключении разъёмов.
Не знал, спасибо. Надо будет про это почитать. В моём случае это не требуется (бывает, что главный монитор отдан телефону, а на боковых в это время что-то делаю на компьютере мышью), но приятно иметь возможности.
Ещё раз спасибо.
Записал. Пока ничего такого не замечал, но мало ли.
У меня получилось четыре реле, подключенных к ножкам контроллера, и два светодиода. До 200 мА всё вместе не дотягивает.
Лямбды транслируются в лямбды же, продление времени жизни переменных мы сейчас прикручиваем на этапе кодогенерации. LINQ транслируется, получается функционально эквивалентный код. Span в наших продуктах не использовался, но, если будет, я не вижу с первого взгляда особых проблем с тем, чтобы эмулировать его через aligned_storage. Из того, что мы пока не прожевали, на ум приходят yeild (хотя на уровне кодогенерации это можно сделать, просто не было настолько нужно) и виртуальные обобщённые функции (т. к. дженерики мы транслируем в шаблоны, виртуальность под запретом).
Проверка утечек на C++ делается циклическим запуском тестов через --gtest_repeat, API, не покрытого тестами, у нас нет, хотя могут оставаться отдельные ветки. Аннотации против утечек расставляют программисты C++ при подготвоке очередного релиза, они же проверяют потребление памяти.
По поводу использования mono — в прошлой статье я писал о том, что C++-версия делалась, в основном, по анналогии с Java-версией, для которой данный подход сработал примерно в то время, когда mono ещё был достаточно сырым фреймворком. Кроме того, непосредственный проброс API не избавит от необходимости решать задачи согласования с плюсовым кодом (обёртки над System.IO.Stream для использования с iostream, итераторы для IEnumerable, синтаксический сахар для работы со строками, и т. д.). Хотя, разумеется, в чём-то это сократило бы объём работы, но зато сделало бы использование продукта более сложным для клиентов.
Одной из наших целей было получить возможно более простое решение для пользователя, который может просто подключить плюсовую библиотеку к плюсовому приложению и работать. Мост между CLR (не важно, mono или нативным) — это усложнение данной схемы, хотя, возможно, и оправданное.
Одна из принципиальных проблем с использованием сборщиков мусора в нашем случае — в том, что мы поставляем библиотеки, а не приложения. Произвольный клиентский код будет не слишком рад, если его вдруг начнут собирать.
Например, модуль, потребляющий видеопоток (окно видеоплейера, программа захвата видео, отрисовщик стикеров с ушами животных), может работать с любым источником (файл, видеохостинг, видеозвонок, web-камера и т. д.), не зная, откуда пришло видео. При этом доступ к произвольному API (сеть, сканирование диска, перехват данных других программ и т. п.) для него закрыт.
Поддерживать никто никого не должен, в т. ч. наша ОС — тех разработчиков, которые используют её не в тех целях, для которых она создаётся.