LINUX.ORG.RU

Концепция передачи параметров в виде строки

 , , , ,


2

4

Есть проект (не важно, на каком языке, но пусть будет C++), в котором есть две так скажем мало связанные между собой части (функции/класса). И одна функция (класс) вызывает другую с передачей туда параметров. И сейчас в качестве интерфейса передачи параметров используется обычная строка, т.е. вызывающая функция генерирует строку вида «Имя_параметра1=значение_параметра1\nИмя_параметра2=значение_параметра2...», передаёт её вызываемой, где происходит парсинг параметров.

Почему сделано так? Потому что вызов реализован через интерфейс (виртуальные функции), которые предполагает быть универсальным, а набор параметров для конкретного вызываемого метода может сильно разниться.

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

Дискасс.

Update:

В качестве примера можно привести программу, обрабатывающую скрипт вида:

Вызвать Функцию1, параметры: п1=з1, п2=з2
Вызвать Функцию2, параметры: п3=з3, п4=з4

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

call_function(Функция1, "строка с параметрами")

★★★★★

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

Я могу предложить так не делать.

intelfx ★★★★★ ()

Что за упорин. Rest придумали вместо soap. Или о чем ты.

Deleted ()

Если действительно нужно - делай, я разрешаю. Потом можно будет легко добавить скриптинг, например, к твоему поделию.

Если нужна именно альтернатива строкам - ну посмотри как ioctl сделаны - передаёшь номер функции и сцылку на структуру с параметрами (или объект, если прям плюсплюсно хочется).

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

Если действительно нужно - делай, я разрешаю

спасибо. Не то, чтобы прям очень-очень, просто решили, что парсинг параметров должен делать тот, для кого эти параметры предназначены, а не вызывающая функция, логично?

За ioctl спасибо, гляну

Update: глянул.

«Долгое время операции ioctl() были основной техникой выполнения нестандартных действий над устройством (и не только в операционной системе Linux, но и в других, например, в той же MS Windows). Но операции ioctl() являются опасными: при их выполнении отклоняется контроль типизации параметров, и в качестве параметра может быть передана любая структура. Если в коде реализующей части вызова ioctl() (модуле ядра) не реализован тщательнейший контроль данных, переданных в вызов, то некоторые параметры могут вызывать тяжелые ошибки в коде ядра, вплоть до его разрушения. Серьёзной альтернативой управляющих операций ioctl() является чтение/запись информации через псевдоимена файловой системы /proc и /sys»

Серебряной пули нет...

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

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

xaizek ★★★★★ ()

Есть проект (не важно, на каком языке)

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

def func(**kwargs):                                                             
    for k, v in kwargs.items():                                                 
        print('{}: {}'.format(k, v))                                            
                                                                                
func(a = 1, b = 2)
a: 1
b: 2
Crocodoom ★★★★ ()
Ответ на: комментарий от Crocodoom

Речь о c++. Собственно, идея с передачей в виде строки возникла благодаря питону :)

Ну и такая концепция кажется логичной: парсинг параметров должен делать тот, для кого эти параметры предназначены, а не вызывающая функция

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

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

А если скастовать void не туда, то произойдёт Segmentation fault, правильно я понимаю?

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

Вот ты распарсил строку «param=val» в param и val, а дальше что?

if(param == "foo") {x.foo = val;}
else if(param == "bar") {x.bar = val;}
//ещё 1000 таких же строк

Неужели так? Рефлексии то нет

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

Тут проблема даже не написать эти 1000 строк кода, а постоянно поддерживать их в актуальном состоянии при изменении кодовой базы.

А какая альтернатива?

В качестве альтернативы можно взять другой язык для твоего менеджера вызовов call_function(Функция1, "строка с параметрами"). Сами функции пускай остаются в C++ — это не проблема. Биндинги генерируются автоматически, а всю работу по разбору будет делать интерпретатор скриптового языка.

Если такой вариант приемлем, то могу набросать минимальный рабочий пример на Python & C++.

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

Если такой вариант приемлем, то могу набросать минимальный рабочий пример на Python & C++.

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

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

А если скастовать void не туда, то произойдёт Segmentation fault, правильно я понимаю?

Если повезёт, то да. Если не повезёт, то тихо будет сделано что-то не то. В том же Qt это надёжно работает, потому что код для вызова и обработки генерируется автоматически.

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

а в этом вашем бусте разве нет какой-нибудь реализации интерфейса Callable? я к тому, что это довольно непростая вещь и велосипедить свое какой смысл?

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

я не знаю, буст не мой, я с ним не работал :)

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

Вдогонку к идее про ioctl: передавай указатель на какой-нибудь пустой базовый класс ArgsObject, а дальше dynamic_cast’ом приводи к типу структуры с аргументами к конкретному методу. Если тебе действительно нужно мультиплексировать несколько методов в один. Но звучит как какое-то переусложнение.

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

Ты описываешь задачу так, как будто эти два модуля у тебя совсем друг про друга не знают. А если так, то можно уж и пойти до конца, заюзав RPC, и считать их разными процессами: какая разница, в одном они адресном пространстве или нет. Но никто тебе не скажет точно, пока ты не расскажешь исходную задачу. А до тех пор мы обречены решать X-Y проблему.

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

Паттерн «стратегия» может быть просто интерфейсом — набором функций объеденным в структуру. В нужный момент выбираем нужную функцию.

Bobby_ ()

Ты что-то делаешь не так.

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

Набор параметров известен на момент компиляции? Если да, то можно просто вызывать функцию - обычную или виртуальную.

