Funkcje wywoływane przez uzytkownika

0

Mam pytanie jeśli np mam w programie 20 funkcji i każda przyjmuje jakiś tam parametr i użytkownik może je wywołać za pomocą wpisania jej nazwy i parametru, na przykład:
Czekaj 20
i wtedy ma zostać wywołana funkcja Czekaj z parametrem 20. No i mamy te 20 funkcji no to pisanie cały czas if string == nazwa_funkcji i sprawdzanie tak każdej jest pewnie nie najlepszym rozwiązaniem. Czy może zastosowanie tablicy wskaźników na funkcji jest lepszym i poprawnym rozwiązaniem? Na zasadzie porównuje string'a z tablicą wskaźników i jeśli się zgadzają nazwy to ją wywołuje z parametrem?

Z góry dziękuje za wszystkie odpowiedzi.

0

Możesz zrobić sobie mapę odwzorowującą string na wskaźnik do funkcji (o ile funkcje mają ten sam typ!).

0

Załóżmy, ze wszystkie zwracają int, ale przyjmują różne typu parametry, wtedy też to zadziała?

0

Nie bo ich typy są różne, chyba ze wepchniesz je do jakiejś unii, ale tam znów musiałbyś mieć ify, więc odpada.

1

mozesz przerobic kod w taki sposob ze bede przyjmowac jako parametr stos (np ten z stl'a), w kazdej funkcji bedziesz wiedzial jak interpretowac dane na stosie, a parametry bedziesz mogl wrzucac np na podstawie tego stringa ktory przerabiasz. wyjdzie Ci z tego prosty interpreter w zasadzie

0

krwq mógłbyś trochę dokładniej wyjaśnić swój pomysł? Mógłbym to zrobić na zasadzie ifów cały czas ale tak np z 30 if'ów to pewnie nie jest za dobre rozwiązanie.

0

masz przykład, przed chwilą napisałem na szybko:

#include <iostream>
#include <stack>
#include <string>
#include <map>

#define ASSIGN(a) trans[#a] = a;

typedef std::stack<int> stack;
using std::cin;
using std::cout;
using std::string;
using std::endl;
typedef int (*func)(stack);
typedef std::map<string,func> transmap;
transmap trans;
bool error = false;
bool terminate = false;

int suma(stack s)
{
  int sum = 0;
  while (!s.empty())
   {
    sum+=s.top();
    s.pop();
   }
  return sum;
}

int roznica(stack s)
{
  if (s.size()!=2)
   {
     cout << "Zla ilosc parametrow!\n";
     error = true;
     return 0;
   }
  // trzeba pamietac ze parametry na stosie sa w odwrotnej kolejnosci
  int sum = -s.top();
  s.pop();
  sum+= s.top();
  return sum;
}

int wypisz(stack s)
{
  cout << "Ta funkcja nic nie robi poza wypisaniem tego tekstu.\n";
  return 0;
}

int koniec(stack s)
{
  terminate = true;
}

// http://www.codeproject.com/KB/stl/stdstringtrim.aspx
void trim(string& str)
{
  string::size_type pos = str.find_last_not_of(' ');
  if(pos != string::npos) {
    str.erase(pos + 1);
    pos = str.find_first_not_of(' ');
    if(pos != string::npos) str.erase(0, pos);
  }
  else str.erase(str.begin(), str.end());
}

// nie sprawdza czy liczba nie jest za dluga
bool try_convert(string& s, int& num)
{
  //cout << "debug: try_convert: s=XX" << s << "XX" << endl; // <-- zeby bylo biale znaki widac
  trim(s);
  //cout << "debug: try_convert: s=XX" << s << "XX" << endl;
  if (s.size()==0)
    return false;

  const char* p = s.c_str();
  bool neg = false;
  if (*p == '-')
   {
    neg=true;
    ++p;
   }
  num = 0;
  while (*p)
   {
    if (*p<'0' || *p>'9')
      return false;
    num *= 10;
    num += *p - '0';
    ++p;
   }
  if (neg)
    num = -num;
  return true;
}

bool call_func(const string& s)
{
  string func_name;
  string func_params;
  size_t p = s.find_first_of('(');
  size_t r = s.find_last_of(')');
  if (p==string::npos || r==string::npos || p>r)
    return false;

  func_name = s.substr(0,p);
  func_params = s.substr(p+1, r-p-1);
  trim(func_name);
  //cout << "debug:   func_name=" << func_name << endl;
  //cout << "debug: func_params=" << func_params << endl;

  transmap::iterator it = trans.find(func_name);
  if (it==trans.end())
    return false;
  //cout << "debug: doszedlem tu" << endl;

  stack st;
  string param;
  int par;
  while (1) // func_params.size()!=0
   {
    p = func_params.find_first_of(',');
    if (p==string::npos)
     {
      //cout << "debug: func_params=" << func_params << endl;
      trim(func_params);
      if (func_params.size()==0)
        break;
      if (!try_convert(func_params, par))
        return false;
      st.push(par);
      break;
     }
    param = func_params.substr(0, p);
    //cout << "debug: param=" << param << endl;
    if (!try_convert(param, par))
      return false;
    st.push(par);
    func_params = func_params.erase(0, p+1);
   }

  int ret = it->second(st);
  cout << "Funkcja zwrocila: " << ret << endl;
  if (error)
    return false;

  return true;
}

int main()
{
  ASSIGN(suma);
  ASSIGN(roznica);
  ASSIGN(wypisz);
  ASSIGN(koniec);

  string s;

  while(!terminate)
   {
    cout << "> ";
    std::getline(cin, s, '\n');
    if (!call_func(s))
      break;
    cout << endl;
   }
  if (!terminate)
    cout << "\n\nProgram zakonczyl sie, bo:\npodales nieprawidlowa nazwe funkcji,\nzle ja wywolales\nlub wystapil blad w funkcji!" << endl;

  return 0;
}

po włączeniu wpisujesz jedną z nazw funkcji:

suma(1,2,3,4)
roznica(7,5)
wypisz()
koniec()

suma moze miec dowolna liczbe parametrow: liczby calkowite (int)
roznica musi miec dokladnie 2 parametry
wypisz moze miec dowolna liczbe parametrow bo je ignoruje
koniec dow. liczba parm. - wylacza program

zla nazwa funkcji lub nieprawidlowe argumenty spowoduja wylaczenie programu (celowe). funkcje ladnie widac jak dodawac samemu.

0

Oku**a nie spodziewałem się całego kodu, który na dodatek działa. Wielkie dzięki teraz przestudiuję kod i spróbuję to ogarnąć. Podziękowania.

I szybkie pytanko jakie zabiegi muszę wykonać aby dodać na przykład funkcje mnozenie?
Napisałem funkcje mnozenie:

int mnozenie(stack s)
{
	int wynik = 0;
	while (!s.empty())
	{
		wynik*=s.top();
		s.pop();
	}
	return wynik;
} 

I jak wykonuje ASSIGN(mnozenie) to pisze, że funkcja zwraca 0 nieważne dla jakich argumentów, pewnie coś głupiego robię. Wielkie podziękowania za kod.

0

Ale jestem głupi jak dam wynik = 0 to przemnoży przez zero i da zero, dałem wynik = 1 i działa. Naprawdę jestem Ci bardzo wdzięczny.

Przerobię ten kod na moje potrzeby.

0

Dodam, że można "zatrudnić" do tego np. Lua http://www.lua.org/

0

No właśnie tak rozmyślałem nad tym. Muszę to jeszcze przemyśleć.

0

co robią te dwie linie kodu?

typedef int (*func)(stack);
#define ASSIGN(a) trans[#a] = a;
0
typedef int (*func)(stack);

definiuje typ o nazwie func, który jest wskaźnikiem do funkcji która zwraca int i w parametrze przyjmuje zmienną typu stack

#define ASSIGN(a) trans[#a] = a;

definiuje makro o nazwie ASSIGN, które przyjmuje 1 parametr (o nazwie 'a'), makro te generuje linię kodu:
trans[#a] = a;
trans - mapa która mapuje string na funkcje, działa to np tak:
trans["nazwafunkcji"] = nazwafunkcji;

jak widac pierwsza część jest stringiem, a druga funkcją. zamiana nazwy na stringa odbywa się poprzez zastosowanie # w makrze (stringizing operator)

0

Dzeki. Po co tylko tutaj typedef?

typedef int (*func)(stack);
0

dla uproszczenia pozniejszego zapisu typu funkcyjnego. duzo lepiej widac po prostu jak sa wszystkie typy zebrane kolo siebie. w map<string,func> jest to uzyte

0

Niebardzo rozumiem. Wielokrotnie przeglądając kody zródłowe niektórych programów natrafiłem na taki zapis z tym typedef jeśli chodzi o wskaźniki na funkcje jak i struktury i nie bardzo wiedziałem jego sens.

0

przyklad

#include <stdio.h>

typedef int (*func)(int);

int przemnozprzezdwa(int a)
{
  return a*2;
}

int podzielprzezdwa(int a)
{
  return a/2;
}

int main()
{
  printf("podaj liczbe: ");
  int a;
  scanf("%d", &a);
  printf("chcesz pomnozyc czy podzielic przez 2 (0 - pomnozyc, 1 - podzielic)");
  func tab[] = {pomnozprzezdwa, podzielprzezdwa};
  int wybor;
  scanf("%d", &wybor);
  printf("wynik: %d", tab[wybor](a));
  return 0;
}

nie kompilowalem, ale koncepcyjnie jest dobrze

wyobraz sobie ze teraz takich funkcji masz 50, chcialoby Ci sie robic taka drabinke ifow?
teraz sam zapis:
func tab[]

wyglądałby chyba tak:
int (*tab[])()

albo tak:
int (*tab)

albo nie wiem jak :P

0

Dzięki wielkie.Juz kojarzę idee tego.

0

Wie ktoś może jak z tego zrobić funkcje które będą przyjmowały stringi? Rozumiem, że wtedy również typ zwracany przez funkcję musi być std::string?
Gdy pozmieniałem wszystko na string, to się kompiluje, ale gdy wywołuje jakąś funkcję gdzie ma wypisać stringi podane na stack'u to nic nie wyświetla - ktoś ma jakiś pomysł?

0

bo musisz jeszcze zrobic zeby jako tekst traktowane bylo wszystko co w cudzyslowiu jest. funkcja ktora przyjmuje stringa nie musi zwracac stringa. moze zwracac cokolwiek

0

Czyli w call_func muszę dodać znajdowanie "" i to co znajdzie pomiędzy "" to wrzucać jako parametr? Ale chyba nie mogę zrobić funkcji typu int, która przyjmuje stringi i używać w tym kodzie prawda? Dzięki za pomoc, przy okazji bardzo pomocny kod, propsy.

0

Czyli w call_func muszę dodać znajdowanie "" i to co znajdzie pomiędzy "" to wrzucać jako parametr?
tak
Ale chyba nie mogę zrobić funkcji typu int, która przyjmuje stringi i używać w tym kodzie prawda?

możesz, z tym że musisz pamiętać o tej linijce i innych

int ret = it->second(st);

jeśli chcesz parametry mieszane tj. np.: string i int jednocześnie to powinieneś zrobić klasę która przechowuje jeden z tych typów (przeciążyć operatory itp.) wszystko kwestia wyobraźni. generalnie: wszystko da się zrobić

0

Chciałbym po prostu aby ten stack to były stringi oraz żeby funkcje zwracały int'y. Więc typedef'y zmieniam z:

typedef std::stack<int> stack;
using std::cin;
using std::cout;
using std::string;
using std::endl;
typedef int (*func)(stack);
typedef std::map<string,func> transmap;

na:

typedef std::stackstd::string stack;
using std::cin;
using std::cout;
using std::string;
using std::endl;
typedef int (*func)(stack);
typedef std::map<string,func> transmap;

I to tyle + pozbywam się paru linijek w call_func i zmieniam obsługę żeby jako parametr dodawała na stack napis?

0

Dzięki krwq, wszystko działa ładnie.

0

Siema, czy da się w jakiś sposób dodać do tych funkcji jeszcze jeden parametr, ale inny niż ten stos? Np int'a albo jakikolwiek inny typ?

0

a kompilowałeś w ogóle ten program? wiesz po co ten stos tam jest? pytania retoryczne - jakbyś zrobił/wiedział którąkolwiek z tych rzeczy to byś wiedział, że ten stos przechowuje parametry o typie int i stos jest użyty po to, aby można było podać tych parametrów nieograniczoną ilość

0

Źle sformułowałem pytanie, czy mogę oprócz tego stosu int'ów wrzucać również jako np ostatni parametr zupełnie innego typu? Np string?

"jeśli chcesz parametry mieszane tj. np.: string i int jednocześnie to powinieneś zrobić klasę która przechowuje jeden z tych typów (przeciążyć operatory itp.) wszystko kwestia wyobraźni. generalnie: wszystko da się zrobić"

0

w takiej wersji jak jest nie możesz, musisz powiedzieć programowi jak ma wszystko interpretować.
przeanalizuj sobie przykładowe wywołanie, które powinno działać tak jak Ty chcesz (XXX nic nie oznaczają, robie je po to, żeby było widać spacje):

XXXXXXX nazwafunkcji ( 123123 , "asdasdasdasd" , 22222 , "asdasdas" ) XXXXXX

czytasz tak:

  • rozdzielasz stringa na dwa: wszystko przed lewym nawiasem wszystko po lewym nawiasie (jeśli brak nawiasu -> błąd):
    A = XXXXXXX nazwafunkcji XXXXXXX
    B = XXXXXXXX 123123 , "asdasdasdasd" , 22222 , "asdasdas" ) XXXXXX
  • szukasz prawego nawiasu i wraz z nim wszystko co jest na prawo wywalasz (mozesz ewentualnie sprawdzic czy to na prawo to biale znaki):
    A = XXXXXXX nazwafunkcji XXXXXXX
    B = XXXXXXXX 123123 , "asdasdasdasd" , 22222 , "asdasdas" XXXXXX
  • w stringu A wywalasz białe znaki na lewo i prawo i sprawdzasz czy to co zostało jest prawidłową nazwą funkcji:
    A = XXXXXXXnazwafunkcjiXXXXXXX
  • stringa B zaczynasz parsować:
    idąc znak po znaku patrzysz czy pierwsze co napotkałeś to liczba czy "
    w zależności od tego co napotkałeś inaczej interpretujesz zawartość, do Twojej klasy wrzucasz dany parametr i wrzucasz go na stos i parsujesz dalej
0

Po pierwsze bardzo dziękuję za pomoc. Po drugie mi chodziło bardziej o coś takiego:
nazwa_funkcji(stack s,inna_zmienna_innego typu), po prostu abym mógł przekazać do funkcji inną zmienną już nie na zasadzie tego co wpisze użytkownik.

0

to musisz zmienić tylko dwie linijki:

typedef int (*func)(stack,int);
...
int ret = it->second(st,1234);

oraz wszystkie funkcje tak, żeby przyjmowały ten dodatkowy parametr

1 użytkowników online, w tym zalogowanych: 0, gości: 1