2010-12-26 31 views
6

Tôi đã đọc các cơ quan chức năng khác nhau về điều này, bao gồm Dewhurst và chưa quản lý ở bất kỳ đâu với câu hỏi dường như đơn giản này.SFINAE và phát hiện nếu đối tượng hàm C++ trả về void

Điều tôi muốn làm là gọi một C++ function object, (về cơ bản, bất cứ điều gì bạn có thể gọi, hàm thuần túy hoặc một lớp với()) và trả lại giá trị của nó, nếu điều đó không làm trống hoặc " đúng "khác.

using std: 

struct Foo { 
    void operator()() { cout << "Foo/"l; } 
}; 
struct Bar { 
    bool operator()() { cout << "Bar/"; return true; } 
}; 

Foo foo; 
Bar bar; 
bool baz() { cout << "baz/"; return true; } 
void bang() { cout << "bang/"; } 

const char* print(bool b) { cout << b ? "true/" : "false/"; } 

template <typename Functor> bool magicCallFunction(Functor f) { 
    return true; // Lots of template magic occurs here 
       // that results in the functor being called. 
} 

int main(int argc, char** argv) { 
    print(magicCallFunction(foo)); 
    print(magicCallFunction(bar)); 
    print(magicCallFunction(baz)); 
    print(magicCallFunction(bang)); 
    printf("\n"); 
} 
// Results: Foo/true/Bar/true/baz/true/bang/true 

CẬP NHẬT

Cám ơn những suy nghĩ và ý tưởng!

Dựa trên điều này, tôi thực sự đã quyết định để đưa lên tất cả của tôi khuôn mẫu một cấp - vì vậy thay vào đó tôi có:

bool eval(bool (*f)()) { return (*f)(); } 

bool eval(void (*f)()) { (*f)(); return true; } 

template <typename Type> 
bool eval(Type* obj, bool (Type::*method)()) { return (obj->*method)(); } 

template <typename Type> 
bool eval(Type* obj, void (Type::*method)()) { (obj->*method)(); return true; } 

và chung lớp để thực hiện các đối tượng và phương pháp khác nhau xung quanh. Cảm ơn Mr.Ree vì mã đã đẩy tôi theo hướng đó!

Trả lời

1

Sẽ không dễ dàng hơn khi triển khai phiên bản không hoạt động quá tải in (void)?

Ahh well. Các mẫu chức năng và quá tải sẽ dễ dàng xử lý điều này khi chạy.

Sẽ hơi phức tạp nếu bạn muốn xử lý điều này lúc biên dịch, để sử dụng với #if macro hoặc xác nhận thời gian biên dịch tĩnh.

Nhưng kể từ khi bạn chỉ muốn trước đây, tôi có thể đề nghị một cái gì đó như thế này như là một điểm khởi đầu:.

(Tested dưới (GCC) 3.4.4 & 4.0.1 - Tôi biết, tôi cần phải nâng cấp !)

#include <iostream> 
using namespace std; 

struct Foo { 
    void operator()() {} 
}; 
struct Bar { 
    bool operator()() { return false; } 
}; 
Foo foo; 
Bar bar; 
bool baz() { return false; } 
void bang() {} 


struct IsVoid 
{ 
    typedef char YES[1]; 
    typedef char NO[2]; 

     /* Testing functions for void return value. */ 

    template <typename T> 
    static IsVoid::NO & testFunction(T (*f)()); 

    static IsVoid::YES & testFunction(void (*f)()); 

    static IsVoid::NO & testFunction(...); 

     /* Testing Objects for "void operator()()" void return value. */ 

    template <typename C, void (C::*)()> 
    struct hasOperatorMethodStruct { }; 

    template <typename C> 
    static YES & testMethod(hasOperatorMethodStruct<C, &C::operator()> *); 

    template <typename C> 
    static NO & testMethod(...); 


     /* Function object method to call to perform test. */ 
    template <typename T> 
    bool operator() (T & t) 
    { 
    return ( (sizeof(IsVoid::testFunction(t)) == sizeof(IsVoid::YES)) 
      || (sizeof(IsVoid::testMethod<T>(0)) == sizeof(IsVoid::YES))); 
    } 
}; 


#define BOUT(X) cout << # X " = " << boolToString(X) << endl; 

const char * boolToString(int theBool) 
{ 
    switch (theBool) 
    { 
    case true: return "true"; 
    case false: return "false"; 
    default:  return "unknownvalue"; 
    } 
} 

