2016-07-07 20 views
7

yêu cầu rất lạ này đã đưa ra ...Có thể kiểm tra xem hàm có kiểu void trở

tôi cần phải làm một thời gian biên dịch kiểm tra xem chức năng hiện nay có một loại void trở lại hay không và thất bại biên dịch nếu kiểu trả về là void.

Tôi đã cố gắng thực hiện một số phép thuật với http://en.cppreference.com/w/cpp/types/result_ofdecltype cũng vậy, tuy nhiên tôi không thể tiến gần hơn đến giải pháp.

#include <type_traits> 

void other_func(void) { } 

void test_maxi(void) 
{ 
    typedef decltype(&::other_func) TYPE; 
    static_assert(std::is_same<TYPE, void>::value, "not void"); 
} 

int main() { 
} 

Vì vậy, ở đây có các câu hỏi:

Có thể thực hiện điều này cho chức năng hiện?

EDIT Kiểm tra kiểu trả về phải có macro, vì nó sẽ được sử dụng trong một số hàm.

+0

Tại sao? Trình biên dịch sẽ hét lên với bạn nếu bạn cố gắng gán khoảng trống cho bất cứ thứ gì. –

+0

không có đối tượng nào như chức năng hiện tại, vậy bạn sẽ nhận được loại như thế nào? –

+0

có thể sử dụng lambda thay vì chức năng truyền thống? – Yves

Trả lời

13

Bạn có thể thực hiện thời gian biên dịch kiểm tra nếu một chuỗi bắt đầu đen với nhau, và sử dụng __PRETTY_FUNCTION__ vĩ mô, được thiết lập để chuỗi chữ bắt đầu với kiểu trả về hàm. Bạn nên kiểm tra xem macro này có bắt đầu bằng void theo sau không gian hay không.

Mã này biên dịch tốt:

constexpr bool startsWith(const char* a, const char* b) { 
    return *a == *b && (*(a + 1) == '\0' || startsWith(a + 1, b + 1)); 
} 

int f() { 
    static_assert(!startsWith("void ", __PRETTY_FUNCTION__), "not void"); 
    return 1; 
} 


int main() { 
} 

Nếu bạn thay đổi f kiểu trả về để void:

constexpr bool startsWith(const char* a, const char* b) { 
    return *a == *b && (*(a + 1) == '\0' || startsWith(a + 1, b + 1)); 
} 

void f() { 
    static_assert(!startsWith("void ", __PRETTY_FUNCTION__), "not void"); 
} 


int main() { 
} 

static_assert sẽ cháy.

Macro __PRETTY_FUNCTION__ dường như là cụ thể cho trình biên dịch GNU C++, tuy nhiên, clang++ hoạt động tốt vì nó cũng định nghĩa macro này. Nếu bạn đang sử dụng trình biên dịch khác, bạn nên kiểm tra xem macro này có thực sự được đặt hay không và đọc tài liệu trình biên dịch để xác định macro tương tự, ví dụ: __FUNCSIG__.

Bạn có thể chơi xung quanh với #ifdef __PRETTY_FUNCTION__ ... để thực hiện việc này dễ dàng hơn giữa các trình biên dịch khác nhau, nhưng tôi tin rằng đây là chủ đề cho một câu hỏi khác.

+1

Dựa trên yêu cầu kỳ lạ của việc làm cho một vĩ mô (yuck) đây là câu trả lời đúng. Lưu ý: Đối với Visual C++ bạn có thể sử dụng '__FUNCSIG__'. –

+3

Rất đẹp. Hãy để tôi thêm rằng bạn nên kiểm tra nó để bắt đầu với "void", để tránh bắn vào các phương thức trả về 'void *' hoặc một struct tùy chỉnh 'voidfoo'. – majk

+0

'namespace {void _() {\ n #ifdef __PRETTY_FUNC __ \ n # define BOB 1 #else #define BOB 2 #endif}}' sẽ nhận cho bạn mã thông báo 'BOB' phân biệt giữa funcsig hoặc funcsig. Với một số chơi xung quanh, shoud có thể xác định một mã thông báo là '__PRETTY_FUNC__' iff nó sẽ là hợp lệ ... – Yakk

4

Nếu bạn có thể đặt tên cho chức năng hiện nay thì cách đơn giản nhất là:

static_assert(!std::is_same<decltype(test_maxi()), void>::value, "void"); 
+0

Đây là một vấn đề nhỏ ... Tôi không thể đặt tên cho chức năng hiện tại, đây là nghĩa vụ phải đi trong một loại vĩ mô của xây dựng. – fritzone

+0

@fritzone Nếu bạn có thể đánh dấu chức năng của bạn là 'constexpr', điều này sẽ hoạt động. – erip

0

Tất cả những gì bạn cần là phát hiện loại trả về và kiểm tra xem loại đó có phải là loại void hay không. Để kiểm tra loại bạn có thể sử dụng std::is_void. kiểu trả về có thể được phát hiện với mẫu sau:

#include <type_traits> // for is_void 

template<typename R, typename... A> 
void test(R (*func)(A...)) { 
    static_assert(
     !std::is_void<R>::value, 
     "void return type is not allowed" 
    ); 
} 

// functions to check: 
void a(int, char*) {} 
int b(char, float) {return 0;} 

int main() 
{ 
    test(a); // < assert triggered here 
    test(b); 
} 
+0

Điều này không làm việc cho các macro. – erip

3

Bạn có thể kiểm tra các kiểu trả về của hàm hiện tại bằng cách kiểm tra xem một người chết return có thể biên dịch:

struct ConvertToAnything { 
    template <class T> 
    operator T() const { assert(!"Don't call me!"); } 
}; 

#define CHECK_NONVOID_RETURN() if(true); else return ConvertToAnything{} 

int f() { 
    CHECK_NONVOID_RETURN(); // Passes 
    return 42; 
} 

void g() { 
    CHECK_NONVOID_RETURN(); // Fails at compile-time 
} 

Trường hợp của void là một người đặc biệt , vậy là đủ rồi. Nhưng bạn cũng có thể cấm các loại khác bằng cách quá tải ConvertToAnything::operator Type() = delete;. Cho phép void tham gia nhiều hơn một chút, nhưng vẫn khả thi.

1

Hãy thử điều này:

template <typename ... Args> 
constexpr bool return_void(void(Args ...)) { return true; } 

template <typename R, typename ... Args> 
constexpr bool return_void(R(Args ...)) { return false; } 

Giả sử chúng ta có các chức năng sau:

void f() {} 
void g(int) {} 
void h(int*,char&) {} 
void i(float,bool) {} 
void j(const char *const) {} 
void k(void()) {} 
int l() { return {}; } 
float m(int) { return {}; } 

Tất cả các cuộc gọi đến return_void sẽ trở thành sự thật miễn là nó được gọi với sáu chức năng đầu tiên, Cuộc gọi return_void(l)return_void(m) sẽ trả về false vì họ sẽ gọi phiên bản thứ hai của mẫu, phiên bản trả về sai.

Check it online

này sẽ cho phép bạn kiểm tra xem một hàm trả về void cả trong thời gian chạy và biên dịch thời gian:

int main() { 
    static_assert(return_void(f), "not void"); 

    if (!return_void(m)) 
     std::cout << "m result is: " << m(0) << '\n'; 

    return 0; 
} 
Các vấn đề liên quan