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

[SFML 2] sf::Packet i zabronienie użycia operatora << dla określonego typu

Ostatnio zmodyfikowano 2013-11-15 14:21
Autor Wiadomość
DejaVu
Temat założony przez niniejszego użytkownika
[SFML 2] sf::Packet i zabronienie użycia operatora << dla określonego typu
» 2013-11-14 21:54:29
Chciałbym zabronić użycie operatora << dla klasy sf::Packet dla określonego typu. Czy da się osiągnąć opisany przeze mnie efekt tak, aby błąd był rzucany już na poziomie kompilacji, bez konieczności ingerowania w implementację SFML-a?

Przykład:
C/C++
#include <SFML/Network/Packet.hpp>

class CPacketType
{
   
    friend void operator <<( sf::Packet &, const CPacketType & )
    {
        throw std::logic_error( "Nie można używać operatora << do zapisywania CPacketType - użyj metody CPacketType::save" );
    }
};
Powyższy przykład rzuca wyjątkiem w chwili wywołania operatora, a mi chodzi o to, aby na poziomie kompilacji wykryć już nieprawidłowe użycie klasy.
P-96286
Monika90
» 2013-11-15 00:26:57
Zależy o co ci dokładnie chodzi. Możesz zwyczajnie nie zadeklarować tego operatora i program się nie skompiluje. Jeżeli zależy ci na konkretnym komunikacie o błędzie, to być może to zadziała:
C/C++
template < class T = int >
void operator <<( sf::Packet &, const CPacketType & )
{
    static_assert( !sizeof( T ), "Miejsce na twoj komunikat" );
}
P-96290
maly
» 2013-11-15 07:06:46
Dyrektywa
#error
?
P-96292
DejaVu
Temat założony przez niniejszego użytkownika
» 2013-11-15 10:09:01
Jeżeli nie zadeklaruję wspomnianego operatora, to klasa sf::Packet użyje operatora TypeE operator() const, który jest w klasie CPacketType i tym samym zapisany zostanie int do pakietu. W przypadku zdefiniowania operatora << w powyższym kodzie uzyskuję więc poprawną sytuację, czyli obsługa operatora sf::Packet<< zostaje przechwycona przez dopisany operator.

/edit:
Alternatywnym rozwiązaniem jest napisanie obsługi w ten sposób:
C/C++
friend bool operator <<( sf::Packet & _packet, const CPacketType & _pt )
{
    //throw std::logic_error("Nie można używać operatora << do zapisywania CPacketType - użyj metody CPacketType::save");
    return _pt.save( _packet );
}
Niemniej jednak obecnie intryguje mnie, czy na poziomie kompilacji da się zabronić użycia jakiegoś operatora zdefiniowanego w innej klasie.
P-96294
Monika90
» 2013-11-15 12:30:09
Jeżeli nie zadeklaruję wspomnianego operatora, to klasa sf::Packet użyje operatora TypeE operator() const, który jest w klasie CPacketType i tym samym zapisany zostanie int do pakietu.
Czy chodzi ci o operator konwersji na jakiś typ całkowitoliczbowy? Nie deklaruj takich operatorów, a jeśli już to z atrybutem explicit. Ale podane przeze mnie wyżej rozwiązanie powinno działać nawet w obecności takiego operatora.

bool operator <<( sf::Packet & _packet, const CPacketType & _pt )
Zwracanie bool z takiego operatora to zły pomysł. Prędzej czy później ktoś napisze:
sf_packet << packet_type << 5;
 i się zdziwi...

Niemniej jednak obecnie intryguje mnie, czy na poziomie kompilacji da się zabronić użycia jakiegoś operatora zdefiniowanego w innej klasie.
Bez modyfikacji tej klasy? - nie sądzę by istniała jakaś pewna w 100% metoda i wątpię by to było potrzebne.
P-96295
Elaine
» 2013-11-15 14:09:27
Najlepszym rozwiązaniem jest niedefiniowanie operatorów konwersji.
Ale jeśli już muszą być, to da się to osiągnąć nawet w starych kompilatorach osbługujących tylko C++03 — dla nowszych wystarczy rozwiązanie Moniki — tylko trzeba się trochę pobawić ze SFINAE:
C/C++
#include <ostream>

template < bool P, typename T >
struct enable_if;

template < typename T >
struct enable_if < true, T > {
    typedef T type;
};

template < typename Base, typename T >
struct is_same_or_base {
    typedef char yes[ 2 ];
    typedef char no[ 1 ];
    static yes * test( Base * );
    static no * test(...);
    static const bool value = sizeof( * test( static_cast < T *>( 0 ) ) ) == sizeof( yes );
};

template < typename Base, typename T >
const bool is_same_or_base < Base, T >::value;

struct Foo {
    operator int() const { return 5; }
};

struct Bar
    : Foo
{
   
};

struct Qux
    : Bar
{
   
};

template < typename T >
typename enable_if < is_same_or_base < Foo, T >::value, std::ostream &>::type operator <<( std::ostream & foo, const T & ) {
    typedef char error_do_not_use_this_function[ sizeof( T ) != 0 ? - 1
        : 1 ];
    error_do_not_use_this_function x;
    static_cast < void >( x );
    return foo;
}

std::ostream & operator <<( std::ostream & foo, const Qux & ) {
    return foo;
}

void foo( std::ostream & stream ) {
    Foo obj;
    stream << obj; // nie
    Bar obj2;
    stream << obj2; // nie
    Qux obj3;
    stream << obj3; // tak
}
Jeśli używasz Boosta, to zamiast enable_if i is_same_or_base możesz użyć boost::enable_if i boost::is_base_of.
P-96297
DejaVu
Temat założony przez niniejszego użytkownika
» 2013-11-15 14:21:12
C/C++
bool operator <<( sf::Packet & _packet, const CPacketType & _pt )
Zwracanie bool z takiego operatora to zły pomysł. Prędzej czy później ktoś napisze: sf_packet << packet_type << 5; i się zdziwi...

U nas w projekcie jest wymaganie, że praca z pakietem sprowadza się do:
C/C++
bool bValid = true;
bValid = bValid &&( _packet << 123 );
//...
Poza tym metoda CPacketType::save nie zawsze wykona operację zapisu jeżeli stan obiektu jest niepoprawny, więc wydaje mi się, że jest to jedyny sposób, aby zapobiec zapisaniu danych w błędnym formacie, które przechowuje CPacketType :)
P-96298
« 1 »
  Strona 1 z 1