2016-03-27 10 views
1

Ich schreibe ein Makro, das Hilfsmethoden generiert, um alle gleichnamigen Methoden aller Basisklassen einer aktuellen Klasse aufzurufen , und überspringe die Basisklassen, die keine Methoden dieses Namens haben. (Ich posten nicht das eigentliche Makro, weil ich niemanden verletzen möchte, unten ist meine Testdatei mit einer Makro-generierten Methode)Methode, die gleichnamige Methode aller Basisklassen aufruft, wenn existiert und Rückgabewert in eine Liste speichert

Ich schaffte es zu arbeiten, ohne die Rückgabewerte dieser Methode zu erhalten . Jetzt möchte ich die Werte speichern und eine Liste zurückgeben.

Unten ist eine Funktion, generiert von meinem Makro, es soll Methoden namens "base_method" aller Basen aufrufen, mit int und string als Argumente.

Ich verstehe nicht, warum ich den Fehler (unter dem Code) bekomme.

#include <type_traits> 
#include <list> 
#include <iostream> 

namespace detail{ 
    template <typename> struct sfinae_true : std::true_type{}; 
} 

namespace detail{ 
    template <typename T, typename A1, typename A2> 
    static auto test_base_method(int) -> 
     sfinae_true<decltype(std::declval<T>().base_method(std::declval<A1>(), std::declval<A2>()))>; 

    template <typename , typename A1, typename A2> 
    static auto test_base_method(long) -> 
     std::false_type; 

    template <typename T, typename A1, typename A2> 
    struct has_base_method : decltype(test_base_method<T, A1, A2>(0)){}; 

    template <typename Base, typename T, std::enable_if_t<has_base_method<Base,int,std::string>::value, bool> = true > 
    auto call_base_method_if_any(T& obj, int arg1, std::string arg2) -> 
     decltype(obj.Base::base_method(std::declval<int>(), std::declval<std::string>())) 
    { 
     return obj.Base::base_method(arg1, arg2); 
    } 

    template <typename Base, typename T, std::enable_if_t<!has_base_method<Base,int,std::string>::value, bool> = false> 
    auto call_base_method_if_any(T&, int, std::string) -> bool 
    { 
     return false; 
    } 
}; 

template <typename ... T> 
class Z : public T ... { 
public: 
    auto call_base_method_of_all_bases_if_any(int arg1, std::string arg2) -> std::list<bool> { 
     return std::list<bool> { (detail::call_base_method_if_any<T>(*this, arg1, arg2)) ... }; 
    } 
}; 

struct A{ 
    bool base_method(int, bool){ std::cout << "A\n"; return true; } 
    bool base_method_narg(){ std::cout << "A no arg\n"; return true; } 
}; 

struct B{ void base_method(int, bool){ std::cout << "B\n"; } }; 
struct C{ void base_method(int a, std::string b){ std::cout << "C, int = " << a << ", string = " << b; } }; 
struct D{ }; 

int main(){ 

    Z<A> b; 
    Z<A,B> c; 
    Z<A,B,C> d; 
    Z<A,B,C,D> a; 

    std::cout << "a:" << std::endl; 
    auto x =a.call_base_method_of_all_bases_if_any(0, "string"); 
    std::cout << std::endl; 

    std::cout << "b:" << std::endl; 
    b.call_base_method_of_all_bases_if_any(0, "string"); 
    std::cout << std::endl; 

    std::cout << "c:" << std::endl; 
    c.call_base_method_of_all_bases_if_any(0, "string"); 
    std::cout << std::endl; 

    std::cout << "d:" << std::endl; 
    d.call_base_method_of_all_bases_if_any(0, "string"); 
    std::cout << std::endl; 

} 

