2012-02-03 27 views
11

Có macro C++ nào nhận được không gian tên và tên hàm hiện tại không? Ví dụ:Macro để lấy tên miền và tên hàm hiện tại (nhưng không có chữ ký đầy đủ)?

namespace foo { 
    namespace bar { 
    void baz(int i, double d) { 
     std::cout << MACRO << std::endl; 
    } 
    } 
} 

sẽ in foo::bar::baz. Tôi biết về __FUNCTION__ nhưng không cung cấp cho không gian tên. Và BOOST_CURRENT_FUNCTION cho toàn bộ chữ ký, bao gồm. đối số và kiểu trả về:

void foo::bar::baz(int, double) 

Có thể viết macro để trích xuất không gian tên và tên hàm từ BOOST_CURRENT_FUNCTION?

Tôi muốn điều đó cho mục đích khai thác gỗ, để có được một chuỗi khai thác gỗ như

foo::bar::baz -- blah logging message blah 
+0

Tôi không nghĩ rằng có bất kỳ nền tảng độc lập nào để có được chữ ký đầy đủ của hàm. Tuy nhiên, tại sao bạn không thể sử dụng combo của '__FILE__' và' __LINE__'? Nó sẽ cho bạn gần như cùng một hiệu ứng; hơn nữa là nhanh hơn để xác định vị trí trong mã của bạn từ các bản ghi so với tìm kiếm một hàm! – iammilind

+0

@iammilind: Thực ra, tôi muốn sử dụng nội dung macro làm tên logger trong log4cxx. Những tên logger này có thứ bậc và bạn có thể thiết lập các mức log khác nhau cho các không gian tên khác nhau, vv Vì vậy, những gì tôi thực sự làm là một cái gì đó giống như logger Logger Logger Logtr Logger Logger Logger Logger Logger Logger Logger Log Log ; LOG4CXX_INFO (logger, "blah logging blah"); 'MACRO sẽ điền vào phần' foo.bar.baz'. Tôi không muốn làm cho nó phức tạp trong bài viết của mình. – Frank

Trả lời

4

Theo như tôi biết, đó là không thể (không portably). Tuy nhiên, từ chữ ký đầy đủ, bạn có thể trích xuất các đối số đó. Tất nhiên nó đòi hỏi phải phân tích cho biết chữ ký ... mà không phải là dễ dàng như vậy: x

Dưới đây là chức năng tôi sử dụng tại thời điểm này:

// What we want to consume: 
// void 
// signed short 
// unsigned int 
// Test::Bar<T, N> 
// 
static char const* consumeType(char const* const begin, char const* const end){ 
    static StringRef const Signed("signed"); 
    static StringRef const Unsigned("unsigned"); 

    char const* it = begin; 

    if (startsWith(it, Signed)) { it += Signed.size() + 1; } 
    else if (startsWith(it, Unsigned)) { it += Unsigned.size() + 1; } 

    // jump over the return type 
    size_t templateNest = 0; 
    while (it != end) { 
    if (*it == ' ' and templateNest == 0) { break; } 
    if (*it == '<') { ++templateNest; } 
    if (*it == '>' and templateNest > 0) { --templateNest; } 

    ++it; 
    } 

    return it; 
} // consumeType 

// 
// \param signature: signature as returned by __func___ on gcc 
// \return: full name, included namespace qualifier and class (if any) 
// 
// void Test::Bar<T, N>::parameterized(U) const 
// [with unsigned int O = 4u, U = Test::Foo, 
// T = Test::Foo, unsigned int N = 3u] 
// -> Test::Bar<T, N>::parameterized 
// 
StringRef parseFunctionName(StringRef const signature) { 
    char const* begin = signature.begin(); 
    char const* end = signature.end(); 

    // Jump over the return type 
    begin = consumeType(begin, end); 
    if (begin == end) { return signature; } 

    // skip the space right after the return type 
    ++begin; 
    if (begin == end) { return signature; } 

    // if we encounter a '(' then it means that we return a function, 
    // and we once again need to jump over the return type 
    if (*begin == '(') { 
    begin = consumeType(++begin, end); 

    // skip the space 
    ++begin; 
    if (begin == end) { return signature; } 
    } 

    // and finally, we got the beginning, and we need to get the end, which is 
    // the first opening '(' 
    char const* e = std::find(begin, end, '('); 
    return StringRef(begin, e - begin); 
} // parseFunctionName 

Và accompagnying nó kiểm tra:

#define UT_FUNCTION_CHECK(Signature_, Name_) \ 
    UT_CHECK(parseFunctionName(StringRef(Signature_)) == Name_); 

void Function() { 
    // Regular functions 
    UT_FUNCTION_CHECK("int main()", "main") 
    UT_FUNCTION_CHECK("int foo(int, double)", "foo") 
    UT_FUNCTION_CHECK("unsigned int foo(int, double)", "foo") 

    // Templates 
    UT_FUNCTION_CHECK("unsigned int Test::Bar<T, N>::print() const" 
        " [with T = Test::Foo, unsigned int N = 3u]", 
        "Test::Bar<T, N>::print") 
    UT_FUNCTION_CHECK("Test::Bar<T, N> Test::Bar<T, N>::print() const" 
        " [with T = Test::Foo, unsigned int N = 3u]", 
        "Test::Bar<T, N>::print") 
    UT_FUNCTION_CHECK("void Test::Bar<T, N>::parameterized(U) const" 
        " [with unsigned int O = 4u, U = Test::Foo," 
        " T = Test::Foo, unsigned int N = 3u]", 
        "Test::Bar<T, N>::parameterized") 

    // Functions returning functions 
    UT_FUNCTION_CHECK("void (* Test::Foo::func() const)()", 
        "Test::Foo::func") 
    UT_FUNCTION_CHECK("void (Test::Foo::* Test::Foo::method() const)(int)volatile", 
        "Test::Foo::method") 
    UT_FUNCTION_CHECK("void (Test::Foo::* Test::Foo::super())" 
        "(void (Test::Foo::*)(int)volatile)const", 
        "Test::Foo::super") 
    } // Function 

Nó hoạt động kết hợp với macro __func__ của gcc.

Lớp StringRef tương tự như llvm::StringRef.

Tôi nghĩ rằng nó nên đáp ứng nhu cầu của bạn nếu bạn có thể đủ khả năng phân tích thêm. Nó khá nhanh: không có backtracking và không có phân bổ động, vì vậy không phải là một vấn đề (đặc biệt là so với viết vào một tập tin ...).

Hy vọng điều đó sẽ hữu ích.

+0

Cảm ơn! Tôi sẽ cố gắng làm cho nó biên dịch. 'ConsumerType' được định nghĩa như thế nào? – Frank

+1

@Frank: Tôi không hiểu câu hỏi ... nó được định nghĩa như được viết trong câu trả lời? –

+0

Ồ, lỗi của tôi, không bao giờ. – Frank

Các vấn đề liên quan