С Новым годом! Форум программистов, компьютерный форум, киберфорум
C++
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.86/7: Рейтинг темы: голосов - 7, средняя оценка - 4.86
0 / 0 / 0
Регистрация: 18.04.2019
Сообщений: 32

Указатель на функцию с произвольным числом/типо аргументов, описанных строковой декларацией

21.07.2023, 14:27. Показов 1560. Ответов 10
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Доброго времени. Обращаюсь к форуму в связи с тем, что не могу даже загуглить вопрос ввиду сложностей его формулировки. Хочу получить что-то вроде этого:

C++
1
2
3
4
5
6
void func(int a, int b, std::string s)
{
//......
}
 
context->registerFunction(func, std::string("iis"));
Чтобы передавать контексту указатель на функцию и описание её параметров в виде строковой переменной в динамическом виде для дальнейшего запуска с передачей соответствующих параметров (отдельные переменные класса context).

Число параметров и их тип известен из декларации. Как это можно реализовать?

Конечной целью является упрощение и уменьшение объема кода, чтобы не расскидывать вручную аргументы для каждой функции.
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
21.07.2023, 14:27
Ответы с готовыми решениями:

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

Оформить функцию, принимающую в качестве аргументов массив целого типа, его размер и указатель на функцию
Дан массив целых. Оформить функцию count_where, принимающую в качестве аргументов массив целого типа a, его размер n и указатель на функцию...

Написать функцию с переменным числом аргументов
Помогите понять, что от меня хотят-то вообще. Мне нужно: объявить функции в соответствии с вариантом. Определить их. мой вариант:написать...

10
267 / 199 / 30
Регистрация: 26.11.2022
Сообщений: 866
21.07.2023, 14:34
так не делают
особенно если вы будете вызывать функцию с неизвестным количеством и типом аргументов - ибо есть соглашение о передаче параметров и оно для каждой архитектуры свое. и на этапе компиляции компилятору надо знать какой аргумент и куда сохранить чтобы передать в функцию.

опишите зачем вам это надо - может что и вспомним как делать.
1
0 / 0 / 0
Регистрация: 18.04.2019
Сообщений: 32
21.07.2023, 15:14  [ТС]
Aledveu,
Пишу компилятор-интерпретатор. Из-за чего приходится реализовывать такие решения:

C++
1
2
3
4
5
6
7
8
9
10
11
12
    
    vmsp::virtual_machine m_vm;
    m_vm.create();
    m_vm.function_handler = function_handler;
 
        m_compiler.m_functions =
        {
        { "abc", vmsp::function_data(func_id::abc, "ii") },
        { "fixme", vmsp::function_data(func_id::fixme , "i") },
        { "test",  vmsp::function_data(2, "s") },
        { "laplas", vmsp::function_data(3, "siss") }
        };
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void function_handler(int id, vmsp::virtual_machine* p_vm)
{
    switch (id)
    {
    case 2:
    {
        p_vm->m_iregister[0] = 0;
        std::cout << "fixme: " << std::endl;
        break;
    }
    case 3:
    {
        p_vm->m_iregister[0] = p_vm->m_iarg[0] * p_vm->m_iarg[0];
        std::cout << "abc: " << std::endl;
        break;
    }
    }
}
В обработчике функций в switch идет индекс функции и под case её реализация (пока без обертки самой функции). И именно обработчик мне и не нравится, поскольку строится по простейшему линейному алгоритму вручную, что довольно грузно, когда число функций станет от нескольких десятков. Его задача заключается в вызове нужной функции и передаче параметров виртуальной машины из регистров m_iarg[] и m_string[], где нумерация массива идет от индекса аргумента.
0
267 / 199 / 30
Регистрация: 26.11.2022
Сообщений: 866
21.07.2023, 15:40
Нехорошее решение. Ибо нет возможности добавлять функции просто, а так же из плагинов и пр. и нет возможности для отладки - ибо параметры надо как-то просматривать.

