LINUX.ORG.RU

Вызов функции из аргумента(C++)

 , ,


0

2

Пишу консольную программу «Органаизер» на С++ и дошел до момента проверки комманд. Пытаюсь сделать это через словарь(map)

map <string, void*> AvailableCommands;

То есть вводится команда, проверяется наличие ключа и после этого мне нужно запустить функцию, которая передаётся в качестве значения словаря. Как это сделать? Можно ли так делать или же лучше это сделать по другому?


Ответ на: комментарий от lovesan

Тебе уже сказали самый вменяемый вариант - делаешь if-else с проверкой строки на название команды, и руками вызываешь нужные функции.

это нерасширяемо динамически и требует ненужные зависимости в мейне.

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

вот примерно этот механизм и изобретает автор, но пока в редуцированном виде.

alysnix ★★★
()
Ответ на: комментарий от Jython

Правильно ли я понял, что идея в том чтобы обращаться к адресу пернменной command а не копировать значение в аргумент? Или я не так понял суть комментария?

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

alysnix ★★★
()
Ответ на: комментарий от Jython

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

Jython
() автор топика
Ответ на: комментарий от Jython

будет выполняться быстрее чем ифы.

с правилами О большое

«О большое» это асимптотика. Чтобы оно начало работать, команд должно стать много.

if-ы это линейный поиск нужной команды. Он хуже асимптотически, но лучше на малых наборах команд.

Кроме того, if-ы это статический набор команд, тогда как map – динамический. Можно сделать динамический с линейным поиском посредством обычного массива пар или (если есть C++23) std::flat_map.

Siborgium ★★★★★
()
Ответ на: комментарий от alysnix

это нерасширяемо динамически и требует ненужные зависимости в мейне.

Ничего себе!

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

Это настоящая беда многих программистов — оверинжиниринг. Они любую, самую простую задачу, способны усложнить до неузнаваемости, приплести туда какие-то плагины, жыд-компиляцию и Тьюринг-полную конфигурацию. Спрашиваю их, зачем? Расширяемо… Анус себе расширь, пёс!

Самое поганое в этом то, что эти программисты зачастую ошибаются в оценке эволюции программы; результат — будущие изменения получают серьезное сопротивление со стороны заложенной архитектуры, поначалу это воркэрандится по инерции, но в конечном результате получаем version 2.0 completely refactored from the ground. И так до следующего мажорного изменения архитектуры 3.0.

Вам диды завещали: keep it simple! Нет, они на каждый хелловорлд плагины блядь изобретают.

вот примерно этот механизм и изобретает автор, но пока в редуцированном виде.

Ну правильно, в нередуцированном виде вы замените словарь таблицей регулярных выражений, динамически компилирующийся из конфигурационного файла; хэндлеры подсосете из плагинов; в сами хендлеры будете скармливать динамически типизированный контекст aka параметры. Интерпрайзно 👎

UPD Message bus сюда прямо напрашивается, к третьей версии прикрутите, пожалуйста?

filosofia
()
Последнее исправление: filosofia (всего исправлений: 1)
Ответ на: комментарий от filosofia

Ну правильно, в нередуцированном виде вы замените словарь таблицей регулярных выражений, динамически компилирующийся из конфигурационного файла; хэндлеры подсосете из плагинов; в сами хендлеры будете скармливать динамически типизированный контекст aka параметры. Интерпрайзно 👎

клован, не бесись. в общем виде это просто список хуков - интерпретаторов, доступный мейну, где сидят обьекты, способные интерпретировать комстроку или ее часть. какие там интерпретаторы и кто их туда поставил - мейн не знает. он просто вызывает последовательно по списку виртуальный метод - accept_command(…) каждого обекта оттуда, и если метод возвратил true - опрос прекращается.

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

это не спор в автором топика, которому и словаря хватит, а спор с длинным if-else в мейне.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от alysnix

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

Ммм, да тут ещё и контроль очередности инициализации объектных файлов и плагинов напрашивается. Ясно-понятно, продолжаем героически создавать проблемы — ясен хрен, за switch по командам коллеги засмеют.

filosofia
()
Ответ на: комментарий от filosofia

