Здравствуйте, okman, Вы писали:
O>Не всякая программа, особенно завязанная на многопоточность, может быть написана исключительно O>стандартными средствами. std::atomic не всемогущ. К тому же, он только в C++11. O>Тогда и _mm_mfence нестандартен, и InterlockedIncrement, и еще куча всего.
Ага, старые стандарты не касались многопоточности. В новом же std::atomic таки всемогущ, т.к. он
имеет атомарные load, store и CAS, с помощью чего можно реализовать любую синхронизацию.
O>А Вы заметили, что в обоих топиках, ссылки на которые я давал выше, были явно O>упомянуты CPU и architecture ?
Аналогично, там предполагается отсутствие store reordering у процессора (и даже упоминается, что с non-temporal
инструкциями на х86 это не работает). Я с этим и не спорю.
V>>>>Пример кода вы привели сами — посмотрите на ассемблерный выхлоп, там не будет инструкций ни явно влияющих на V>>>>реордеринг (вроде mfence), ни влияющих неявно (вроде cmpxchg).
O>Что мне не удалось Вас убедить. O>
Ну а я делаю (имхо, очевидный) вывод, что барьеры памяти компилятором не ставятся
O>Да, я встречал это мнение раньше. O>Вот, например, была здесь такая тема несколько лет назад — http://www.rsdn.ru/forum/cpp/978815.flat.aspx
Вообще, по технической части у нас с вами особых противоречий нет, просто вы слишком оптимистично воспринимаете
поведение volatile, хотя это всего лишь побочный эффект и особенности архитектуры.
V>>В новом же std::atomic таки всемогущ, т.к. он V>>имеет атомарные load, store и CAS, с помощью чего можно реализовать любую синхронизацию.
D>отнимая 100% CPU?
Не всегда. В конце концов, есть wait-free. Если нужно уметь засыпать — придется дополнительно
использовать вызовы ядра, ибо стандарт не занимается шедулированием.
Здравствуйте, enji, Вы писали:
E>- напишите функцию реверса строки
Кстати, о реверсе строки.
Напишите свою, если не сложно(ну, кандидат-то должен, отчего бы и вы не написали бы?). Мне вот интересно было бы посмотреть.
Новости очень смешные. Зря вы не смотрите. Как будто за наркоманами подсматриваешь. Только тетка с погодой в завязке.
There is no such thing as a winnable war.
E__>Кстати, о реверсе строки.
E__>Напишите свою, если не сложно(ну, кандидат-то должен, отчего бы и вы не написали бы?). Мне вот интересно было бы посмотреть.
Ах, да, чтобы чисто было — данные берем из файла, кладем в файл.
Новости очень смешные. Зря вы не смотрите. Как будто за наркоманами подсматриваешь. Только тетка с погодой в завязке.
There is no such thing as a winnable war.
Здравствуйте, landerhigh, Вы писали:
L>shared_ptr. В embedded? Мьсе знает толк.
эмбеддед — он вообще-то разный Есть 8к флеша, а есть мегабайт... Где-то нельзя использовать динамическую память, где-то можно. К тому ж у нас есть и не эмбеддед тоже
L>Не говоря уже о том, что наличие shared_ptr эквивалентно знанию конкретного типа.
Да неужели.
struct A
{
};
struct B : A
{
string s;
};
shared_ptr<A> createB() { return shared_ptr<A>(new B); }
{
auto pA = createB();
} // тут мы не знаем типа B, однако он вполне корректно удалится
И согласись, что шаред птр не эквивалентен "50 dynamic_cast" вообще никак. Вариант с dynamic_cast (который кстати не поддерживается тем-же иар-ом ) — это полная жопа как по написанию, так и по поддержке.
Здравствуйте, Kernan, Вы писали:
K>Здравствуйте, enji, Вы писали:
K>Так С или C++?
Дык писал выше. И то и то. Есть старые проекты, которые вообще были на асме, потом мигрировали на С, потом частично дописываются на С++.
K>Лучше напиши немног оговнокода и попроси исправить и отформатировать на ноуте.
Можно и так, спасибо за идею
Здравствуйте, enji, Вы писали:
L>>shared_ptr. В embedded? Мьсе знает толк. E>эмбеддед — он вообще-то разный Есть 8к флеша, а есть мегабайт... Где-то нельзя использовать динамическую память, где-то можно. К тому ж у нас есть и не эмбеддед тоже
Ну ты сам упомянул эмбеддед, в чем вопрос? И таки да, бывает, что и динамическую память использовать можно, и ее вроде бы даже много, мегабайты, да на поверку оказывается, что лучше не надо.
L>>Не говоря уже о том, что наличие shared_ptr эквивалентно знанию конкретного типа. E>Да неужели.
Абсолютно.
E>
E>struct A
E>{
E>};
E>struct B : A
E>{
E> string s;
E>};
E>shared_ptr<A> createB() { return shared_ptr<A>(new B); }
E>{
E> auto pA = createB();
E>} // тут мы не знаем типа B, однако он вполне корректно удалится
E>
В том-то и дело, что тут мы "знаем" тип B, точнее, о нем за нас "знает" shared_ptr. Равно как знали бы, если бы нам дали голый указатель вместе с его удалятором. Такой случай никакого интереса не представляет, ибо о корректном удалении позаботились заранее.
Вот когда у тебя уже есть только IInterface* pPointer, да без виртуального деструктора, и ничего больше, как будешь выкручиваться?
Здравствуйте, Eugeny__, Вы писали: E__>Напишите свою, если не сложно(ну, кандидат-то должен, отчего бы и вы не написали бы?). Мне вот интересно было бы посмотреть.
std::string rev(const std::string &s)
{
std::string r = s;
for (auto i = 0U, len = s.size(); i < len/2; ++i)
std::swap(r[i], r[len - i - 1]);
return r;
}
про std::reverse я в курсе, но на память не помню, что он принимает.
Здравствуйте, landerhigh, Вы писали:
L>В том-то и дело, что тут мы "знаем" тип B, точнее, о нем за нас "знает" shared_ptr.
Тут его знаем не мы, а шаред-птр, что вещи немного разные.
L>Вот когда у тебя уже есть только IInterface* pPointer, да без виртуального деструктора, и ничего больше, как будешь выкручиваться?
Ну вестимо обращаться к тому, кто мне его дал. Вероятно там предусмотрена процедура его удаления. А если не предусмотрена — то стоит ли его мне вообще удалять? МОжет быть он удалится автоматом при удалении какого-то другого объекта?
И только если я точно знаю, что тут косяк у автора библиотеки, который не предусмотрел удаление, и не могу поправить исходный код и согласен при каждом новом выпуске библиотеки проверять это место на предмет того, не добавились ли новые типы-наследники IInterface и не поменялась ли иерархия наследования (а ведь проверить это не имея кода — невозможно. А если код есть, то его можно поправить), и библиотека настолько ценна, что я не могу от нее отказаться — вот только тогда я сделаю кучу динамик-кастов.
Кстати если мы все еще про эмбеддинг — то часто динамик-каста там нет... Кроме того, готовые скомпилированные библиотеки для эмбеддинга — редкость. Слишком большой зоопарк компиляторов и их опций.
Здравствуйте, maxkar, Вы писали:
M>Плюс обязательная обработка таймаутов. Вы же сами хотели кормить неверный ввод, а ошибки в передаче на устройство более важная проблема.
Проверка таймаутов нужна далеко не всегда. Особенно подчиненному устройству. Во многих протоколах начало пакета четко определяется и таймауты не нужны.
E>>Опять таки все зависит от протокола. В данном конкретном достаточно таблицы команда — функция-обработчик E>>Если реализовать ее не в виде switch, то можно будет добавлять новые команды динамически — например в зависимости от конфигурации. Или на одном хоботе поддерживать одни команды, а на другом — другие.
M>А почему в виде таблицы?
Потому что проще добавить новую команду в рантайме, если есть таблица указателей на обработчики. а не фиксированный свитч. M>Почему от конфигурации?
Потому что задачи разные, сегодня одна, завтра другая. Иногда проще иметь общий код и конфигурацию, а не два свитча. M>Может, их нужно вообще с устройством согласовывать на самом деле?
Не понял вопроса...
M>Кстати, а как устройство реагирует на неподдерживаемые (и неполностью полученные) команды?
Как описано в протоколе. На неподдерживаемые возвращает ошибку, не полностью полученные игнорирует
M>Вот исходя из всех этих вопросов и не понятно, почему кандидат должен прийти к тому решению, которое вы хотите. Для простейшей задачи (даже с добавлением) табличка обработчиков — типичный overengeneering, там для решения if/of или case'ов более чем достаточно (особенно если ввод ручками парсить и на ошибки проверять!). А для корректного выбора более сложной схемы недостаточно данных. С реальном устройстом и приложением чуть попроще, там и круг комад ограничен, и сценарии использования более-менее очевидны.
Я не хочу, чтобы он пришел к моему решению. Пусть придет к своему. Если оно будет правильным — т.е. при данном вводе даст правильный вывод, то дальше можно дискутировать — об архитектуре, о стиле...
Еще раз повторяю — это не тест, это разговор...
Здравствуйте, Eugeny__, Вы писали:
E__>Нормальная реализация failover в рамках даже довольно простого протокола уже несколько выходит за пределы "простой задачи на пару часов". В рамках чуть более сложного — это уже реальная задача, решать которую нахаляву — глупость. В немалой степени из-за того, что в доках к девайсам я _никогда_ не видел полного описания всех возможных flow, остальное додумывать и доделывать самому. Может, ТС живет в мире эльфов, и там всегда есть более чем развернутая документация по устройствам. Я живу в реальном мире, где иногда единственное, что есть — это двойной автоперевод китайский-->английский-->русский(без оригиналов, есть в наличии только этот "русский" вариант), и несколько логов с перепиской каких-то индусов с кошмарным английским... Как хорошо, что хотя-бы цифры во всех языках одинаковы.
Протокол, который я прошу реализовать (а точнее его часть) — очень простой. Кроме того, он наш собственный, дока на него весьма проста, подробна и на русском. Про то, что протоколы бывают разные — я как бы в теме, лет 5 уже ими занимаюсь
Более того, даже если два устройства работают по одному протоколу, они могут быть не совместимы — например первое делали немцы, а второе — турки. И каждый понял исходный американский протокол по своему
Здравствуйте, Eugeny__, Вы писали:
E__>А теперь вопрос: если человек нормально отвечает и рассказывает все нюансы при использовании volatile, стоит ли его грузить унылыми вопросами про переворот строки и прочим? Может ли человек, понимающий, как работает этот барьер памяти на уровне разных архитектур, не понимать указателей? Или чего-то еще?
Ок, согласен. Стоит упорядочить от сложного к простому
Здравствуйте, enji, Вы писали:
E__>>Напишите свою, если не сложно(ну, кандидат-то должен, отчего бы и вы не написали бы?). Мне вот интересно было бы посмотреть.
E>
E>std::string rev(const std::string &s)
E>{
E> std::string r = s;
E> for (auto i = 0U, len = s.size(); i < len/2; ++i)
E> std::swap(r[i], r[len - i - 1]);
E> return r;
E>}
E>
E>про std::reverse я в курсе, но на память не помню, что он принимает.
Я не зря уточнил про чтение и запись в файл.
Меня, в частности, интересует корректность работы алгоритма с разными хитрыми юникодами(особенно всякие диакритики и суррогаты). Там все оказывается не совсем просто.
Новости очень смешные. Зря вы не смотрите. Как будто за наркоманами подсматриваешь. Только тетка с погодой в завязке.
There is no such thing as a winnable war.
Здравствуйте, Eugeny__, Вы писали:
E__>Я не зря уточнил про чтение и запись в файл. E__>Меня, в частности, интересует корректность работы алгоритма с разными хитрыми юникодами(особенно всякие диакритики и суррогаты). Там все оказывается не совсем просто.
Ну батенька, куда нам до юникодов. Нам по простому, в ascii
Но если кандидат спросит про юникод — честь и хвала ему.
А кстати да, ведь как только начинается юнокод, задача сразу становится нетривиальной. Там символ может быть выражен несколькими "кодовыми точками" (или как она правильно называются)...
E__>>Я не зря уточнил про чтение и запись в файл. E__>>Меня, в частности, интересует корректность работы алгоритма с разными хитрыми юникодами(особенно всякие диакритики и суррогаты). Там все оказывается не совсем просто.
E>Ну батенька, куда нам до юникодов. Нам по простому, в ascii E>Но если кандидат спросит про юникод — честь и хвала ему.
E>А кстати да, ведь как только начинается юнокод, задача сразу становится нетривиальной. Там символ может быть выражен несколькими "кодовыми точками" (или как она правильно называются)...
Ну а вдруг человек раньше плотно работал с юникодом? Ты-то про ascii в задании не упомянул! А как раз задача реверснуть строку в случае юникода становится именно что не особо тривиальной — твой алгоритм превратит строку в кашу. Потому он будет думать о написании переворота юникода, и может вполне не вспомнить все нюансы, потому скажет "без компа не смогу выполнить это задание". А ты подумаешь, что он банально байты поменять местами не умеет .
Мораль простая — точнее описывать задания.
Новости очень смешные. Зря вы не смотрите. Как будто за наркоманами подсматриваешь. Только тетка с погодой в завязке.
There is no such thing as a winnable war.
Здравствуйте, Eugeny__, Вы писали:
E__>Ну а вдруг человек раньше плотно работал с юникодом? Ты-то про ascii в задании не упомянул! А как раз задача реверснуть строку в случае юникода становится именно что не особо тривиальной — твой алгоритм превратит строку в кашу. Потому он будет думать о написании переворота юникода, и может вполне не вспомнить все нюансы, потому скажет "без компа не смогу выполнить это задание". А ты подумаешь, что он банально байты поменять местами не умеет . E__>Мораль простая — точнее описывать задания.
Может быть. Но в этой ветке к примеру только ты вспомнил про юникод Мне все же кажется, что человек перед тем как начать думать над явно нетривиальным алгоритмом, задаст вопрос. Хотя конечно, стоит уточнить
Здравствуйте, enji, Вы писали:
E>Здравствуйте, maxkar, Вы писали:
M>>Плюс обязательная обработка таймаутов. Вы же сами хотели кормить неверный ввод, а ошибки в передаче на устройство более важная проблема. E>Проверка таймаутов нужна далеко не всегда. Особенно подчиненному устройству. Во многих протоколах начало пакета четко определяется и таймауты не нужны.
Не понял. Если к подчиненному устройству ничего не подключить, оно у вас зависнет намертво (потому что ответа не получает)? Или как? Таймауты нужны не потому, что нужно определять начало/конец пакета. Таймату нужны, чтобы программа не зависала в следующих случаях:
* Устройство не подключено (пакеты уходят, но не приходят). Это если линия связи не поддерживает состояние "установлено соединение".
* Подключено другое устройство. Оно спокойно съело ваш пакет и ждет еще, ничего не отвечая.
* Устройство чем-то ответило, но в ответе нет маркеров начала/конца пакета.
* Пришла только часть ответа устройства, а остальная не пришла из-за нарушения канала связи (очень похоже на предыдущий варинат).
И во всех этих случаях если читать ответ от устройства на блокирующем канале без таймаутов, у вас драйвер зависнет навсегда. Кроме того, у вас в задаче драйвер пишется не для подчиненного, а для ведущего устройства (там управление с консоли вроде бы было). Это те сценарии, где ваш "автотестер" который вместо кривых пакетов вообще ничего не отвечает. Это вполне вероятная ситуация для устройств.
E>>>Опять таки все зависит от протокола. В данном конкретном достаточно таблицы команда — функция-обработчик E>>>Если реализовать ее не в виде switch, то можно будет добавлять новые команды динамически — например в зависимости от конфигурации. Или на одном хоботе поддерживать одни команды, а на другом — другие.
M>>А почему в виде таблицы? E>Потому что проще добавить новую команду в рантайме, если есть таблица указателей на обработчики. а не фиксированный свитч. M>>Почему от конфигурации? E>Потому что задачи разные, сегодня одна, завтра другая. Иногда проще иметь общий код и конфигурацию, а не два свитча. M>>Может, их нужно вообще с устройством согласовывать на самом деле? E>Не понял вопроса...
Последнее — это когда нужно понять, какая ревизия устройства, и автоматически определить набор команд.
По всем пунктам выше. Они не были в исходной задаче. Зато там было сформулировано "Предусмотрите возможность добавления новых команд". И оно никак к тому выше отношения не имеет. Для этого нужно знать предметную область или хотя бы конкретную задачу.
M>>Кстати, а как устройство реагирует на неподдерживаемые (и неполностью полученные) команды? E>Как описано в протоколе. На неподдерживаемые возвращает ошибку, не полностью полученные игнорирует
M>>Вот исходя из всех этих вопросов и не понятно, почему кандидат должен прийти к тому решению, которое вы хотите. Для простейшей задачи (даже с добавлением) табличка обработчиков — типичный overengeneering, там для решения if/of или case'ов более чем достаточно (особенно если ввод ручками парсить и на ошибки проверять!). А для корректного выбора более сложной схемы недостаточно данных. С реальном устройстом и приложением чуть попроще, там и круг комад ограничен, и сценарии использования более-менее очевидны. E>Я не хочу, чтобы он пришел к моему решению. Пусть придет к своему. Если оно будет правильным — т.е. при данном вводе даст правильный вывод, то дальше можно дискутировать — об архитектуре, о стиле...
А к чему тогда фраза про "предусмотрите возможность добавления новых команд"? Она выглядит так, как будто работодатель чего-то хочет, но сам не может объяснить, чего именно. Причем по виду вопроса видно, что он на самом деле хочет конкретного решения. А нужно описывать исходную проблему. Вот как вы отнесетесь к вопросу кандитада "для чего предусматривать возможность добавления новых команд" вместо выполнения задания? А ведь именно ответ на этот вопрос должен быть в постановке задачи. И уже из самой задачи будет понятно, нужно ли там "добавление новых команд". Может, будут еще куча вариантов решения именно той, исходной, задачи.
Здравствуйте, enji, Вы писали:
L>>В том-то и дело, что тут мы "знаем" тип B, точнее, о нем за нас "знает" shared_ptr. опровоп E>Тут его знаем не мы, а шаред-птр, что вещи немного разные.
Не вижу никакой разницы. Главное то, что конкретный тип объекта или конкретный удалятор известен. Что в корне меняет суть изначального вопроса.
L>>Вот когда у тебя уже есть только IInterface* pPointer, да без виртуального деструктора, и ничего больше, как будешь выкручиваться? E>Ну вестимо обращаться к тому, кто мне его дал. Вероятно там предусмотрена процедура его удаления. А если не предусмотрена — то стоит ли его мне вообще удалять? МОжет быть он удалится автоматом при удалении какого-то другого объекта?
вот ты сам и ответил на свой вопрос. А правильный ответ будет "не надо заниматься ерундой, а надо добавить виртуальный конструктор. Если нельзя (интерфес не наш) — ищи варианты". Вариантов уже озвучили кучу.
В дальнейшем я говорю с т.зр подчиненного устройства. ТЗ как раз и состоит в его реализации.
M>>>Плюс обязательная обработка таймаутов. Вы же сами хотели кормить неверный ввод, а ошибки в передаче на устройство более важная проблема. E>>Проверка таймаутов нужна далеко не всегда. Особенно подчиненному устройству. Во многих протоколах начало пакета четко определяется и таймауты не нужны. M>Не понял. Если к подчиненному устройству ничего не подключить, оно у вас зависнет намертво (потому что ответа не получает)?
Протокол типа запрос-ответ. Если к подч устройству ничего не подключить, то оно не получит ответа и не будет формировать запросов. Если подключить и тут же отключить, то если устройство успеет получить запрос, оно выплюнет ответ. При этом ему все равно, получен ответ или нет. Если ответ не может быть получен, он просто уйдет в пустоту.
M> * Устройство не подключено (пакеты уходят, но не приходят). Это если линия связи не поддерживает состояние "установлено соединение".
Линия связи в данном конкретном случае RS232, без доп сигналов — только RX\TX M> * Подключено другое устройство. Оно спокойно съело ваш пакет и ждет еще, ничего не отвечая.
Это его проблемы. А не подчиненного устройства. Подчиненное устройство просто отвечает на правильные запросы правильными ответами и делает свою работу. M> * Устройство чем-то ответило, но в ответе нет маркеров начала/конца пакета.
Это как? Подчиненное устройство ответило, но нет маркеров? Ну значит в программе бага, надо ее править. M> * Пришла только часть ответа устройства, а остальная не пришла из-за нарушения канала связи (очень похоже на предыдущий варинат).
Опять таки проблемы мастера. Если ему это важно, он может переспросить или считать состояние. M>И во всех этих случаях если читать ответ от устройства на блокирующем канале без таймаутов, у вас драйвер зависнет навсегда.
Мы говорим про реализацию подчиненного устройства, а не мастера. Кроме того, в реальном устройстве чтение вообще-то идет в отдельном потоке, не мешая работе всего остального. Конкретно в данной тестовой задаче не подразумевается "ничего остального", поэтому используется обычное блокирующее чтение из консоли, без всяких потоков. M> Кроме того, у вас в задаче драйвер пишется не для подчиненного, а для ведущего устройства (там управление с консоли вроде бы было).
Нет, для подчиненного. Которое управляется с консоли и плюет ответы в консоль
M>>>Вот исходя из всех этих вопросов и не понятно, почему кандидат должен прийти к тому решению, которое вы хотите. M>>>Для простейшей задачи (даже с добавлением) табличка обработчиков — типичный overengeneering, там для решения if/of или case'ов более чем достаточно (особенно если ввод ручками парсить и на ошибки проверять!).
Еще раз, самый последний — я не прошу кандидата прийти к моему решению. Это невозможно в принципе — у меня один опыт, у него другой.
Задача то простейшая, но я прошу подумать над определенными вопросами. Дальше, имея реализацию кандидата, с ним можно обсуждать его решения. Почему он решил так, а не иначе, в чем плюсы, в чем минусы.
Вот ты говоришь — сделать switch, этого достаточно. Может быть и да. А если добавится новая команда? А если в одном варианте потребуется поддержать такие команды, в в другом — другие?
Или например — разделит ли кандидат уровень сборки пакета из байт и уровень обработки? Насколько они у него получатся связанными? А если к примеру завтра RS-232 сменится на UDP и структура пакетов на канальном уровне поменяется (вполне реальная задача кстати)? А если потребуется совершенно другой протокол, но с той же логикой формирования пакетов — можно ли будет использовать часть имеющегося кода, или потребуется его переписывание?
Все это вопросы для обсуждения.
M>А к чему тогда фраза про "предусмотрите возможность добавления новых команд"? Она выглядит так, как будто работодатель чего-то хочет, но сам не может объяснить, чего именно.
Ты не поверишь, это самая обычная рабочая ситуация. От меня чего-то хотят, мне надо включать мозг и думать — а если я сделаю вот так и они захотят вот того — как я буду выкручиваться? А если сделать по другому?