предлагаю классический вариант - введите тип данных что-то типа "аргумент", который будет описывать один из параметров. Причем в этим типе будет много служебной информации - имя переменной в интерпретируемой программе, её тип, и пр.
И соответствуйющиц набор функций-обработчиков, каждая из которых принимает массив этих "аргументов".
0
0 / 0 / 0
Регистрация: 18.04.2019
Сообщений: 32
21.07.2023, 16:22  [ТС]
Aledveu, Это не интерпретатор высокоуровневого языка. Сишный код перегоняется компилятором до ассемблера, а далее до интерпретируемого байткода по типу x86. Тот же стек, регистры, флаг компаратора и т.д. Можно, конечно, перегонять std::to_string/std::stol для каждого нестрокового аргумента функции перед вызовом, но там другие усложнения.
0
267 / 199 / 30
Регистрация: 26.11.2022
Сообщений: 866
23.07.2023, 19:18
тоесть вы пишите с компилятор?
ну так возьмите готовый типа https://github.com/rui314/chibicc или https://bellard.org/tcc/ и экспериментируйте.
или с++ ?
0
0 / 0 / 0
Регистрация: 18.04.2019
Сообщений: 32
24.07.2023, 02:24  [ТС]
Aledveu, я пишу скриптовый движок. Компилятор идет отдельной частью кода, с ним проблем нет, он конвертирует скрипты в байткод, который исполняется кодом виртуальной машины. Виртуальная машина извлекает из байткода переменные и вызывает функции с определенным индексом. И я не придумал ничего лучше, кроме как реализовать их обработку в виде отдельной функции-обработчика с switch-case, который придется набивать передачей аргументов вручную.
0
267 / 199 / 30
Регистрация: 26.11.2022
Сообщений: 866
24.07.2023, 02:49
если у вас строгая типизация в вашем скриптовом движке - воспользуйтесь тем же соглашением о вызовах что и в С.
вспомните как это было раньше - https://en.wikipedia.org/wiki/... onventions
cdecl делался именно так
компилятор проверял соответствие типов и складывал параметры в стек. и всё.

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

в результате - вы всё равно напишите С компилятор.
0
0 / 0 / 0
Регистрация: 18.04.2019
Сообщений: 32
24.07.2023, 03:15  [ТС]
Aledveu, да нет же, вы все путаете. Про коддинг компилятора в контексте данной темы можно вообще забыть, он не имеет отношения к моему вопросу.

Я сделал и компилятор/интерпретатор в виде отдельной библиотеки/инклудов. Чтобы в других проектах на С/C++ его как можно проще прикручивать. И в этом вся проблема. Нужно каким-то образом вызывать внешние функции, не ковыряя кода инклудов интерпретатора. Виртуальная машина загружает регистры и индекс функции, в моем случае запускает function_handler и там уже приходится запускать switch-case в нужные функции и пихать регистры в их аргументы.
0
 Аватар для Annemesski
2670 / 1333 / 479
Регистрация: 08.11.2016
Сообщений: 3,683
24.07.2023, 09:57
Если правильно понял что нужно вот тяп-ляп реализация на "подумать" и распрямить и развить для своей задачи. Писал чисто по приколу:

header.h

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#ifndef HEADER_H
#define HEADER_H
 
#include <string>
#include <sstream>
#include <memory>
#include <map>
#include <fstream>
 
class IProcessorFace
{
    const std::string mRef;
public:
    IProcessorFace(const std::string &reference) : mRef(reference) { }
    virtual ~IProcessorFace() { }
 
    const std::string & reference() const { return mRef; }
 
    virtual std::string usage() = 0;
    virtual std::string operate(const std::string &args) = 0;
};
 
class FMessage : public IProcessorFace
{
public:
    FMessage(const std::string &ref) : IProcessorFace(ref) { }
 
    std::string usage() override { return reference() + " <message>"; }
    std::string operate(const std::string &msg) override { return msg; }
};
 
class FSum : public IProcessorFace
{
public:
    FSum(const std::string &ref) : IProcessorFace(ref) { }
    std::string usage() override { return reference() + " <operand_a> <operand_b>"; }
    std::string operate(const std::string &args) override
    {
        std::istringstream is(args);
        int ops[2];
        is >> ops[0] >> ops[1];
 
        return std::to_string(ops[0] + ops[1]);
    }
};
 
class FFileSave : public IProcessorFace
{
public:
    FFileSave(const std::string &ref) : IProcessorFace(ref) { }
    std::string usage() override { return reference() + " <file_name> [content]"; }
    std::string operate(const std::string &args) override
    {
        std::istringstream is(args);
        std::string tmp;
        is >> tmp;
        is.ignore();
        std::ofstream ofs(tmp, std::ios::out | std::ios::trunc);
 
        if (ofs.is_open()) {
            std::getline(is, tmp);
            ofs.write(tmp.c_str(), tmp.size());
            ofs << std::endl;
            ofs.close();
            return "done!";
        } else {
            return "error open file: \"" + tmp + "\": ";
        }
    }
};
 
class Shell
{
    std::map<std::string, std::unique_ptr<IProcessorFace>> mFuncs;
public:
    void registrate(IProcessorFace *functor) { mFuncs[functor->reference()] = std::unique_ptr<IProcessorFace>{ functor }; }
    void run(const std::string &exit_comand);
 
    std::string help(void);
};
 
#endif // HEADER_H
header.cpp

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include "header.h"
 
#include <iostream>
 
