Ciekawe zastosowanie operatorów

Odpowiedz Nowy wątek
2011-11-22 23:28
4

Bawiłem się przed chwilą operatorami i wpadłem na ciekawy pomysł, aby zrealizować coś takiego, żeby możliwe było zapisanie kodu w ten sposób i działał on poprawnie:

#include <iostream>
#include "mojeoperatory.h"
int _silnia(int n)
{
  int wynik = 1;
  for (int i=1; i<=n; i++)
    wynik*=i;
  return wynik;
}

int _kwadrat(int n)
{
  return n*n;
}

int _potega(int a, int b)
{
  int c = 1;
  for (int i=0; i<b; i++)
    c*=a;
  return c;
}

#define silnia zprawej(int,_silnia)
#define kwadrat zlewej(int,_kwadrat)
#define potega zobu(int,_potega)

using namespace std;

int main()
{
  int s = 5 silnia;
  int k = kwadrat 3;
  int p = 2 potega 3;
  cout << s << endl;
  cout << k << endl;
  cout << p << endl;
  return 0;
}

i udało mi się :P

oto jak wygląda "magiczny" plik mojeoperatory.h:

template<class T>
struct __tmp1__
{
  __tmp1__(T (*f)(T)): f(f) {}
  T (*f)(T);
  T operator()(T a) { return f(a); }
};

template<class T>
struct __tmp2__
{
  __tmp2__(T (*f)(T,T)): f(f) {}
  T b;
  T (*f)(T,T);
  T operator()(T a) { return f(a,b); }
};

template<class T>
T operator>> (T a, __tmp1__<T> __tmp1__)
{
  return __tmp1__(a);
}

template<class T>
T operator<< (__tmp1__<T> __tmp1__, T a)
{
  return __tmp1__(a);
}

template<class T>
T operator>> (T a, __tmp2__<T> __tmp1__)
{
  return __tmp1__(a);
}

template<class T>
__tmp2__<T> operator<< (__tmp2__<T> __tmp1__, T a)
{
  __tmp1__.b = a;
  return __tmp1__;
}

#define zprawej(typ,funkcja) >> __tmp1__<typ>(funkcja)
#define zlewej(typ,funkcja) __tmp1__<typ>(funkcja) <<
#define zobu(typ,funkcja) >> __tmp2__<typ>(funkcja) <<

nie jest to może zbyt skomplikowane, ale zastosowanie wydaje mi się dość ciekawe :P


░█░█░█░█░█░█░█░█░█░█░█░
edytowany 1x, ostatnio: krwq, 2011-11-22 23:29
pomyslowe, nie powiem. Pytanie tylko, po co? - icek 2011-11-23 00:06
to samo pytanie można zadać do wielu rzeczy. nie wszystko musi mieć sens. - krwq 2011-11-23 00:27
zakładając, że nie musi, to gratuluje :-) Nie wpadlbym na cos takiego. - icek 2011-11-23 00:33

Pozostało 580 znaków

2011-11-23 10:27
0

ponazywałeś wszystko tak, żeby nie można było się rozczytać. na przykład __tmp1__ raz jest typem, a raz zmienną typu __tmp2__.

potęga była źle (sprawdzałem pod VC++ 2010).

template<class T>
struct unary_operator
{
  typedef T (*func_type)(T);
  func_type func;
  unary_operator(func_type f): func(f) {}
  T operator()(T a) { return func(a); }
};

template<class T>
T operator>> (T left, unary_operator<T> right)
{
  return right(left);
}

template<class T>
T operator<< (unary_operator<T> left, T right)
{
  return left(right);
}

#define zprawej(typ,funkcja) >> unary_operator<typ>(funkcja)
#define zlewej(typ,funkcja) unary_operator<typ>(funkcja) <<

template<class T>
struct binary_operator
{
  typedef T (*func_type)(T,T);
  T a;
  func_type func;
  binary_operator(func_type f): func(f) {}
  T operator()(T b) { return func(a,b); }
};

template<class T>
binary_operator<T> operator>> (T left, binary_operator<T> right)
{
  right.a = left;
  return right;
}

template<class T>
T operator<< (binary_operator<T> left, T right)
{
  return left(right);
}

