b00rt00s Temat założony przez niniejszego użytkownika  | 
[SOLVED] wyrazenia lambda + traits + static_assert » 2013-08-16 16:41:51 Napisałem szablonową funkcję, która wymaga podania jako argument funkcji o sygnaturze  void (*)(string,unsigned) lub obiektu z operatorem  operator()(string, unsigned). Oczywiście możliwe jest też podanie odpowiedniego wyrażenia lambda. Aby sprawdzić podczas kompilacji czy podany argument jest poprawny napisałem taki kod: #include <iostream>
  using namespace std;
  void A( string, unsigned ) { } void B( string, float ) { }
  struct cA {     void operator ()( string, unsigned ) { } };
  struct cB {     void operator ()( string, int ) { } };
  template < typename T, typename ReturnType, typename...Args > struct _check_args_ {          template < typename U, U >     class check_obj { };               constexpr static ReturnType( * func_ptr )( Args...) = nullptr;          template < typename C >     constexpr static bool test( check_obj < C, func_ptr >* ) { return true; }                    template < typename C >     constexpr static bool test( check_obj < ReturnType( C::* )( Args...), & C::operator () >* ) { return true; }               template < typename >     constexpr static bool test(...) { return false; }          static const bool value = test < T >( nullptr ); };
 
 
  template < typename T > void make_check( T ) {     static_assert( _check_args_ < T, void, string, unsigned >::value, "Error message" ); }
  int main() {     make_check( & A );      make_check( cA() );                                    return 0; } Program działa wyśmienicie dla funkcji i dla obiektów funkcyjnych. Ale jak zmusić go do odpowiedniego interpretowania wyrażeń lambda? Ma ktoś pomysł jak to zrobić?  | 
 | 
Elaine  | 
» 2013-08-16 17:23:09 Domyślnie operator() lambd jest const, wyjątkiem są sytuacje, gdy lambda jest oznaczona jako mutable (5.1.2/5). Jeśli dodasz odpowiedni overload, to zacznie działać – nie tylko dla lambd, ale także dla normalnych klas, których operator() jest const.  | 
 | 
b00rt00s Temat założony przez niniejszego użytkownika  | 
» 2013-08-16 17:31:08 Dzięki wielkie Alueril! W życiu bym na to nie wpadł. Szukałem dwa dni po całym internecie różnych informacji, sam kombinowałem jak koń pod górę, a rozwiązanie było takie proste. Proste, jak się coś wie...  | 
 | 
Elaine  | 
» 2013-08-16 18:15:49 Można to jeszcze uprościć,  check_obj nie jest tu potrzebny, wystarczy odpowiednio wykorzystane  std::enable_if i  std::is_same. Po dodaniu brakujących overloadów dla  volatile i  ref-qualifiers oraz zmianie składni wywołania na bardziej przypominającą  std::function, wygląda to tak: #include <type_traits>
  template < typename T, typename U > struct check_args;
  template < typename T, typename ReturnType, typename...Args > struct check_args < T, ReturnType( Args...) > {     template < typename U, typename V >     using true_type_if_same = typename std::enable_if < std::is_same < U, V >::value, std::true_type >::type;          template < typename C >     static auto test( int )->true_type_if_same < C, ReturnType( * )( Args...) >;     template < typename C >     static auto test( int )->true_type_if_same < decltype( & C::operator () ), ReturnType( C::* )( Args...) >;     template < typename C >     static auto test( int )->true_type_if_same < decltype( & C::operator () ), ReturnType( C::* )( Args...) &>;     template < typename C >     static auto test( int )->true_type_if_same < decltype( & C::operator () ), ReturnType( C::* )( Args...) &&>;     template < typename C >     static auto test( int )->true_type_if_same < decltype( & C::operator () ), ReturnType( C::* )( Args...) const >;     template < typename C >     static auto test( int )->true_type_if_same < decltype( & C::operator () ), ReturnType( C::* )( Args...) const &>;     template < typename C >     static auto test( int )->true_type_if_same < decltype( & C::operator () ), ReturnType( C::* )( Args...) const &&>;     template < typename C >     static auto test( int )->true_type_if_same < decltype( & C::operator () ), ReturnType( C::* )( Args...) volatile >;     template < typename C >     static auto test( int )->true_type_if_same < decltype( & C::operator () ), ReturnType( C::* )( Args...) volatile &>;     template < typename C >     static auto test( int )->true_type_if_same < decltype( & C::operator () ), ReturnType( C::* )( Args...) volatile &&>;     template < typename C >     static auto test( int )->true_type_if_same < decltype( & C::operator () ), ReturnType( C::* )( Args...) volatile const >;     template < typename C >     static auto test( int )->true_type_if_same < decltype( & C::operator () ), ReturnType( C::* )( Args...) volatile const &>;     template < typename C >     static auto test( int )->true_type_if_same < decltype( & C::operator () ), ReturnType( C::* )( Args...) volatile const &&>;     template < typename >     static auto test(...)->std::false_type;          using type = decltype( test < typename std::decay < T >::type >( 0 ) );     static constexpr bool value = type(); };
  template < typename T, typename ReturnType, typename...Args > constexpr bool check_args < T, ReturnType( Args...) >::value; Mały przykład użycia:  #include <iostream>
  template < typename T > void check( T && ) {     std::cout << check_args < T, void( double, int ) >::value << '\n'; }
  void f1( double, int ) { } void f2( float, int ) { } void f3( double, unsigned ) { } void f4( float, unsigned ) { } int f5( double, int ) { return 0; }
  struct Foo {     void operator ()( double, int ) { } };
  struct Bar {     void operator ()( double, int ) const volatile && { } };
  int main() {     check( & f1 );     check( & f2 );     check( & f3 );     check( & f4 );     check( & f5 );     check( Foo() );     check( Bar() );     check([]( int ) { } );     check([]( double, int ) { } );     check([]( double, int ) mutable { } ); }  | 
 | 