Ммм, да тут ещё и контроль очередности инициализации объектных файлов и плагинов напрашивается.

а он всегда напрашивается, поскольку один плагин может конфликтовать с другим в частности по интерпретации некого куска комстроки.

вот кто первый поставил свой хук - то и прав. а первыми ставят свои хуки те, чей «приоритет» считается более высоким. иначе у вас все превратится в кашу, работающую от случая к случаю.

alysnix ★★★
()
Ответ на: комментарий от filosofia

То есть ты не находишь, что уже превратил решение тривиальной проблемы в кашу?

я проблему клиента вообще не пойму. он видимо хочет искать некие «интерпретаторы» динамически и как можно быстрей, чтобы команду исполнить. для это применяет maр.

сложность и семантика его программы не указана. может там у него появятся плагины например.

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

alysnix ★★★
()

странно всё..рассуждают про О большое, выжимают наносекунды и никто ещё не предложил решения в шаблонах; Чтобы по максимуму в компил-тайм и позволить компилятору развернуться по полной. Что, никто не знает современный C++ ? :-)

для ТС: продумай юз-кейсы (как ты собираешься сейчас и в дальнейшем использовать класс), напиши для него тесты. Пробный производный класс. Всё встанет на свои места - что и как лучше использовать.

MKuznetsov ★★★★★
()
Ответ на: комментарий от MKuznetsov

никто ещё не предложил решения в шаблонах

Если я правильно понимаю, чтобы на шаблонах работало и код был не очень страшным, надо чтобы команда компилировалась после ввода пользователя. Что-то типа eval надо.

Можно ещё фабричный метод приплести, чтобы через полиморфизм работало, но это тоже извращение. Использование словаря с указателями на функции тут наиболее красиво. В C++ case уродлив, поэтому при его использовании код будет зашумлен. Возможно, в паскале оно бы красивее выглядело. Или в языках с паттерн-матчингом.

В питоне есть модуль cmd для таких целей. Тоже неплохой подход, но на C++ такое наверное не сделать.

Kogrom
()
Ответ на: комментарий от Kogrom

Если я правильно понимаю, чтобы на шаблонах работало и код был не очень страшным, надо чтобы команда компилировалась после ввода пользователя. Что-то типа eval надо.

шаблонная магия, это когда таблица команд и диспетчер вызова (который по строке ищет и исполняет команду) строятся в компил-тайм, при инстанцировании шаблона. Это будет современный уровень С++, а не как у нас с вами Си-с-классами-и-STL :-) тогда компилятор может инлайнить и оптимизировать команды

художественно-лирическое примечание про скорость (про которую требований не было, но все отчего-то упёрлись и даже слегка пересрались). На фоне сравнений std::string, абсолютно по барабану что использовать map<> или вереницу if. Если бы задача ТС была-бы не учебной и с требованиями по скорости, то в первую очередь избавляться от строк, заменять токенами/литералами/объектами

MKuznetsov ★★★★★
()
Последнее исправление: MKuznetsov (всего исправлений: 1)
Ответ на: комментарий от MKuznetsov

Ну покажите тогда код с шаблонами. Пусть даже для простоты командами будут не строки, а, например, целочисленные переменные. Для инстанцирования шаблона потребуется вызвать его с именем команды во время компиляции. А нам надо во время исполнения.

Что касается скорости, то тут говорить не о чем. Намного большее время заберёт вывод в консоль, например, чем любой поиск по словарю или проход по if-ам. Стоит ли усилий эта борьба со строками и словарями если она даст прирост по скорости 0,1 %?

Kogrom
()
Ответ на: комментарий от Kogrom

про то что никаких требования про скорость НЕТ выше неоднократно указано. Поэтому для ТС все попытки оптимизировать преждевременны и являются абсолютным злом.

а отвлечённо: кто сказал что команды будут подаваться только из консоли ? может консольный ввод просто для отладки, а вся халабуда для скриптов и мета-программ :-)

хороший полезный C++ чего-то похожего, можно например тут глянуть: https://github.com/ArashPartow/exprtk ; где-то ещё попадались lisp/js/command_shell/somelang С++ templates header-only но не пригодились поэтому не вспомню ссылок

