Войти
ПрограммированиеФорумОбщее

Движок на Си (14 стр)

Advanced: Тема повышенной сложности или важная.

Страницы: 113 14 15 16 17 Следующая »
#195
(Правка: 1:41) 1:39, 27 июня 2019

u960
> Коллеги, а есть ли возможность вызвать функцию с произвольным количеством
> аргументов?

typedef void (*function_t)(); 

static                                             
void f1(int a)
{
    printf("%s:%d: '%s' : %ul\n", 
           __FILE__, __LINE__, __func__, a); 
}


static                                             
void f2(int a, int b)
{                                                  
    printf("%s:%d: '%s' : %ul, %ul\n",
           __FILE__, __LINE__, __func__, a, b); 
}                                                  


int main(int argc, char* argv[]) 
{                                                  
    function_t func = f1;           
    func(123);                                     
    
    func = f2;                                     
    func(321, 321);                                
}

#196
2:23, 27 июня 2019

exchg
ого, да это же основа для event-driven
в своё время по незнанию использовал boost::bind/boost::apply

#197
3:39, 27 июня 2019

#!
> в своё время по незнанию использовал boost::bind/boost::apply

Используя С++ ты лишаешься этой магии - он запретит вызывать func от непонятного числа аргументов. Там func(void) есть то же самое что и func(). Это называется строгая типизация.
В Си же от рождения были некоторые "проблемы" со строгой типизацией. Си тогда во многом был транслятором наподобие ассемблеров и вызов функции был лишь одним из способов использовать символ (именованную метку) и название этой метки на момент вызова не обязано было быть известным.
Т.е. компилятор мог вызвать в любом месте модуля трансляции функции вообще не зная что она такое и есть ли она вообще. Проблему того есть ли она вообще решал на втором этапе компиляции линкер - просто вставлял адреса меток в уже откомпилированный код.
И вот тут возникла проблема - как вызвать функцию вообще не имея на руках ни её имени ни параметров? И тут Си сделал очень просто - все параметры по сути приводились к int (точнее - слову) и важно было соблюсти только их число и чтобы они влезли в слово - первое должен был обеспечить программист (и как мы видим выше - мог и не обеспечивать), а второе - отсутствие возможности передачи в параметрах сложных структур данных - на структуры и массивы можно было передавать только указатели на них, которые тоже укладывались в слово. char же просто расширялся до int.
Ну и третье - подчищать стек от переданных параметров должна была не вызванная функция, а вызывающий код, тогда даже если он передаст больше параметров чем надо - это может не привести к падению программы в рантайме если в первых не передана чушь. Кроме того всегда считалось, что функция возвращает опять же слово, а если не возвращала по факту, то просто мусорное значение.

Остатки этого поведения мы видим в функции main которую можно объявлять даже в С++ как угодно - с параметрами или без и даже менять тип возвращаемого аргумента, это всё в некоторых рамках считается допустым компилятором C++. Ранние компиляторы Си же вообще не делали ничего специального для такого поведения - оно было основой их работы.

В общем когда повводили типов данных не укладывающихся в int и устали отлавливать блохи в падениях программы на ровном месте из-за неправильно переданных в функции параметров - тогда эти все фишки существенно изменили и подрезали, но остатки такого поведения в Си в режиме совместимости и в особых случаях (например есть разница между func(void) и func() - второе это "неспицифицированные параметры") остатки его сохранились.

#198
7:50, 27 июня 2019

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

int f1(int a)
int f2(int *a)
int f3(void)
int f4(int b, int c)
int f5(Mesh* m)

в вашем случае вы руками проставляете параметры сами
func(123);                                   
func(321, 321); 

#199
8:37, 27 июня 2019

u960
> Можно ли в "автоматическом" режиме, "другим кодом", вызвать список функций, с
> разным колвом и типом аргументов.

А откуда эти параметры да еще и разные берутся?

#200
10:23, 27 июня 2019

=A=L=X=
> А откуда эти параметры да еще и разные берутся?

Задача-желание были такие:
Есть очень много разных механик, и для каждой механики нужны разные данные, разные ресурсы.

init_cars(max_cars, cars_models)
init_specials_monsters(void)
int_crash_walls(player, blast_effect)
и .тд
и так же функции логики-отрисовки, если что специальное для данной механики.

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

Можно конечно в лоб, и все в таком духе:

if (level == 10) {
    init_cars(max_cars, cars_models)
} else if (level == 5) {
    int_crash_walls(player, blast_effect)
}
else
....
и тд

#201
(Правка: 12:18) 11:51, 27 июня 2019

#!
> ого, да это же основа для event-driven
Да, вполне.

> в своё время по незнанию использовал boost::bind/boost::apply
В C++ этого нету. В С++ func() это аналог func(void) в Си. С некоторым эффектом в С++ ты можешь использовать func(...) для таких целей, но придется список аргументов раскручивать руками.

=A=L=X=
> В Си же от рождения были некоторые "проблемы" со строгой типизацией.
Не может быть проблем с тем чего нету.

u960
> Можно ли в "автоматическом" режиме, "другим кодом", вызвать список функций, с
> разным колвом и типом аргументов
Тогда я повторю вопрос =A=L=X=
> А откуда эти параметры да еще и разные берутся?

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

#202
(Правка: 16:49) 16:32, 27 июня 2019

