Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Как передать функции несколько параметров


Автор: YouROK 29.12.2011, 15:26
Есть строка допустим "printf(\"%s %d\"); chars:\"Hello world\", int:25;"

Моя функция парсит строку
получаем строку 
char *paramstr = "%s %d"
и список или что то в этом роде пока еще не определился
1)char* "Hello world"
2)int 25

далее идет вызов
printf(paramstr,тут как-то надо вставить эти параметры);

думал как-нибудь собрать va_list но там работа со стеком, что без ассемблера не сделаешь????

подскажите кто-нибудь, может через boost можно как-то или стандартными способами c++?

Автор: boostcoder 29.12.2011, 15:29
нифига не понял smile 

Автор: YouROK 29.12.2011, 15:35
в программу ко мне по сети приходит строка
 "printf(\"%s %d\"); chars:\"Hello world\", int:25;"
моя программа должна вызвать функцию с этими аргументам
функция одна, а вот аргументы все время разные и количество их тоже меняется.

Автор: boostcoder 29.12.2011, 15:50
Цитата(YouROK @  29.12.2011,  15:35 Найти цитируемый пост)
chars:\"Hello world\", int:25;

распарсив эту часть, ты получаешь массив указателей на токены, и узнаешь кол-во аргументов. далее, итерируйся по списку полученных указателей и выводи при помощи std::cout

Автор: YouROK 29.12.2011, 16:03
Цитата(boostcoder @  29.12.2011,  15:50 Найти цитируемый пост)
выводи при помощи std::cout

printf был взят для примера
мне нужно именно передать все полученные параметры в функцию допустим в 
function1(char*str,...);
или
function1v(char *str, va_list *list);

Автор: boostcoder 29.12.2011, 16:32
так передай в функцию список указателей. в чем сложность?

Автор: bsa 29.12.2011, 16:36
YouROK, такое действительно можно делать или на ассемблере, или перебирать в коде все возможные варианты (т.е. сделать код для всех возможных комбинаций параметров).

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

Автор: boostcoder 29.12.2011, 16:49
Цитата(bsa @  29.12.2011,  16:36 Найти цитируемый пост)
или перебирать в коде все возможные варианты

варианты чего? он получает строки. варианты строк? smile 

Цитата(bsa @  29.12.2011,  16:36 Найти цитируемый пост)
(т.е. сделать код для всех возможных комбинаций параметров)

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

Автор: bsa 29.12.2011, 17:06
варианты наборов аргументов функций.

Автор: YouROK 29.12.2011, 17:48
Видимо придется делать 6 функций с разным количеством параметров, думаю больше 5 аргументов не будет.

Автор: boostcoder 29.12.2011, 19:01
зачем?

Автор: xvr 3.1.2012, 13:53
В gcc есть встроенные функции, которые (судя по названию) умеют делать нечто подобное. См http://info2html.sourceforge.net/cgi-bin/info2html-demo/info2html?%28gcc.info.gz%29Constructing%2520Calls

Автор: feodorv 6.1.2012, 21:10
Теоретически, можно постараться реализовать всё это через va_list. Однако в таком случае передача неверных аргументов может завершить Вашу программу сигфолтом. Как минимум, Вам придётся проверять соответствие форматов в строке printf и передаваемых ей аргументов. То есть разбирать форматную строку.

Я всё же предлагаю реализовать задачу иначе. Простите, запишу всё через struct, легко можно будет перепесать и через классы.
  • завести понятие аргумент функции типа 
    Код

    struct farg
    {
      int type;
      int length;
      union anyvalue value;
    };

    где в anyvalue будут находится значения аргумента в зависимости от его типа type. Очевидные типы - int и string. Можно ввести какие-нибудь свои специфические.
  • функции-обработчики удалённых вызовов определять не через va_list, а через struct farg:
    Код

    rfunction(struct farg *list, int count);

  • собственно список аргументов создавать при парсинге приходящей строки.
  • для реализации printf придётся самому делать разбор форматной строки. Это не так уж и сложно, для начала можно ограничиться тремя форматами - %%, %s, %d, затем добавить обработку желаемых флагов при форматах, потом можно добавить и свои форматы, отсутствующие в стандартной printf и т.д.
  • для унификации вызов собственного printf осуществлять через команду "printf, chars:"%s %d", chars:"Hello world", int:25;", затем просто проверять, чтобы первый аргумент всегда был строкой.
Как-то так...

Автор: xvr 6.1.2012, 23:09
Вариант с va_list имеет еще один недостаток - этот самый va_list очень компиляторно и системное зависим. Недавно столкнулся - на 64х битном х86 этот самый лист не void*, как у всех, а массив (из 1 элемента) весьма развесистой структуры

Автор: 586 7.1.2012, 07:45
Код
#include <stdarg.h>

int my_printf(const char *format, ...)
{
    int n;
    va_list v;
    va_start(v, format);
    n=vprintf(format, v);
    va_end(v);
    return n;
}

int main()
{
    my_printf("%s %d", "abc", -2);
    return 0;
}

Автор: YouROK 13.1.2012, 14:13
feodorv
В том то и дело что функции которые принимают аргументы не мои и их изменить нельзя,
в конце концов сделал парсинг из строки в вектор типа boost::any
и сделал 7 вызовов функций, в зависимости от количества аргументов вызывается та или иная функция.
правда получилось немного коряво, но все работает.

Думаю вопрос интересный, может кто еще предложит идеи.

Интересует как это можно сделать на ассемблере, есть кто разбирается, может выложит хоть что нибудь?
Пусть и архетектурнозависимо