MKuznetsov ★★★★★
()
Ответ на: комментарий от MKuznetsov

На фоне сравнений std::string, абсолютно по барабану что использовать map<> или вереницу if.

Обалдеть аргументация: «сравнения дорогие, а посему нам пофиг их N или log(N)». Ню ню.

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer

Обалдеть аргументация: «сравнения дорогие, а посему нам пофиг их N или log(N)». Ню ню.

вот примерно поэтому нынешний софт тормозит и жрёт как не в себя :-) погонялово за асимптоматику О(), при немерянных множителях.

MKuznetsov ★★★★★
()
Ответ на: комментарий от bugfixer

Приведённый тривиальный (спешу заметить) пример доказывает обратное.

И вот такой же срач под каждым простеньким вопросом про плюсы. %)))) Посмотрит @Jython на это всё, и подумает.

pr849
()
Ответ на: комментарий от Shushundr

Использовать какую-нибудь готовую библиотеку для разбора ключей командной строки…

ДВА ЛИТРА ЧАЯ этому господину! И я бы даже уточнил: не какую-нибудь, а банальный getopt() из glibc. Расширенного вида (getopt_long()) хватает с головой на все случаи жизни.

pr849
()
Последнее исправление: pr849 (всего исправлений: 1)
Ответ на: комментарий от snake266

Как вариант. Сам всегда так делаю на каждом новом типе задач, чтобы потом, юзая чужое готовое, понимать его сильные и слабые стороны.

UPD: Впрочем, getopt() лишь сканирует командную строку, он ортогонален что map, что if-else.

pr849
()
Последнее исправление: pr849 (всего исправлений: 2)
Ответ на: комментарий от Siborgium

Главная ошибка тут в 2 лукапах в мапу на каждую команду.

Слыш, @Noob_Linux, давай расскажи тут всем, что в нормальных приложениях (tm) список команд вообще в конфиг надо помещать.

А я пожалуй плюсану топящих за тупой if-then-else: ни заполнять мап не надо, ни лукапить. Разве что если команд там 100500 разных, и за каждый запуск приложения парсятся-отрабатывают сильно больше одной, тогда std::unordered_map действительно может дать выгоду.

dimgel ★★★★★
()
Последнее исправление: dimgel (всего исправлений: 2)
Ответ на: комментарий от dimgel

сильно больше одной, тогда std::unordered_map действительно может дать выгоду.

ну вот, пришёл и всё опошлил :-) а так всё начиналось, оптимизация «лишнего asm xor» при вызове колбека..

MKuznetsov ★★★★★
()
Ответ на: комментарий от MKuznetsov

шаблонная магия, это когда таблица команд и диспетчер вызова (который по строке ищет и исполняет команду) строятся в компил-тайм, при инстанцировании шаблона. Это будет современный уровень С++

Так, а вот тут яп поспорил. Современный уровень это constexpr: https://github.com/mapbox/eternal

И инициализация map в таком случае будет в compile-time, а в рантайме только lookup. И реализация его заточена (как сказано в readme) на минимизацию binary size, а не на скорость, на отсутствие у ТС требования которой ты упираешь.

dimgel ★★★★★
()
Последнее исправление: dimgel (всего исправлений: 1)
Ответ на: комментарий от dimgel

Так, а вот тут яп поспорил. Современный уровень это constexpr: https://github.com/mapbox/eternal

в принципе да, из актуальных «подглядеть»: https://learnmoderncpp.com/2020/06/01/strings-as-switch-case-labels/ там более доходчиво

MKuznetsov ★★★★★
()
Ответ на: комментарий от MKuznetsov

Супер. Итого мы имеем примеры и для constexpr map, и для constexpr switch. ТСу – играйся - не хочу.

А вот то, что std::hash() – не constexpr, действительно бред собачий. Интересно, они там в своём комитете планируют что-то с этим делать?

dimgel ★★★★★
()
Последнее исправление: dimgel (всего исправлений: 1)
Ответ на: комментарий от dimgel