Если нет - приведи пример реального кейса когда такое возможно. Ведь если программа не знает на момент компиляции сколько параметров у вызываемой функции, сколько параметров она будет передавать при вызове этой функции? Приведи кейс, уверен что он решается либо с помощью массивов, либо с помощью опциональных параметров (ну, почти), либо variadic functions.

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

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

Серьёзной альтернативой управляющих операций ioctl() является чтение/запись информации через псевдоимена файловой системы /proc и /sys

Олег Цилюрик

http://psihomed.com/grafomaniya/

ох...шь open/close делать и разбираться куда чего писать, при том что проблема

не реализован тщательнейший контроль данных

никуда не денется.

https://github.com/torvalds/linux/blob/master/Documentation/ABI/obsolete/sysf...

This ABI is deprecated and will be removed after 2020. It is

replaced with the GPIO character device.

разработчики чета не в восторге от такой «альтернативы»

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

разработчики чета не в восторге от такой «альтернативы»

Были бы не в восторге - выпилили бы весь sysfs. А тут тупо у говнокодеров разбери-пи тормозил.

anonymous ()

Что-то мне кажется ты пытаешься сделать единообразными совершенно разные по сути вещи. А это плевок в сторону ООП. Глянь сторону Питона или чего-то подобного.

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

gRPC ищешь ты.

нет, но спасибо за годную инфу :)

Sahas ★★★★★ ()
Ответ на: комментарий от i-rinat

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

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

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

Набор параметров известен на момент компиляции? Если да, то можно просто вызывать функцию - обычную или виртуальную.

известен, но некоторые из них опциональные (могут быть не заданы)

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

скажи сейчас :) Вызов различных функций хочется реализовать через единый «универсальный» интерфейс

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

Это общая проблема при форматировании. Если в сообщении может использоваться любой байт 0-255, то какой бы не был выбран разделитель, будет пересечение с данными. Поэтому, например, в url в параметрах символ & нужно кодировать особо.

i-rinat ★★★★★ ()
Ответ на: комментарий от Sahas

Можно упороться и воспользоваться protobuf :)

Функция всегда принимает только один аргумент — Message определенного типа. Это тип можно выводить шаблоном и динамически парсить в него text/json-представление этого message.

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

известен, но некоторые из них опциональные (могут быть не заданы)

В C++ опциональные аргументы можно передавать несколькими способами. Я бы рекомендовал обратить внимание на два:
1. Уникальность функции определяется не только ее именем, но и количеством аргументов. То есть ты можешь задавать разную имплементацию в зависимости от количества аргументов.
2. Параметрам функции можно задать дефолтные значения.

#include <iostream>

void fn(std::string str, int i=0)
{
        std::cout << "called fn('" << str << "', " << i << ")" << std::endl;
}

void fn()
{
        std::cout << "called fn()" << std::endl;
}

int main()
{
        fn("test", 5);
        fn("test");
        fn();

        return 0;
}

$ g++ test.cpp && ./a.out
called fn('test', 5)                                                                                                                                                             
called fn('test', 0)
called fn()


скажи сейчас :) Вызов различных функций хочется реализовать через единый «универсальный» интерфейс

Давай сначала решим вопрос передачи произвольных наборов данных.

То, что тебе нужно, называется сериализация. Дальше нужно выбрать формат/протокол. Сейчас популярен JSON. Раньше был XML. Есть другие, например Google Protocol Buffers. Как видишь, уже давно есть множество реализаций сериализации/десериализации под типовые форматы. Ссылки на примеры я дал, но лучше погуглить побольше по ключевым словам, например, C++ object serialization json. Валидаторы также имеются, например для JSON.

Что до интерфейса - я так и не понял архитектуру.

Если это микросервисы, то, например, REST может быть вариантом. Но у REST свои недостатки; например, тот, что заточен под работу с объектами - создание, модификация, и т. п., а если нужно что-то шире, то приходится извращаться. Если у тебя произвольные функции, то гугли на тему RPC; вариантов много.
Посмотри вот это

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

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

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

D-Bus, XML-RPC, SOAP, WSDL, REST.

Но ведь в основе всех этих технологий стоит передача всех параметров в виде одной строки, которая парсится на вызываемой стороне.

В чем принципиальное отличие от того, что есть у ТС?

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

Именно так. Парсить её в любом случае надо, вопрос — в какой части программы?

В парсере. </thread>

t184256 ★★★★★ ()
Ответ на: комментарий от deep-purple

А сколько всего у тебя этих разных параметров? 10-20?

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

Sahas ★★★★★ ()

Поздравляю, ты изобрел сериализацию. Не мучайся, возьми готовую реализацию какую-нибудь )

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

Что до интерфейса - я так и не понял архитектуру.

Грубо говоря так, как описано в ОП: условно есть программа, которая считывает скриптовый файл и вызывает написанные в этом файле функции с передачей им параметров, которые считываются из этого же файла. Реализация этих функций сделано в виде виртуального класса, где виртуальная функция «call_function» принимает, условно говоря, одну строку со списком параметров, которую парсит внутри себя конкретная реализация этой функции.

Передавать параметры в виде JSON, XML или ini (как у меня сейчас) — не принципиально, но спасибо за советы :)

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

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

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

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

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

ну, почитай теорию, что ли :) тебе всё равно придётся обрабатывать всякие там циклы, переходы при выполнении кода внутри скриптов, возврат из функций. а это всё делается через стек.

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

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

deep-purple ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.