2010-05-24 54 views
7

Vì vậy, tôi có một loạt các chức năng toàn cầu, nói:Làm thế nào để tạo một hàm 'passthru' trong C++ bằng cách sử dụng macro hoặc lập trình meta?

foo_f1(int a, int b, char *c); 
foo_f2(int a); 
foo_f3(char *a); 

Tôi muốn thực hiện một C++ bao bọc xung quanh đó, một cái gì đó như:

MyFoo::f1(int a, int b, char* c); 
MyFoo::f2(int a); 
MyFoo::f3(char* a); 

Có khoảng 40 chức năng như thế này, 35 trong số họ Tôi chỉ muốn chuyển sang chức năng toàn cầu, 5 người còn lại tôi muốn làm điều gì đó khác biệt.

Lý tưởng nhất là việc thực hiện các MyFoo.cpp sẽ là một cái gì đó như:

PASSTHRU(f1, (int a, int b, char *c)); 
PASSTHRU(f2, (int a)); 

MyFoo::f3(char *a) 
{ 
    //do my own thing here 
} 

Nhưng tôi đang gặp khó khăn để tìm ra một cách thanh lịch để làm cho vĩ mô passthru trên.

Những gì tôi thực sự cần là một cái gì đó giống như getArgs X huyền thoại() dưới đây:

MyFoo::f1(int a, int b, char *c) 
{ 
    X args = getArgs(); 
    args++; //skip past implicit this.. 
    ::f1(args); //pass args to global function 
} 

Nhưng ngắn rơi vào lắp ráp tôi không thể tìm thấy một thực hiện tốt getArgs().

+1

Đối với hầu hết các phần, điều này là không thể với phiên bản C++ hiện tại. Nếu bạn Google "chuyển tiếp hoàn hảo", bạn sẽ nhận được nhiều lượt truy cập về lý do tại sao tính năng này hiện không hoạt động và những gì C++ 0x bổ sung để giúp giải quyết vấn đề. –

+3

Mục đích là làm cho hàm toàn cầu trở thành thành viên của một lớp, đặc biệt khi bạn cố gắng bỏ qua 'điều này'? – GManNickG

Trả lời

10

Bạn có thể sử dụng Boost.Preprocessor để cho những điều sau đây:

struct X { 
    PASSTHRU(foo, void, (int)(char)) 
}; 

... mở rộng ra:

struct X { 
    void foo (int arg0 , char arg1) { return ::foo (arg0 , arg1); } 
}; 

... sử dụng các macro :

#define DO_MAKE_ARGS(r, data, i, type) \ 
    BOOST_PP_COMMA_IF(i) type arg##i 

#define PASSTHRU(name, ret, args) \ 
    ret name (\ 
    BOOST_PP_SEQ_FOR_EACH_I(DO_MAKE_ARGS, _, args) \ 
) { \ 
    return ::name (\ 
     BOOST_PP_ENUM_PARAMS(BOOST_PP_SEQ_SIZE(args), arg) \ 
    ); \ 
    } 
+0

chết tiệt, đánh tôi với nó; thanh lịch hơn nữa. Bạn có một lỗi mặc dù;) ~ –

+0

Điều này dẫn đến đệ quy vô hạn. :) – GManNickG

+0

@GMan: Rất tiếc, cảm ơn. –

0

Suy nghĩ ban đầu của tôi, và điều này có thể sẽ không hoạt động hoặc những người khác đã tuyên bố điều này, là để đặt tất cả các chức năng cơ bản của bạn với nhau trong một lớp học như ảo. Sau đó, viết các cải tiến chức năng vào các lớp kế thừa và chạy với nó. Nó không phải là một trình bao bọc macro, nhưng bạn luôn có thể gọi các hàm toàn cầu trong các lớp ảo.

Với một số thủ thuật lắp ráp, bạn có thể làm chính xác những gì bạn muốn, nhưng bạn sẽ mất khả năng di động nhiều hơn khả năng. Câu hỏi thú vị và tôi cũng muốn nghe câu trả lời của người khác.

0

Bạn có thể muốn sử dụng namespace nếu bạn không muốn xử lý nội dung lớp học, chẳng hạn như this. Bạn cũng có thể sử dụng phương pháp thành viên static trong một lớp học, nhưng tôi nghĩ rằng mọi người không thích điều đó nữa.

#ifndef __cplusplus 
#define PASSTHRU(type, prefix, func, args) type prefix##_##func args 
#else 
#define PASSTHRU(type, prefix, func, args) type prefix::func args 
#endif 

Hoặc

#ifndef __cplusplus 
#define PASSTHRU(type, prefix, func, ...) type prefix##_##func(__VA_ARGS__) 
... 
2

cú pháp Hơi khác nhau nhưng ...

#include <boost/preprocessor.hpp> 
#include <iostream> 

void f1(int x, int y, char* z) { std::cout << "::f1(int,int,char*)\n"; } 

#define GENERATE_ARG(z,n,unused) BOOST_PP_CAT(arg,n) 
#define GET_ARGS(n) BOOST_PP_ENUM(n, GENERATE_ARG, ~) 

#define GENERATE_PARAM(z,n,seq) BOOST_PP_SEQ_ELEM(n,seq) GENERATE_ARG(z,n,~) 

#define GENERATE_PARAMS(seq) BOOST_PP_ENUM(BOOST_PP_SEQ_SIZE(seq), GENERATE_PARAM, seq) 

#define PASSTHROUGH(Classname, Function, ArgTypeSeq) \ 
    void Classname::Function(GENERATE_PARAMS(ArgTypeSeq)) \ 
{ \ 
    ::Function(GET_ARGS(BOOST_PP_SEQ_SIZE(ArgTypeSeq))); \ 
} 

struct test 
{ 
    void f1(int,int,char*); 
}; 

PASSTHROUGH(test,f1,(int)(int)(char*)) 

int main() 
{ 
    test().f1(5,5,0); 

    std::cin.get(); 
} 

Bạn có thể nhận được một cái gì đó gần gũi hơn với bạn nếu bạn sử dụng các bộ, nhưng bạn sẽ phải cung cấp số lượng arg đến chức năng cơ bản (bạn có thể 't lấy được kích thước từ một bộ tuple). Sắp xếp như vậy:

PASSTHROUGH(test,f1,3,(int,int,char*)) 

Điều đó về những gì bạn đang tìm kiếm? Tôi biết nó có thể được thực hiện; mất khoảng nửa giờ để giải quyết. Bạn dường như mong đợi rằng có một 'điều này' tiềm ẩn mà phải được loại bỏ nhưng tôi không thấy tại sao ...vì vậy có lẽ tôi hiểu sai vấn đề. Ở bất kỳ mức nào, điều này sẽ cho phép bạn nhanh chóng thực hiện các hàm thành viên "passthrough" mặc định để trì hoãn một số chức năng toàn cầu. Bạn sẽ cần một DECPASSTHROUGH cho khai báo lớp nếu bạn muốn bỏ qua việc khai báo tất cả ... hoặc bạn có thể sửa đổi điều này để tạo các hàm nội dòng.

Gợi ý: Sử dụng BOOST_PP_STRINGIZE ((XX)) để kiểm tra đầu ra của các bộ tiếp hợp tiền xử lý.

+0

'Điều này' sẽ chỉ là vấn đề nếu bạn truy cập trực tiếp vào bộ nhớ. Cảm ơn giải pháp, hy vọng mọi người ở đây sẽ ổn với việc bao gồm bộ tiền xử lý Boost (chúng tôi không sử dụng Boost, nhưng PP trông độc lập). – Ryan

+3

Vâng, nếu mọi người không ổn với việc sử dụng boost bạn chỉ có thể làm những gì mọi người khác C++ dev làm khi họ không thể sử dụng boost: cố gắng viết những phần mà bạn thực sự cần, làm điều đó một cách tệ hại và than thở không đủ năng lực của các ông chủ của bạn. –

+0

@Ryan: PP là độc lập và thậm chí hoạt động trong C. Ngoài ra, bạn luôn có thể trích xuất các thành phần Boost với công cụ [bcp] (http://www.boost.org/tools/bcp/index.html). –

0

Chuyển tiếp hoàn hảo dựa trên các tham chiếu giá trị rvalue. STL có một mục blog trên nó tại http://blogs.msdn.com/b/vcblog/archive/2009/02/03/rvalue-references-c-0x-features-in-vc10-part-2.aspx và bạn sẽ muốn chọn một trình biên dịch hỗ trợ tính năng này để có cách tiếp cận này. Anh ấy đang thảo luận về Visual C++ 2010.

+0

OP không thực sự cần chuyển tiếp hoàn hảo vì chúng đang cung cấp đủ thông tin để chuyển tiếp các loại chính xác. Chuyển tiếp hoàn hảo là bắt buộc khi bạn làm việc ở cấp độ chung chung hơn. Do một trong các triển khai macro được giải thích trong các chú thích, OP có thể đặt int & int const và khi chúng được áp dụng và nó sẽ được chuyển tiếp chính xác như được chỉ định cho hàm thực hiện cuối cùng. –

5

Ở 40 hàm lẻ, bạn có thể nhập trình bao bọc bằng tay trong một giờ. Trình biên dịch sẽ kiểm tra tính chính xác của kết quả. Giả sử thêm 2 phút cho mỗi chức năng mới cần gói và thêm 1 phút để thay đổi chữ ký.

Như được chỉ định và không đề cập đến các cập nhật hoặc thay đổi thường xuyên, có vẻ như vấn đề này không yêu cầu giải pháp xảo quyệt.

Vì vậy, đề xuất của tôi là giữ cho nó đơn giản: làm điều đó bằng tay. Sao chép nguyên mẫu vào tệp nguồn, sau đó sử dụng macro bàn phím (emacs/Visual Studio/vim) để sửa mọi thứ và/hoặc nhiều lần tìm kiếm và thay thế, tạo một tập hợp các định nghĩa và một bộ khai báo. Cắt khai báo, dán vào tiêu đề. Điền các định nghĩa cho các hàm không chuyển giao. Điều này sẽ không giành cho bạn bất kỳ giải thưởng nào, nhưng nó sẽ sớm kết thúc.

Không phụ thuộc thêm, không có công cụ xây dựng mới, hoạt động tốt với trình duyệt/thẻ/intellisense/v.v., hoạt động tốt với bất kỳ trình gỡ lỗi nào và không có cú pháp/tính năng/mẫu hiện đại chuyên biệt, để mọi người có thể hiểu kết quả. (Đúng là không ai sẽ bị ấn tượng - nhưng nó sẽ là một loại không ấn tượng.)

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