int main() 
{ 
    IsVoid i; 

    BOUT(IsVoid()(foo)); 
    BOUT(IsVoid()(bar)); 
    BOUT(IsVoid()(baz)); 
    BOUT(IsVoid()(bang)); 
    cout << endl; 

    BOUT(i(foo)); 
    BOUT(i(bar)); 
    BOUT(i(baz)); 
    BOUT(i(bang)); 
} 



Được rồi, tôi bắt đầu để xem chi tiết của vấn đề.

Trong khi chúng ta có thể làm điều gì đó dọc theo dòng này:

#include <iostream> 
using namespace std; 

struct FooA { 
    void operator()() {} 
}; 
struct FooB { 
    bool operator()() { return false; } 
}; 
struct FooC { 
    int operator()() { return 17; } 
}; 
struct FooD { 
    double operator()() { return 3.14159; } 
}; 
FooA fooA; 
FooB fooB; 
FooC fooC; 
FooD fooD; 

void barA() {} 
bool barB() { return false; } 
int barC() { return 17; } 
double barD() { return 3.14159; } 


namespace N 
{ 
     /* Functions */ 

    template <typename R> 
    R run(R (*f)()) { return (*f)(); } 

    bool run(void (*f)()) { (*f)(); return true; } 


     /* Methods */ 

    template <typename T, typename R> 
    R run(T & t, R (T::*f)()) { return (t .* f)(); } 

    template <typename T> 
    bool run(T & t, void (T::*f)()) { (t .* f)(); return true; } 
}; 


#define SHOW(X) cout << # X " = " << (X) << endl; 
#define BOUT(X) cout << # X " = " << boolToString(X) << endl; 

const char * boolToString(int theBool) 
{ 
    switch (theBool) 
    { 
    case true: return "true"; 
    case false: return "false"; 
    default:  return "unknownvalue"; 
    } 
} 


int main() 
{ 
    SHOW(N::run(barA)); 
    BOUT(N::run(barA)); 
    SHOW(N::run(barB)); 
    BOUT(N::run(barB)); 
    SHOW(N::run(barC)); 
    SHOW(N::run(barD)); 
    cout << endl; 

    SHOW(N::run(fooA,&FooA::operator())); 
    BOUT(N::run(fooA,&FooA::operator())); 
    SHOW(N::run(fooB,&FooB::operator())); 
    BOUT(N::run(fooB,&FooB::operator())); 
    SHOW(N::run(fooC,&FooC::operator())); 
    SHOW(N::run(fooD,&FooD::operator())); 
} 

Bạn làm vẫn có mà cần pha vào bóng ăn & CLASS :: operator() trong như một cuộc tranh cãi.


Cuối cùng, trong khi chúng ta có thể xác định xem hành của đối tượng() phương thức trả về một khoảng trống, chúng ta có thể không bình thường quá tải dựa trên các loại trở lại.

Chúng tôi có thể vượt qua giới hạn quá tải đó thông qua chuyên môn mẫu. Nhưng sau đó chúng tôi nhận được vào sự xấu xí này trong đó chúng tôi vẫn cần phải xác định các loại ... Đặc biệt là loại trả lại! Hoặc là thủ công hoặc bằng cách chuyển vào một đối số phù hợp để từ đó chúng tôi có thể trích xuất các loại cần thiết.

BTW: #define macro cũng không giúp ích gì. Các công cụ như thế nào?: Yêu cầu cùng loại cho cả hai? và: một phần.

Vì vậy, đây là tốt nhất tôi có thể làm ...



Dĩ nhiên ...

Nếu bạn không cần kiểu trả về ...

Nếu bạn chỉ chuyển kết quả cho một chức năng khác ...

Bạn có thể làm một việc như sau:

#include <iostream> 
using namespace std; 

struct FooA { 
    void operator()() {} 
}; 
struct FooB { 
    bool operator()() { return false; } 
}; 
struct FooC { 
    int operator()() { return 17; } 
}; 
struct FooD { 
    double operator()() { return 3.14159; } 
}; 
FooA fooA; 
FooB fooB; 
FooC fooC; 
FooD fooD; 

void barA() {} 
bool barB() { return false; } 
int barC() { return 17; } 
double barD() { return 3.14159; } 


#define SHOW(X) cout << # X " = " << (X) << endl; 

namespace N 
{ 
    template <typename T, typename R> 
    R run(T & t, R (T::*f)()) { return (t .* f)(); } 

    template <typename T> 
    bool run(T & t, void (T::*f)()) { (t .* f)(); return true; } 


    template <typename T> 
    void R(T & t) 
    { 
    SHOW(N::run(t, &T::operator())); 
    } 

    template <typename T> 
    void R(T (*f)()) 
    { 
    SHOW((*f)()); 
    } 

    void R(void (*f)()) 
    { 
    (*f)(); 
    SHOW(true); 
    } 
}; 


int main() 
{ 
    N::R(barA); 
    N::R(barB); 
    N::R(barC); 
    N::R(barD); 
    N::R(fooA); 
    N::R(fooB); 
    N::R(fooC); 
    N::R(fooD); 
} 
+0

Thử nghiệm nó và nó hoạt động trên gcc 4.2.1. –

+0

"Bản in" chỉ là để trình diễn những gì đang xảy ra ... những gì tôi đang làm là gói các phương thức và chức năng để chuyển sang cơ chế gọi lại asynch của người khác. Hầu hết các hàm và hàm của tôi trả về void - đôi khi tôi cần có khả năng trả về boolean cho cuộc gọi lại không đồng bộ của bên thứ ba. –

+0

Điều này rất trơn! Không có một hoặc hai nhưng ba thủ thuật tôi đã không nhận được đúng - một là hai trường hợp NO trong quá tải của testFunction, một trong những hasOperatorMethodStruct fiddle, và một là cách kết hợp hai điều kiện! Rất giáo dục, tôi nợ bạn một thức uống giải khát của sự lựa chọn của bạn. –

1

Có lẽ bạn có thể sử dụng thực tế rằng void & không có ý nghĩa như loại nhưng void * làm.

-1

cố gắng chuyên cho loại trả lại trống:

template<class F> 
class traits; 

template<class F, class T> 
class traits<T (F)()>; 

template<class F> 
class traits<void (F)()>; 

tôi nghĩ rằng ...

0

Với C++ 0x bạn có thể làm điều đó một cách dễ dàng bằng cách sử dụng decltype.

+0

decltype sẽ hoạt động như thế nào? – paulm

0

Nếu bạn có thể sử dụng Boost, mã sau có thể sẽ phân phát. Tôi đoán tất cả các hàm/hàm là nullary như trong câu hỏi của bạn. Tuy nhiên, để sử dụng điều này, result_type phải được xác định trong tất cả các functors (lớp chức năng).

#include <boost/utility/result_of.hpp> 
#include <boost/utility/enable_if.hpp> 
#include <boost/type_traits.hpp> 
using namespace boost; // Sorry, for brevity 

template< class F > 
// typename result_of< F() >::type 
typename disable_if< 
    is_void< typename result_of< F() >::type > 
, typename result_of< F() >::type 
>::type 
f(F const& x) 
{ 
    return x(); 
} 

template< class F > 
typename enable_if< 
    is_void< typename result_of< F() >::type >, bool 
>::type 
f(F const& x) 
{ 
    x(); 
    return true; 
} 

template< class T > 
T f(T x()) 
{ 
    return x(); 
} 

bool f(void x()) 
{ 
    x(); 
    return true; 
} 

static void void_f() {} 
static int int_f() { return 1; } 

struct V { 
    typedef void result_type; 
    result_type operator()() const {} 
}; 

struct A { 
    typedef int result_type; 
    result_type operator()() const { return 1; } 
}; 

int main() 
{ 
    A a; 
    V v; 
    f(void_f); 
    f(int_f); 
    f(a); 
    f(v); 
} 

Hope this helps

6

Để phát hiện một giá trị trả về khoảng trống tại thời gian biên dịch, lừa giữa các ý kiến ​​để quá tải operator,. Điều thú vị với toán tử dấu phẩy là nó có thể lấy một tham số void, và trong trường hợp này nó mặc định là được xây dựng trong operator,. Trong mã:

template <typename> tag {}; 

template <typename T> 
tag<T> operator,(T, tag<void>); 

Bây giờ, expr, tag<void>() có kiểu tag<typeof(expr)> ngay cả khi expr có kiểu void. Sau đó, bạn có thể nắm bắt điều này bằng các thủ thuật thông thường:

char (&test(tag<void>))[1]; 
template <typename T> char (&test(tag<T>))[2]; 

template <typename F> 
struct nullary_functor_traits 
{ 
    static const bool returns_void = sizeof(test((factory()(), tag<void>()))) == 1; 
private: 
    static F factory();  
}; 
Các vấn đề liên quan