void Shell::run(const std::string &exit_command)
{
    std::string com;
    while (com != exit_command) {
        std::cout << "shell> ";
 
        std::string line;
        std::getline(std::cin, line);
        std::istringstream is(line);
        is >> com;
        is.ignore();
 
        std::string args;
        std::getline(is, args);
 
        if (mFuncs.count(com)) {
            std::cout << mFuncs[com]->operate(args) << std::endl;
        } else if (com == "help") {
            std::cout << help() << std::endl << exit_command << std::endl;
        } else if (com == "usage") {
            if (mFuncs.count(args)) {
                std::cout << mFuncs[args]->usage() << std::endl;
            } else {
                std::cout << "Unknown command \"" << args << "\"" << std::endl;
            }
        } else if (com != exit_command) {
            std::cout << "Unknown command \"" << com << "\"" << std::endl;
        }
    }
}
 
std::string Shell::help()
{
    std::string ret;
    for (auto &f : mFuncs) {
        ret.append(f.second->reference());
        ret.append("\n");
    }
 
    return ret;
}
Пример использования
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
#include "header.h"
 
int main()
{
    Shell shell;
    shell.registrate(new FMessage("mesg"));
    shell.registrate(new FSum("sum"));
    shell.registrate(new FFFileSave("fsave"));
 
    shell.run("exit");
 
    return 0;
}
0
 Аватар для zayats80888
6352 / 3523 / 1428
Регистрация: 07.02.2019
Сообщений: 8,995
30.07.2023, 15:31
Цитата Сообщение от Хостес Посмотреть сообщение
Число параметров и их тип известен из декларации. Как это можно реализовать?
Конечной целью является упрощение и уменьшение объема кода, чтобы не расскидывать вручную аргументы для каждой функции.
Реализовать можно через шаблоны.
А вот что касается "не раскидывать вручную аргументы", если это относится к "раскидыванию на основе строкового описания",
то это вам виднее, я же не знаю, как вы хотите передавать аргументы(откуда их брать) и что из себя представляют ваши регистры.
Цитата Сообщение от Хостес Посмотреть сообщение
Нужно каким-то образом вызывать внешние функции, не ковыряя кода инклудов интерпретатора.
Тогда какой код вы хотите изменить? Как сейчас там реализован "вызов внешних функций"? В вашем function_handler я не вижу ни одного вызова.

Небольшой демонстрационный пример, как это можно автоматизировать при помощи шаблонов.
Тут только идея, для конечного безопасного использования придется много "допиливать".
c++17
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#include <any>
#include <unordered_map>
#include <string>
#include <functional>
 
class vm
{
    static constexpr size_t num_registers = 8;
 
    // Регистры.
    // Регистр с индексом 0 зарезервирован под
    // возвращаемый результат (не реализованно).
    std::any m_registers[num_registers];
 
    std::unordered_map<
        std::string, // идентификатор
        std::pair<
            std::string,          // описание аргументов
            std::function<void()> // функция
            >>
        m_functions;
 
    void put_args_from_description(std::string const &/*description*/)
    {
        // Какой-то алгоритм заполнения регистров аргументами для вызова функции
        // на основании строкового описания.
        // Например, цикл по символам и последовательное распихивание
        // соответствующих аргументов из типизированных хранилищ.
        //...
    }
 
    // Хэлпер для выбора правильной перегрузки std::any_cast
    template <class T>
    using any_ref = std::conditional_t<std::is_lvalue_reference_v<T>, std::any &, std::any &&>;
 
    // Фактически вызываемая функция, указатель на инстанс которой
    // опосредованно сохраняется.
    template <class R, class... Args, size_t... Idx>
    void invoke_impl(R (*pfn)(Args...), std::index_sequence<Idx...>)
    {
        std::invoke(pfn, std::any_cast<Args>(static_cast<any_ref<Args>>(m_registers[Idx + 1]))...);
    }
 
    // Автоматически заполняет ригистры
    // (для демонстрационного метода vm::call_with_args)
    template <class... Args, size_t... Idx>
    void put_args_impl(std::index_sequence<Idx...>, Args &&...args)
    {
        static_assert(
            sizeof...(Args) < num_registers,
            "the number of arguments exceeds the number of available registers");
 
        (m_registers[Idx + 1].emplace<Args>(std::forward<Args>(args)), ...);
    }
 
public:
    // Регистрирует указатель на функции по имени
    // и строковым описанием аргументов (последнее не ниспользуется).
    template <class R, class... Args>
    void register_function(std::string name, R (*pfn)(Args...), std::string description)
    {
        static_assert(
            sizeof...(Args) < num_registers,
            "the number of arguments exceeds the number of available registers");
 
        m_functions.emplace(
            std::move(name),
            std::make_pair(
                std::move(description),
                [this, pfn]
                {
                    this->invoke_impl(pfn, std::index_sequence_for<Args...>{});
                }));
    }
 