exchg
> Если ты имеешь в виду некий диспетчер по можешь прокидывать в выбранную функцию
> аргументы приходящие снаружи через va_list
обобщённая диспетчеризация она немного по-другому устроена:
1. связываем аргументы с функцией которая должна их обработать
2. кладём получившееся в контейнер, для событий обычно очередь, но вот у u960 это может быть просто массив или хеш-таблица
3. просто дёргаем функцию

я вот пока навскидку вижу затруднения с п.3. т.к. остатки типизации в Си могут не давать вызывать подобным образом:

int f5(Mesh* m);
uint8_t buffer[100500];
memcpy(buffer, m, meshSize);
f = f5;
f(buffer);
хотя.. это у меня зашквар, так всё работает т.к. сигнатура f5 оказывается скрытой и ругаться не на что

#203
(Правка: 17:43) 17:10, 27 июня 2019

#!
> 1. связываем аргументы с функцией которая должна их обработать
> 2. кладём получившееся в контейнер, для событий обычно очередь, но вот у u960
> это может быть просто массив или хеш-таблица
> 3. просто дёргаем функцию
Ну так я не совсем понимаю что нужно то в итоге. Но если то, что выше, то можно примерно вот так:

typedef struct disp
{
    void (*callback)();
    void* arg;
} disp_t;


typedef struct arg1
{
    int count;
    char* name;
} arg_func1_t;


typedef struct arg2
{
    double value[3];
} arg_func2_t;


static void func1(arg_func1_t* a);
static void func2(arg_func2_t* a);


static disp_t table[] =
{
    {func1, &(arg_func1_t){.count = 123, .name = "some name"}},
    {func2, &(arg_func2_t){.value = {.1, .2, .3}}},
    {func1, &(arg_func1_t){.count = 325, .name = "test"}},
};


static
void func1(arg_func1_t* a)
{
    printf("%s:%d: '%s' -> %u:'%s'\n",
           __FILE__, __LINE__, __func__,
           a->count, a->name
        );
}

static
void func2(arg_func2_t* a)
{
    printf("%s:%d: '%s' -> [%f, %f, %f]\n",
           __FILE__, __LINE__, __func__,
           a->value[0], a->value[1], a->value[2]
        );
}


int main()
{
    size_t u;
    for (u=0; u<3; ++u)
        table[u].callback(table[u].arg);
}

Эт чисто как иллюстрация идеи. А даьше добавляй сколь нужно любых функций со своими любыми аргументами.

#204
(Правка: 18:00) 17:54, 27 июня 2019

exchg
> можно примерно вот так
по мне так то что надо, пожалуй даже запишу себе в блокнотик

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

#205
(Правка: 19:59) 19:34, 27 июня 2019

Всё ещё есть возможность вызывать функции, складывая аргументы в стёк ручками. Правда, для каждой платформы придется реализовывать это самому, и это не сработает с fastcall. Это основа маршаллинга практически в любом скриптовом языке:

#define PUSH_ARG(arg) __asm push arg;
#define INVOKE(func) __asm call func;

...

void marshalFunction(const char* func, script_param_t* params, int param_num)
{
  int i;

  for(i = param_num - 1; i >= 0; i—)
    PUSH_ARG(marshalParam(params[i]));

  INVOKE(func);
}

#206
0:19, 28 июня 2019

exchg
> Ну так я не совсем понимаю что нужно то в итоге. Но если то, что выше, то можно
> примерно вот так:

Вы правильно все поняли, только, хотелось чтобы функции

static void func1(arg_func1_t* a);
static void func2(arg_func2_t* a);

имели раздельные параметры

static void func1(int count, char *name);
static void func2(float a, float b, float c);

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

А так да, потом добавить их в таблицу, список, как в вашем примере.

#207
(Правка: 1:18) 1:02, 28 июня 2019

u960
> хотелось чтобы функции
До нового года еще далеко. )))

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

> А так да, потом добавить их в таблицу, список, как в вашем примере.
Ну покажи как ты их собираешься добавить и хранить в таблице ))))

Вообще если упороться, то можешь посмотреть в <stdarg.h> и окажется, что аргументы раскручиваются банальным макросом. Дальше можешь руками/скриптом упаковать параметры в таблицу подобным манером (это просто линейный список аргументов с рекастами), потом пушить их на стек руками по примеру monobogdan. И это будет работать по крайней мере для 32 бит.. Но такое можно делать ради "любви к искусству" и с ручным "управлением" раскладкой под разные платформы. Если нада для дела, то так не нада делать.

#208
2:23, 28 июня 2019

u960

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

void invoke( variant_array *args, variant *res );
в которой был бы некий маппинг на нативные функции через промежуточный слой кода. в том же DCOM он автоматически генерируется из некоего промежуточного описательного текстового формата (.idl) чтобы легко было поддерживать эту связь между нативным и "скриптовым" кодом. но в DCOM возможны трюки типа ассемблерных вставок, а если мы полностью кроссплатформенны, то видимо придётся генерировать полностью тела этих функций в каждом конкретном случае подставляя потом их единого формата указатели в лукап-таблицу. Хотя тут есть возможность генерировать только одну функцию-распрыжку для каждой спецификации функции. Так или иначе вызов такой функции через invoke - вещь как и полагается в скриптах сравнительно затратная - каждый раз производится сверка состава и количества аргументов.

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

#209
3:54, 28 июня 2019

u960
> if (level == 10)

геймдиз: Вася а где можно такой-то параметр поменять?
Вася: тут у меня в коде калбэк
геймдиз: #$%%^ в задницу..
+ Показать
Страницы: 113 14 15 16 17 Следующая »
ПрограммированиеФорумОбщее