#define zobu(typ,funkcja) >> binary_operator<typ>(funkcja) <<
edytowany 2x, ostatnio: Azarien, 2011-11-23 10:29
na poczatku mialem inne nazwy ale potem wzielem replace na calym tekscie i pozamienialo tez niektore rzeczy ktorych nie chcialem, a nie zwrocilem na to uwagi :P nazwy po prostu miały nie robić żadnych kofliktów z innymi nazwami, a wyszlo jak wyszlo ;p - krwq 2011-11-23 21:42

Pozostało 580 znaków

2011-11-23 12:51
1

osobiście uważam takie rozwiązania za błędogenne.
Przykład: priorytety operatorów w twoim rozwiązaniu nie są zgodne z oczekiwaniem.

cout << 2 silnia; // to chyba się nawet nie skompiluje
int s = 3 * 2 potega 3; // daje zły wynik

Lepiej przeciążyć operatory o wysokim priorytecie.
Dodatkowo twoje rozwiązanie kiepsko obsługuje typy! Co jeśli chcę liczyć potęgę z liczby zmiennoprzecinkowej? Zły wynik bez ostrzeżeń.

Wszystkie te problemy można rozwiązać, więc pomęcz się z tym jeszcze :P


Jeśli chcesz pomocy, NIE pisz na priva, ale zadaj dobre pytanie na forum.

Pozostało 580 znaków

2011-11-23 20:09
0

trudno będzie rozwiązać problem priorytetów, bo „nie wiadomo” jaki priorytet miałoby mieć tworzone właśnie działanie.
przykładowo: chciałbym operator kiszenie o priorytecie słabszym od dodawania, oraz ugniatanie o mocniejszym niż mnożenie ale słabszym od potega.

chyba żeby ograniczyć się do listy kilku dobrze działających operatorów (potega, silnia) bez możliwości szybkiego tworzenia „własnych”.

albo (rozwiązanie pośrednie) do wyboru kilka ustalonych priorytetów (np. additive — priorytet dodawania/odejmowania, multiplicative — mnożenia/dzielenia)

edytowany 3x, ostatnio: Azarien, 2011-11-23 20:15

Pozostało 580 znaków

2011-11-23 20:45
0

Uznanie za chęć ogarnięcia i przemyślenia tego co zrobiłeś. Przyznam szczerze że bardzo ciężko by mi było z szablonami(rzadko z nimi pracuję), więc sporo by to czasu kosztowało. Jedno jest tylko mało fajne - makra. W C++ trzeba uciekać od #define i innych rzeczy z preprocesora - w prywatnym projekcie można tak się bawić ale w czymś większym jest to ryzykowne.


Jeśli uważasz mój post za wartościowy - daj punkt.
Mój post pomógł Ci rozwiązać problem - zaznacz go.

Pozdrawiam

Pozostało 580 znaków

2011-11-23 21:48
0

to było zrobione tylko dla testów, po prostu zastanawiałem się czy takie coś jest możliwe do napisania. Na pewno znajdzie się zawsze coś co spowoduje, że coś zadziała nie tak jak powinno. Miało to działać tylko na prostych operacjach. Na ten pomysł wpadłem, jak zacząłem bawić się definami, żeby zrobić coś w stylu nakładki na C++, która by pozwoliła pisać w naturalnym języku. Tego nie udało mi się do końca zrobić tak jak planowałem, ale przy okazji zrobiłem to co widać :)


░█░█░█░█░█░█░█░█░█░█░█░

Pozostało 580 znaków

2011-11-23 21:52
0

@**Wszyscy wyżej**, nie wiem gdzie widzicie problem, akurat postawienie nawiasu załatwia sprawę priorytetów, a nie jest to wiele pisania.
@Hostel, ale gadasz głupoty. Słyszałeś kiedyś o metaprogramowaniu? Tam do tego także używa się makr. A jest to jedno z najważniejszych rzeczy przy pisaniu gier komputerowych, bo działa w czasie kompilacji a nie w czasie działania programu.
Przykład:

template<unsigned N>
struct _Fib_
{
    enum { Val = _Fib_<N - 1>::Val + _Fib_<N - 2>::Val };
};

template<> _Fib_<0> { enum { Val = 0 }; };
template<> _Fib_<1> { enum { Val = 1 }; };

//Użycie makra
#define FibT( n ) _Fib_< n >::Val

//Wykorzystanie w kodzie
cout << FibT(5);