b00rt00s Temat założony przez niniejszego użytkownika  | 
» 2013-08-16 19:24:14 Bardzo ciekawy przykład, ale... nie skompilował mi się :P. Dostaję komunikat mówiący, że nie można przeciążyć funkcji test. Kompiluje oczywiście  z flagą  -std=c++11 (GCC 4.8.1).  Nie rozumiem w tym kodzie tylko jednego: do czego potrzebny jest końcowy fragment szablonu: template < typename T, typename ReturnType, typename...Args > constexpr bool check_args < T, ReturnType( Args...) >::value; Poza tym chyba czas przyglądnąć się nowościom w STL. Te kilka funkcji jest bardzo przydatnych i bardzo poprawia czytelność kodu.  | 
 | 
Elaine  | 
» 2013-08-16 19:45:37 | Bardzo ciekawy przykład, ale... Nie skompilował mi się :P. Dostaję komunikat mówiący, że nie można przeciążyć funkcji test. Kompiluje oczywiście  z flagą -std=c++11 (GCC 4.8.1). |  
 GCC 4.8.1 ma bug, przez który nie można przeciążyć funkcji ani specjalizować szablonu, jeśli jedyną różnicą są  ref-qualifiers w typie parametru. Ja mam 4.8.2 sprzed tygodnia, u mnie działa, Clang też nie ma z tym żadnych problemów. Jako obejście możesz usunąć wszystkie te wersje  test, które korzystają z  ref-qualifiers (z dwunastu wersji zrobią się tylko cztery) lub schować je pod np.  #ifdef GCC_HAS_BUGGY_REF_QUALIFIER_SPECIALIZATIONS. Nie rozumiem w tym kodzie tylko jednego: do czego potrzebny jest końcowy fragment szablonu:
 template < typename T, typename ReturnType, typename...Args > constexpr bool check_args < T, ReturnType( Args...) >::value;  |  
 Jeśli w jakimś miejscu kodu zostanie pobrany adres zmiennej  value (lub nastąpi inny  odr-use tej zmiennej), to konieczna jest jej definicja, w przeciwnym wypadku zachowanie będzie niezdefiniowane (w tym wypadku prawdopodobnie objawi się błędami linkera). 9.4.2/3:  | The member shall still be defined in a namespace scope if it is odr-used (3.2) in the program and the namespace scope definition shall not contain an initializer. |  
 Ten fragment kodu właśnie dostarcza definicję tej zmiennej.  | 
 | 
b00rt00s Temat założony przez niniejszego użytkownika  | 
» 2013-08-16 19:55:59 Jeszcze raz dzięki. Będę musiał poczekać aż w repo Arch'a pojawi się nowe GCC i będzie git. Pozdrawiam.  | 
 | 
Elaine  | 
» 2013-08-16 20:02:58 Jeśli używasz Archa, to nie musisz czekać – możesz zbudować nowszy snapshot (GCC 4.8.1 w Archu to tak naprawdę 4.8.2! Niestety, o trzy tygodnie za stare, żeby ten bug był poprawiony) korzystając z ABS. Wystarczy zmienić _snapshot=4.8-20130725 w PKGBUILD na _snapshot=4.8-20130815, albo nawet _snapshot=LATEST-4.8.  | 
 | 
|  « 1 »  2 |