vak: (Default)
Serge Vakulenko ([personal profile] vak) wrote2021-02-11 10:46 pm

Помогите с задачкой на Си

Наверное вы знаете, что в юниксе есть такой системный вызов execv(), который выполняет нужный файл с произвольным списком параметров. Вот таким макаром:
int execv(const char *path, char *const argv[]);
А мне нужно нечто похожее, но чтобы вызвать функцию (по указателю) с произвольным списком аргументов. Что-то вроде:
void call(void (*func)(...), int *argv[]);
Функция определена где-то как обрабатывающая фиксированный набор параметров. К примеру, это может быть:
void add(int *a, int *b, int *c)
{
*c = *a + *b;
}
У меня есть указатель на функцию, и массив параметров, количество которых заранее не фиксировано. Хотелось бы вызвать некую функцию call(), которая бы положила нужные параметры на стек и вызвала функцию:
call(func, argv);
Может такое есть уже где-то, а я просто не в курсе?
mopexod: (Default)

[personal profile] mopexod 2021-02-12 07:04 am (UTC)(link)
Я бы начал отсюда:
https://en.cppreference.com/w/c/variadic
Статья про С++, но это сишные штуки.
spamsink: (Default)

[personal profile] spamsink 2021-02-12 07:26 am (UTC)(link)
Такие вещи хорошо на stackoverflow спрашивать.
ircicq: (Default)

[personal profile] ircicq 2021-02-12 07:36 am (UTC)(link)
> call(func, argv);

откуда call усзнает размер массива argv?
если предполагается что все элементы ненулевые то примерно так:

void call(void (*func)(...), int *argv[]) {
    int n = ArgvLen(argv);
    switch (n) {
    case 0:
          ((void *())func)();
          break;

//...
    case 2:
          ((void *(int *, int *))func)(argv[0], argv[1]);
          break;
//...


    }

}


[personal profile] permeakra 2021-02-12 07:55 am (UTC)(link)
для этого делали libffi. Сам не пользовался, но на ней сделан ffi в языках с динамической подгрузкой скомпилированного кода, типа питона.
Edited 2021-02-12 07:56 (UTC)
spamsink: (Default)

[personal profile] spamsink 2021-02-12 08:06 am (UTC)(link)
Если такая универсальная функция и существует, то ей нужно давать указатель на массив слов нативной длины, который можно было бы литерально скопировать в стек и вызвать желаемую функцию. А то мало ли разных типов с разными длинами бывает, а формат стека согласно ABI фиксирован.

Возможно, примерно этого самого может достигнуть сочетание alloca(), копирования правильным способом, и вызова функции без аргументов, плюс-минус несколько ассемблерных команд. Но всегда есть риск, что полученный трюк работает только на данном конкретном ABI, и сломается при первом же удобном случае.

[personal profile] permeakra 2021-02-12 08:39 am (UTC)(link)
Интринсиков в С для этого нет, потому что это ABI-зависимо. А у нас теоретически даже на одной платформе могут существовать функции с разными ABI Если у вас есть контроль над интерфейсом вызываемых функций, то проще всего сделать так, чтобы они принимали указатель на структуру с аргументами и вызывать их через указатели void* (*)(void*). А если нет, то для работы со стеком нужна библиотека, абстрагирующая детали платформы. напр. libffi, в которой для этого самого есть платформо- и ABI-специфичные асмовые вставки.
Edited 2021-02-12 08:42 (UTC)
sab123: (Default)

[personal profile] sab123 2021-02-12 11:47 am (UTC)(link)
На amd64 (и на РИСКах уже давно), вообще-то, первые несколько параметров передаются в регистрах, причем целые и плавающие значения - в разных.

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

[personal profile] sergegers 2021-02-12 08:18 am (UTC)(link)
Для этого в C и введён эллипсис, а без него никак, насколько я понимаю. В C++ это тривиально и даже есть в STL.

[personal profile] sergegers 2021-02-12 08:31 am (UTC)(link)
А, тогда на чистом C или C++ вообще никак. Это ABI dependent.
archaicos: Шарж (Default)

[personal profile] archaicos 2021-02-12 09:52 am (UTC)(link)
Как вызов делается, откуда? Рукописный C-код? Сгенерированная хрень?
Какова однородность типов аргументов?
Какую проблему решаем?

А то можно же было бы в самом простом случае собирать аргументы в массив и евоный адрес передавать.
А массив – можно фиксированного размера, а можно через malloc(). А можно и VLA (хотя, лучше избегать).
Через ... и va_arg() тоже можно. Даже разнообразное.
Но нужно или число аргументов, или признак конца, или ещё что-то, если нет фиксированной схемы.

А можно ещё прикол типа чтобы функция возвращала указатель на функцию... Тогда можно паровозик с вагончиками: f(1)(2)(3)(4). Но это больше для прикола. Разницы между разделением запятыми и скобками не много.

Ещё были трюк, где макрос считал число аргументов.

Занятные вещи, которые могут не пригодится, на пофантазировать:
https://en.cppreference.com/w/c/language/generic
https://en.cppreference.com/w/c/language/compound_literal
norian: (Default)

[personal profile] norian 2021-02-12 10:45 am (UTC)(link)
на крестах коты как-то делали тредпулы, может пригодицца наковырять изюмчегу из

грёбаный хтмл не даёт вставить кусок с темплейтами, но там не очень много кода

https://github.com/norian-n/WorkBox/blob/master/workbox.h
vit_r: default (Default)

[personal profile] vit_r 2021-02-12 10:59 am (UTC)(link)
Просто из соображений безопасности проще положить где-то функцию, содержащую таблицу, переводящую абстрактное в конкретное, а при приходе неизвестной функции или навранного числа параметров, генерирующую ошибку.

Можно выпендритсья и поддерживать таблицу динамически, но это уже перебор.