Co do krwq, to gratuluję, zastanawiałem się zawsze, czy można stworzyć własne operatory, bo zawsze można wymyślić jakiś ciąg działań i nazwać go np @. Dzięki, że wrzuciłeś to na forum ;]


Gdy się nie wie, co się robi, to dzieją się takie rzeczy, że się nie wie, co się dzieje ;-)
edytowany 1x, ostatnio: MJay, 2011-11-23 21:54
nie ma sprawy :P masz jakąś dobrą książkę na temat metaprogramowania? zawsze chciałem trochę ten temat liznąć a na sieci są tylko proste przykłady (ostatno ktoś tutaj wrzucił nawet kod, który praktycznie zacina kompilator :P) - krwq 2011-11-23 22:00
Akurat jedyna jaką do tej pory znalazłem to "Perełki programowania gier tom 1" to jest zaraz na poczatku na stonie 41. Najprostsze przykłady idzie załapać po paru kodach źródłowych ;-) - MJay 2011-11-23 22:08
dzięki bardzo :) - krwq 2011-11-23 22:19
"Thinking in C++" - dobre na początek (ale najnowsza wersja) - vpiotr 2011-11-23 23:20
mam na półce część 1 :P - krwq 2011-11-23 23:32
Chodzi o vol. 2 - jest tam m.in. ten Fibonacci. Do ściągnięcia za darmo ze strony autora: http://www.mindview.net/Books/DownloadSites - vpiotr 2011-11-23 23:35

Pozostało 580 znaków

2011-11-23 23:18
0
MJay napisał(a)

@Hostel, ale gadasz głupoty. Słyszałeś kiedyś o metaprogramowaniu? Tam do tego także używa się makr. A jest to jedno z najważniejszych rzeczy przy pisaniu gier komputerowych, bo działa w czasie kompilacji a nie w czasie działania programu.

C++ jest słabe jeśli chodzi o metaprogramowanie - taka jest prawda. W grach jak już to wykorzystuje się języki skryptowe(np. lua), C++ jedynie wczytuje i odpala skrypt. Jeśli wtedy mamy metaprogramowanie to za pomocą skryptów bo C++ jako tako nie wspiera metaprogramowania. W Qt np. zakodowali metaprogramowanie w środku- możesz zmieniać właściwości obiektu podczas wykonywania. Sam szablon i definicja makro bym nie stosował jako metaprogramowanie w przypadku gdy ktoś może np. nie wiedzieć co dane makro robi i wstawi bubla. Tego typu makra są dobre jedynie by zakodować bebechy do których nikt nie będzie zaglądał. Tak więc wcale nie uważam bym pisał głupoty. W każdej książce i każdy doświadczony programista powie że makra trzeba omijać, chyba że lubimy rwać włosy i ślęczeć w nocy z debuggerem.

@krwq
Mam nadzieję że nie odbierasz moich wypowiedzi negatywnie. Uważam że zrobiłeś fajne ćwiczenie i że to dobra nauka.

Edyta dopisała:
Wracając do metaprogramowania i szablonów - przekombinowanie szablonu, lub błędne użycie(gdy się w dodatku "wie" i "ma rację" że się dobrze użyło) powoduje litanię kompilatora z której bez słownika języka nordyckich bogów nie da się dojść o co chodzi.


Jeśli uważasz mój post za wartościowy - daj punkt.
Mój post pomógł Ci rozwiązać problem - zaznacz go.

Pozdrawiam
edytowany 1x, ostatnio: Hostel, 2011-11-23 23:24
Ogólnie metaprogramowanie w C++ wyszło przez przypadek jak robili template'y, więc nie ma co się komunikatów o błędy czepiać ;). - Zjarek 2011-11-23 23:39

Pozostało 580 znaków

2011-11-24 00:18
0

Zapomniałem dopisać struct:

template <> struct _Fib_< 0 > { enum { Val = 0 }; };
template <> struct _Fib_< 1 > { enum { Val = 1 }; };

Teraz już nie będzie litanii, tylko piękna liczba równa 120.


Gdy się nie wie, co się robi, to dzieją się takie rzeczy, że się nie wie, co się dzieje ;-)

Pozostało 580 znaków

2014-08-23 18:52
0

A da się tak zrobić z "obiektami"?


~~

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

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