Kompilieren Fehler:

 g++ --std=c++14 expression_sfinae.3.cpp                               
    expression_sfinae.3.cpp: In instantiation of ‘std::__cxx11::list<bool> Z<T>::call_base_method_of_all_bases_if_any(int, std::__cxx11::string) [with T = {A, B, C, D}; std::__cxx11::string = std::__cxx11::basic_string<char>]’: 
    expression_sfinae.3.cpp:48:63: required from here 
    expression_sfinae.3.cpp:27:99: error: no matching function for call to ‘std::__cxx11::list<bool>::list(<brace-enclosed initializer list>)’ 
       return std::list<bool> { (detail::call_base_method_if_any<T>(*this, arg1, arg2)) ... }; 
                            ^
    In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/list:63:0, 
        from expression_sfinae.3.cpp:2: 
    /usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/bits/stl_list.h:697:9: note: candidate: template<class _InputIterator, class> std::__cxx11::list<_Tp, _Alloc>::list(_InputIterator, _InputIterator, const allocator_type&) 
      list(_InputIterator __first, _InputIterator __last, 
      ^
    /usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/bits/stl_list.h:697:9: note: template argument deduction/substitution failed: 
    expression_sfinae.3.cpp:27:74: note: cannot convert ‘detail::call_base_method_if_any<C, Z<A, B, C, D>, 1u>((*(Z<A, B, C, D>*)this), arg1, std::__cxx11::basic_string<char>(arg2))’ (type ‘void’) to type ‘const allocator_type& {aka const std::allocator<bool>&}’ 
       return std::list<bool> { (detail::call_base_method_if_any<T>(*this, arg1, arg2)) ... }; 
                      ^
    In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/list:63:0, 
        from expression_sfinae.3.cpp:2: 
    /usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/bits/stl_list.h:678:7: note: candidate: std::__cxx11::list<_Tp, _Alloc>::list(std::initializer_list<_Tp>, const allocator_type&) [with _Tp = bool; _Alloc = std::allocator<bool>; std::__cxx11::list<_Tp, _Alloc>::allocator_type = std::allocator<bool>] 
      list(initializer_list<value_type> __l, 
     ^
    /usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/bits/stl_list.h:678:7: note: candidate expects 2 arguments, 4 provided 
    /usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/bits/stl_list.h:667:7: note: candidate: std::__cxx11::list<_Tp, _Alloc>::list(std::__cxx11::list<_Tp, _Alloc>&&) [with _Tp = bool; _Alloc = std::allocator<bool>] 
      list(list&& __x) noexcept 
     ^

u/5.3.0/include/g++-v5/bits/stl_list.h:697:9: note: template argument deduction/substitution failed: 
expression_sfinae.3.cpp:27:74: note: cannot convert ‘detail::call_base_method_if_any<C, Z<A, B, C, D>, 1u>((*(Z<A, B, C, D>*)this), arg1, std::__cxx11::basic_string<char>(arg2))’ (type ‘void’) to type ‘const allocator_type& {aka const std::allocator<bool>&}’ 
      return std::list<bool> { (detail::call_base_method_if_any<T>(*this, arg1, arg2)) ... }; 
                     ^
In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/list:63:0, 
       from expression_sfinae.3.cpp:2: 
/usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/bits/stl_list.h:678:7: note: candidate: std::__cxx11::list<_Tp, _Alloc>::list(std::initializer_list<_Tp>, const allocator_type&) [with _Tp = bool; _Alloc = std::allocator<bool>; std::__cxx11::list<_Tp, _Alloc>::allocator_type = std::allocator<bool>] 
     list(initializer_list<value_type> __l, 
    ^
/usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/bits/stl_list.h:678:7: note: candidate expects 2 arguments, 4 provided 
/usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/bits/stl_list.h:667:7: note: candidate: std::__cxx11::list<_Tp, _Alloc>::list(std::__cxx11::list<_Tp, _Alloc>&&) [with _Tp = bool; _Alloc = std::allocator<bool>] 
     list(list&& __x) noexcept 
    ^

// 9000 lines ommited 

P. S. Ich habe auch noch nicht herausgefunden, wie man mit Funktionen umgeht, die nichts zurückgeben (ich glaube nicht, dass man eine Liste von Lücken haben kann). Alle Ratschläge sind willkommen.

Antwort

0

Die Ursache des Problems ist, dass C ‚s Methode gibt void:

struct C { 
    void base_method(int a, std::string b); 
    ^^^^^ 
}; 

Und Sie versuchen, diesen Rückgabetyp zu verwenden, um Ihre std::list<bool> aufzubauen.

auf der Annahme gehen, die man sich wünschen kann, diesen Fall auszuschließen, würde ich Ihre details wie so umschreiben:

// this is the preferred overload (last argument is 'int' instead of '...') 
// but will be SFINAE-d out if base_method() isn't callable with int or string 
// or it returns something other than bool 
template <class Base, class T, 
    class = std::enable_if_t<std::is_same<bool, 
     decltype(std::declval<Base&>().base_method(
      std::declval<int>(), 
      std::declval<std::string>())) 
     >::value>> 
bool call_base_method_if_any_impl(T& obj, int arg1, std::string arg2, int) 
{  
    return obj.Base::base_method(arg1, arg2); 
} 

// fallback overload 
template <class Base, class T> 
bool call_base_method_if_any_impl(T& obj, int arg1, std::string arg2, ...) {  
    return false; 
} 

template <class Base, class T> 
bool call_base_method_if_any(T& obj, int arg1, std::string arg2) { 
    return call_base_method_if_any_impl<Base>(obj, arg1, arg2, 0); 
} 
+0

cool. Ich werde immer noch versuchen, mit declltype für den Rückgabewert zu gehen, weil in dem Makro verschiedene Rückgabetypen unterstützt werden sollen, sind Ihre Änderungen lebensrettend. –

+0

Leider funktioniert Ihre Lösung immer noch nicht für mich. Das Problem ist, dass es den Teil "Skip-Methode, wenn es nicht existiert" nicht löst. –

+0

@ScottTiger Ziemlich sicher, dass es so ist. In Ihrem Beispiel hat keine der Klassen eine 'bool base_method (int, string)' Funktion und 'call_base_method_of_all_base_if_any()' gibt korrekt eine 'list ' zurück, die '{false, false, false, false}' enthält. – Barry