Клоунадой занимаетесь, уважаемый. Мало того что человек решает принципиально другую задачу, так он ещё не описал зачем это делает и какой жизненный цикл у данной структуры. А вы притягиваете тезис из другого треда (о вполне конкретной задаче работы с динамично изменяемой во времени набором фильтров) сюда.

Noob_Linux ★★★★
()
Ответ на: комментарий от pr849

Да, нет. Мне интересно разное мнение людей. Вот например мнение про то что в конструкторе лучше не делать считывание ввода от пользователя, я согласен, протупил. Но насчёт ифов, я ещё думаю менять или не менять. Мне же нужно не только будет добавлять и выполнять методы, но и ещё выводить доступные команды. А для этого мне придётся сейчас делать Вместо std::function нужно будет писать if и в теле вызов функции, а потом заводить массив с доступными коммандами, если я правильно понял рекоммендацию. Да и почему бы не попробовать этот инструмент, всё равно потом буду сидеть с ручкой и записывать теорию в тетрадь

Jython
() автор топика
Ответ на: комментарий от Forum0888

Как вот вообще на Си программы можно разрабатывать без использования стандартов, которые для C++ разработали?

Очень просто - надо понимать что ты пишешь, и под какую, или какие, платформы.

В целом C++ прекрасно подходит для системной разработки

В целом - отвратительно, там даже ABI нет.

lovesan ★★
()
Ответ на: комментарий от alysnix

Какие хуки? Какие плагины, нахер? Тут человек чуть ли не лабораторку делает.

И кстати в этих хуках и плагинах, даже когда они по-настоящему нужны, что редко бывает - можно ногу сломать.

lovesan ★★
()
Ответ на: комментарий от lovesan

Так то я не делаю ничего особо серьёзного. Я сам учусь и сам себе придумал задачу. Органайзер на С++ с использованием sqlite. Плагинов я писать пока не собираюсь, да и пока не знаю как это делать. Я лишь хочу изучить разные инструменты в С++ и сделать маленький проект для того чтобы в дальнейшем начать работать программистом. В идеале разработка игр

Jython
() автор топика
Ответ на: комментарий от Cufa3

IMHO, using приятнее

Согласен. И ничего против using не имею. Я это вижу как исключительно вопрос привычки. Ну, и имеющейся codebase: когда у Вас мешанина разных стилей читать (и сопровождать) код становится существенно сложнее. И тратить время на замену всех typedef на using - так себе затея: я бы, например, не смог объяснить начальству зачем я это делаю.

Другими словами - там где мне хватает функционала / семантики typedef с 99% вероятностью это будет именно typedef. Ситуация кардинально меняется если нужно алиасить темплейты.

bugfixer ★★★★★
()
Последнее исправление: bugfixer (всего исправлений: 1)
Ответ на: комментарий от bugfixer

Мне вот интересно - а «лишнего» redirection Вы не видите?

Я написал «одинаков насколько это возможно.»

Вот, добавим const&, теперь совсем похожи.

#include <functional>

typedef void (*FuncType1)(void);
typedef std::function<void(void)> FuncType2;

void CallMe1(const FuncType1& func1)
{
   func1();
}

void CallMe2(const FuncType2& func2)
{
    if(!func2) {
        __builtin_unreachable();
    }
   func2();
}
CallMe1(void (* const&)()):
        jmp     [QWORD PTR [rdi]]
CallMe2(std::function<void ()> const&):
        jmp     [QWORD PTR [rdi+24]]

https://godbolt.org/z/Kjbsx3drd

fsb4000 ★★★★★
()
Ответ на: комментарий от safocl

чем std::function плох?

Тем что им «тыркают» куда ни поподя, даже близко не понимая зачем он вводился. В терминах generated code - выше уже было. И таки да - std::function вовсе не бесплатен (за любые абстракции нужно платить).

bugfixer ★★★★★
()
Ответ на: комментарий от safocl

абстрактивно он бесплатен.

Я - практик, и прагматик «до мозга костей». Приведённый asm говорит сам за себя. Или Вы тоже сторонник передачи интов by const-ref?

bugfixer ★★★★★
()
Последнее исправление: bugfixer (всего исправлений: 1)