Добавлено через 4 минуты и 5 секунд
Цитата(586 @  7.1.2012,  07:45 Найти цитируемый пост)
#include <stdarg.h>
int my_printf(const char *format, ...)
{
    int n;
    va_list v;
    va_start(v, format);
    n=vprintf(format, v);
    va_end(v);
    return n;
}
int main()
{
    my_printf("%s %d", "abc", -2);
    return 0;
}

Суть в том что количество аргументов("abc",-2 у тебя их два) у меня все время разное

Автор: 586 13.1.2012, 18:26
Цитата(YouROK @  13.1.2012,  15:13 Найти цитируемый пост)
Суть в том что количество аргументов("abc",-2 у тебя их два) у меня все время разное 

В функцию my_printf можно сколько угодно аргументов передать.

Автор: xvr 13.1.2012, 18:33
Цитата(YouROK @  13.1.2012,  14:13 Найти цитируемый пост)
Интересует как это можно сделать на ассемблере, есть кто разбирается, может выложит хоть что нибудь?
Пусть и архетектурнозависимо

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

Автор: 500mhz 13.1.2012, 20:52
ну можно внутри функции сравнить значение стека, и понять сколько аргументов там лежит

пример

Код

изначально к примеру esp = 1000

stdcall MyFunc,arg,arg,arg,arg


MyFunc:
mov ebx,esp     // текуший адрес стека
mov eax,1000  // предыдущий адрес стека
sub eax,ebx     // вычитаем
sub eax,4           // отнимаем 4 так как там кроме аргументов адрес возврата
shr eax,2          // делим на 4 - в случае если у на 4 байтные аргументы
                           // в ЕАХ колво аргументов
.......
ret

Автор: bsa 13.1.2012, 21:32
500mhz, откуда ты взял "предыдущий адрес стека"?

Автор: 500mhz 13.1.2012, 21:45
ну допустим пусть он передаеться в ЕАХ при вызове функции

Автор: bsa 13.1.2012, 23:46
500mhz, интересно, а какой это тип вызова функции такой? что-то я не помню.
А потом, количество и тип аргументов известен на этапе выполнения.

Автор: boostcoder 13.1.2012, 23:54
Цитата(bsa @  13.1.2012,  23:46 Найти цитируемый пост)
количество и тип аргументов известен на этапе выполнения.

нет же.
стек формируется компилятором.

Автор: xvr 14.1.2012, 14:09
Цитата(boostcoder @  13.1.2012,  23:54 Найти цитируемый пост)
нет же.
стек формируется компилятором. 

У ТС задача обратная - у него известны параметры (run-time), надо самому сформировать стек для вызова

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

Нужно просто явно вычислить все параметры (с первого или с последнего, в зависимости от типа вызова), и запихнуть их в стек (командой push). Потом позвать функцию, как void (*)(void). Если тип функции был cdecl, то после возврата надо сбросить стек (через add sp,<size>)


Автор: 500mhz 14.1.2012, 15:28
ха, с gcc не прокатывает он компилит нечто типа

Код

mov [esp+24],arg
mov [esp+20],arg1
call func

может есть какие то ключи для компиляции с push ?

Автор: xvr 14.1.2012, 18:42
Цитата(500mhz @  14.1.2012,  15:28 Найти цитируемый пост)
ха, с gcc не прокатывает он компилит нечто типа

Это неважно - вызываемая функция ожидает найти параметры в стеке, и она их там найдет. То, что они туда попадут не так, как это делает gcc роли не играет, главное что бы ничего не сломалось


Автор: 500mhz 14.1.2012, 19:35
xvr
правильно аргументы на стеке, но сам стек нифига не меняеться 

Автор: xvr 15.1.2012, 14:24
Цитата(500mhz @  14.1.2012,  19:35 Найти цитируемый пост)
но сам стек нифига не меняеться  

gcc заранее резервирует необходимое место на стеке (в прологе функции). Но ничего не мешает это место дополнительно алоцировать в процессе вызова функции (теми самыми push'ами)

Кстати, для __stdcall типа вызова gcc явно резервирует место в стеке (хотя и не с помощью push, но резервирует)
Код

extern void __stdcall func(int,int,int);

void tst(int a, int b, int c)
{
 func(a,b,c);
 func(a,b,c);
}



Код

    .file    "t.c"
    .text
.globl _tst
    .def    _tst;    .scl    2;    .type    32;    .endef
_tst:
    pushl    %ebp
    movl    %esp, %ebp
    subl    $24, %esp
    movl    16(%ebp), %eax
    movl    %eax, 8(%esp)
    movl    12(%ebp), %eax
    movl    %eax, 4(%esp)
    movl    8(%ebp), %eax
    movl    %eax, (%esp)
    call    _func@12

; <<<<<<<<<<<<<
    subl    $12, %esp
    movl    16(%ebp), %eax
    movl    %eax, 8(%esp)
    movl    12(%ebp), %eax
    movl    %eax, 4(%esp)
    movl    8(%ebp), %eax
    movl    %eax, (%esp)
    call    _func@12
    subl    $12, %esp
; >>>>>>>>>>>>>

    leave
    ret
    .def    _func@12;    .scl    2;    .type    32;    .endef


Помечен 2й вызов func, первая же комманда subl $12, %esp  резервирует место в стеке (непосредственно в месте вызова)

Добавлено @ 14:32
Хотя, присмтрелся внимательно - это не резервирование места, а восстановление места (стека), которое сбросит __stdcall функция.

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

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)