Virpi Temat założony przez niniejszego użytkownika |
Sprytny switch » 2015-04-27 14:02:52 Cześć. Czy istnieje jakiś sposób na automatyczną inkrementację case'ów w switchu? Chodzi mi o sytuację: QVariant Class::headerData( int _col ) { switch( _col ) { case 0: return trUtf8( "a" ); case 1: return trUtf8( "b" ); case 2: return trUtf8( "c" ); case 3: return trUtf8( "d" ); case 4: return trUtf8( "e" ); case 5: return trUtf8( "f" ); } return QVariant(); }
Jeżeli mam dużo case'ów (załóżmy 20), a chciałbym dodać jeszcze jeden pomiędzy 1 i 2, to okazuje się, że muszę zmienić wszystkie kolejne liczby po 1. Czy da się napisać coś w stylu: QVariant Class::headerData( int _col ) { int a = 0; switch( _col ) { case a++: return trUtf8( "a" ); case a++: return trUtf8( "b" ); case a++: return trUtf8( "c" ); case a++: return trUtf8( "d" ); case a++: return trUtf8( "e" ); case a++: return trUtf8( "f" ); } return QVariant(); }
tak, żeby nie trzeba było po wstawieniu w dowolnym miejscu case'a zmieniać wszystkich następnych? Ograniczeniem switcha jest to, żeby case'y były znane na etapie kompilacji, a tu tak właśnie jest, tylko nie wiem, w jaki sposób i czy w ogóle da się to zapisać. Myślałem nad jakimiś sprytnymi makrami, ale na razie nic ciekawego z tego nie wyszło. Czy macie jakieś pomysły? |
|
Virpi Temat założony przez niniejszego użytkownika |
» 2015-04-27 15:04:24 Znalazłem sposób, niezbyt elegancki, bo wstawienie gdziekolwiek entera wszystko psuje, ale wygląda to tak: #define SWITCH(v) switch(v + __LINE__ + 1) #define CASE case __LINE__
QVariant CQDBWorkstation::field( int _col ) const { SWITCH( _col ) { CASE: return "a"; CASE: return "b"; CASE: return "c"; CASE: return "d"; CASE: return "e"; CASE: return "f"; } return QVariant(); }
I spokojnie można dodać case'a z "ą" i nie trzeba zmieniać pozostałych ;) |
|
pekfos |
» 2015-04-27 15:58:05 Po co w ogóle switch..? if( X ) return trUtf8( F( 'a' + _col ) ); T tab[] = { "a", "b", "c" };
if( X ) return trUtf8( tab[ _col ] ); |
|
Virpi Temat założony przez niniejszego użytkownika |
» 2015-04-27 16:26:19 Zgadzam się, tu jest przykład, gdzie mogę mieć tablicę napisów, ale korzystając z Qt i architektury model-widok potrzebna mi jest również podobna metoda, która w zależności od podanej kolumny zwraca jedno z pól klasy: QVariant Class::field( int _col ) const { switch( _col ) { case 0: return id(); case 1: return name(); case 2: return createId(); case 3: return createTime(); case 4: return modId(); case 5: return modTime(); } return QVariant(); }
Tutaj nie jest już tak łatwo, bo powinienem trzymać wskaźniki na funkcje (więc też wszystkie powinny zwracać QVariant, a tak następuje automatyczna konwersja). No i to są metody klasy, czyli nie można tak po prostu przechowywać wskaźników na nie, bo odwołują się do obiektu this (w Borlandzie załatwiało się to przez __closure). Wiem, że to rozwiązanie nie jest eleganckie, ale gdy kod i struktura danych są w fazie rozwoju to chyba optymalne rozwiązanie. Po ustaleniu można powrócić do normalnego switcha. |
|
pekfos |
» 2015-04-27 16:34:26 Dalej można zastosować tablice, ale switch jest w tym przypadku krótszy. Nic lepszego się raczej nie poradzi. o i to są metody klasy, czyli nie można tak po prostu przechowywać wskaźników na nie |
Nie..? |
|
Virpi Temat założony przez niniejszego użytkownika |
» 2015-04-28 09:18:46 Nie w każdej sytuacji jest to takie proste, np. ten kod: class CFuzzySet : public ICFuzzySet { public: #ifdef __BORLANDC__ typedef void( __closure * setter )( double ); typedef double( __closure * getter )(); virtual double param( std::string _name ) throw( EInvalidName ); virtual TDName2Value params(); virtual void setParam( std::string _name, double _val ) throw( EInvalidName ); #endif virtual CFuzzySignal * parent() const { return FParent; } virtual void setParent( CFuzzySignal * _parent ); virtual void setName( std::string _name ) throw( EInvalidName ); virtual void setActivationLvl( double _y ) { FActivationLvl = _y; } virtual double activationLvl() const { return FActivationLvl; } virtual double boundedMembership( double _x ); protected: CFuzzySet(); virtual ~CFuzzySet() { }; virtual void reset() { FActivationLvl = 0.; } virtual void calculate( double _x ); #ifdef __BORLANDC__ std::map < std::string, setter > FSetters; std::map < std::string, getter > FGetters; #endif double FActivationLvl; private: CFuzzySignal * FParent; };
class CTrapezoid : public CFuzzySet { public: static ICFuzzySet * constructor(); static const char * className() { return "trapezoid"; } virtual EnSetType type() const { return enFLstTrapezoid; } virtual double membership( double _x ); double leftBottom() const { return FLeftBottom; } double leftTop() const { return FLeftTop; } double rightTop() const { return FRightTop; } double rightBottom() const { return FRightBottom; } void setLeftBottom( double _leftB ); void setLeftTop( double _leftT ); void setRightTop( double _rightT ); void setRightBottom( double _rightB ); #ifndef __BORLANDC__ virtual double param( std::string _name ) throw( EInvalidName ); virtual TDName2Value params(); virtual void setParam( std::string _name, double _val ) throw( EInvalidName ); #endif private: CTrapezoid(); virtual ~CTrapezoid() { }; double FLeftBottom; double FLeftTop; double FRightTop; double FRightBottom; };
CTrapezoid::CTrapezoid() { #ifdef __BORLANDC__ FGetters[ "left_bottom" ] = & leftBottom; FSetters[ "left_bottom" ] = & setLeftBottom; FGetters[ "left_top" ] = & leftTop; FSetters[ "left_top" ] = & setLeftTop; FGetters[ "right_top" ] = & rightTop; FSetters[ "right_top" ] = & setRightTop; FGetters[ "right_bottom" ] = & rightBottom; FSetters[ "right_bottom" ] = & setRightBottom; #endif }
Nie skompiluje się bez słowa __closure w deklaracjach wskaźników na funkcje, dlatego może być kompilowany tylko pod borlandem. Usuwając kompilację warunkową i słowo __closure typedef void( * setter )( double ); typedef double( * getter )();
pod MinGW 4.9.1 dostaję w kostruktorze klasy CTrapezoid błąd: "c_trapezoid.cpp:10: error: ISO C++ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to member function. Say '&CTrapezoid::leftBottom' [-fpermissive] FGetters["left_bottom"] = &leftBottom;" ^ |
|
maly |
» 2015-04-28 09:28:34 Użyj boost::function lub std::function. Kiedyś w Borlandzie też potrzebowałem podobnej funkcjonalności i zmajstrowałem takie coś: #include <iostream> #include <vector>
#include <vcl.h> #include <utilcls.h>
#include <boost/bind.hpp> #include <boost/function.hpp>
struct Prop { std::vector < boost::function0 < TVariant > > getter; template < typename M, typename P > void add( M m, P * p ) { getter.push_back( boost::bind( m, p ) ); } };
class Test { AnsiString getString() { return "ala"; } int getInt() { return 123; } Prop prop; public: Test() { prop.add( & Test::getString, this ); prop.add( & Test::getInt, this ); } TVariant get( int c ) { return prop.getter[ c ](); } };
int main() { Test t; AnsiString s = t.get( 0 ); std::cout << s << std::endl; int i = t.get( 1 ); std::cout << i << std::endl; return 0; } |
|
pekfos |
» 2015-04-28 20:25:57 pod MinGW 4.9.1 dostaję w kostruktorze klasy CTrapezoid błąd: |
Bo wskaźnik na funkcje i metodę to co innego. Jak już kombinować, to lepiej użyć std::bind(), std::mem_fn(), lambd, czy tego typu rzeczy. |
|
« 1 » |