Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

Sprytny switch

Ostatnio zmodyfikowano 2015-04-28 20:25
Autor Wiadomość
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ę:
C/C++
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:
C/C++
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?
P-131491
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:
C/C++
#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 ;)
P-131494
pekfos
» 2015-04-27 15:58:05
Po co w ogóle switch..?
C/C++
if( X )
     return trUtf8( F( 'a' + _col ) );
C/C++
T tab[] = { "a", "b", /* .., */ "c" };

if( X )
     return trUtf8( tab[ _col ] );
P-131501
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:
C/C++
QVariant Class::field( int _col ) const
{
    switch( _col )
    {
    case 0: return id(); // id() zwraca int
    case 1: return name(); // name() zwraca QString
    case 2: return createId(); // createId() zwraca int
    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.
P-131504
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..?
P-131505
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:
C/C++
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
C/C++
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;"
                             ^
P-131542
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ś:
C/C++
#include <iostream>
#include <vector>

#include <vcl.h>
#include <utilcls.h> // TVariant

#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;
}
P-131546
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.
P-131574
« 1 »
  Strona 1 z 1