    // Вызов зарегистрированной функции по имени.
    // предполагается, что корректные аргументы были положены в регистры,
    // например, на остновании строкового описания:
    //   put_args_from_description(m_functions.at(name).first));
    void call(std::string const &name)
    {
        m_functions.at(name).second();
    }
 
    // Вызов с аргументами для демонстрации.
    template <class... Args>
    void call_with_args(std::string const &name, Args &&...args)
    {
        put_args_impl(std::index_sequence_for<Args...>{}, std::forward<Args>(args)...);
        call(name);
    }
};
 
#include <iostream>
 
void foo(int i, std::string const &s)
{
    std::cout << "foo: " << i << ' ' << s << '\n';
}
 
void bar(void (*pfn)(int, std::string const &), int &i, std::string const &s)
{
    std::cout << "this call from bar:\n\t";
    pfn(i, s);
}
 
int main()
{
    try
    {
        vm ctx;
        ctx.register_function("foo", foo, "");
        ctx.register_function("bar", bar, "");
 
        // Типы аргументов должны строго соответствовать
        // требуемым функции, т.е. никаких неявных преобразований
        // делаться не будет при извлечении.
        // Чтоб реализовать такую возможность, нужно дополнительно сохранять
        // типы фактических аргументов, чтоб корректно их изъять из регистров,
        // сейчас типы извлекаются на основе "формальных параметров" функции.
        // В случае ошибки вылетит исключение std::bad_any_cast.
        // Возможность передать аргументы по ссылке не реализована,
        // эти аргументы сохраняются в регистрах по значению,
        // но значения в регистрах могут изменяться функцией.
        ctx.call_with_args("foo", 42, std::string("hello foo"));
        ctx.call_with_args("bar", &foo, 33, std::string("hello bar"));
    }
    catch (std::exception const &e)
    {
        std::cerr << e.what() << std::endl;
    }
}
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
30.07.2023, 15:31
Помогаю со студенческими работами здесь

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

Запишите объявления указателя на массив с 5х10 указателей на функцию без аргументов, которая возвращает указатель на зна
Запишите объявления указателя на массив с 5х10 указателей на функцию без аргументов, которая возвращает указатель на значение типа int....

exec с произвольным количеством аргументов
Никак не могу разобраться с работой exec. Задание разработать программку с использованием функции из семейства exec, чтобы она принимала...

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

Функции с произвольным количеством и типом аргументов.
Начал изучать ф-ции с определенным количеством параметров и при изучении возник вопрос, а именно: #include &lt;iostream&gt; using...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
11
Ответ Создать тему
Новые блоги и статьи
Восстановить юзерскрипты Greasemonkey из бэкапа браузера
damix 15.01.2026
Если восстановить из бэкапа профиль Firefox после переустановки винды, то список юзерскриптов в Greasemonkey будет пустым. Но восстановить их можно так. Для этого понадобится консольная утилита. . .
Изучаю kubernetes
lagorue 13.01.2026
А пригодятся-ли мне знания kubernetes в России?
Сукцессия микоризы: основная теория в виде двух уравнений.
anaschu 11.01.2026
https:/ / rutube. ru/ video/ 7a537f578d808e67a3c6fd818a44a5c4/
WordPad для Windows 11
Jel 10.01.2026
WordPad для Windows 11 — это приложение, которое восстанавливает классический текстовый редактор WordPad в операционной системе Windows 11. После того как Microsoft исключила WordPad из. . .
Classic Notepad for Windows 11
Jel 10.01.2026
Old Classic Notepad for Windows 11 Приложение для Windows 11, позволяющее пользователям вернуть классическую версию текстового редактора «Блокнот» из Windows 10. Программа предоставляет более. . .
Почему дизайн решает?
Neotwalker 09.01.2026
В современном мире, где конкуренция за внимание потребителя достигла пика, дизайн становится мощным инструментом для успеха бренда. Это не просто красивый внешний вид продукта или сайта — это. . .
Модель микоризы: классовый агентный подход 3
anaschu 06.01.2026
aa0a7f55b50dd51c5ec569d2d10c54f6/ O1rJuneU_ls https:/ / vkvideo. ru/ video-115721503_456239114
Owen Logic: О недопустимости использования связки «аналоговый ПИД» + RegKZR
ФедосеевПавел 06.01.2026
Owen Logic: О недопустимости использования связки «аналоговый ПИД» + RegKZR ВВЕДЕНИЕ Введу сокращения: аналоговый ПИД — ПИД регулятор с управляющим выходом в виде числа в диапазоне от 0% до. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru