2016-04-08 21 views
7

Tôi đang cố gắng tạo hàm bao hàm chung có chức năng làm đối số mẫu và lấy các đối số giống như hàm đó làm đối số của nó. Ví dụ:Trình bao bọc hàm chức năng C++ với chức năng làm đối số mẫu

template <typename F, F func> 
/* return type of F */ wrapper(Ts... Args /* not sure how to get Ts*/) 
{ 
    // do stuff 
    auto ret = F(std::forward<Ts>(args)...); 
    // do some other stuff 
    return ret; 
} 

Các giải pháp cần phải được cast cho một con trỏ hàm với cùng loại như func để tôi có thể vượt qua nó để một api C. Nói cách khác, giải pháp cần phải là một hàm và không phải là một đối tượng hàm. Quan trọng nhất, Tôi cần để có thể làm việc trong hàm wrapper.

Nếu các ý kiến ​​inline không rõ ràng, tôi muốn để có thể làm điều gì đó như sau:

struct c_api_interface { 
    int (*func_a)(int, int); 
    int (*func_b)(char, char, char); 
}; 

int foo(int a, int b) 
{ 
    return a + b; 
} 

int bar(char a, char b, char c) 
{ 
    return a + b * c; 
} 

c_api_interface my_interface; 
my_interface.func_a = wrapper<foo>; 
my_interface.func_b = wrapper<bar>; 

Tôi nhìn cho bài viết có liên quan và phát hiện này, nhưng không ai trong số họ là khá gì Tôi đang cố gắng làm. Hầu hết các bài viết này đều liên quan đến các đối tượng hàm. Là những gì tôi đang cố gắng làm thậm chí có thể?

Function passed as template argument

Function wrapper via (function object) class (variadic) template

How does wrapping a function pointer and function object work in generic code?

How do I get the argument types of a function pointer in a variadic template class?

Generic functor for functions with any argument list

C++ Functors - and their uses

Để trả lời 2 câu trả lời đầu tiên, tôi đã chỉnh sửa câu hỏi để làm rõ rằng tôi cần có khả năng làm việc trong hàm bao bọc (tức là sửa đổi một số trạng thái toàn cầu trước và sau khi gọi hàm bọc)

Trả lời

3
#include <utility> 
#include <iostream> 

struct c_api_interface { int (*func_a)(int, int); int (*func_b)(char, char, char); }; 
int foo(int a, int b) { return a + b; } 
int bar(char a, char b, char c) { return a + b * c; } 


template<typename Fn, Fn fn, typename... Args> 
typename std::result_of<Fn(Args...)>::type 
wrapper(Args... args) { 
    std::cout << "and ....it's a wrap "; 
    return fn(std::forward<Args>(args)...); 
} 
#define WRAPIT(FUNC) wrapper<decltype(&FUNC), &FUNC> 

int main() { 
    c_api_interface my_interface; 
    my_interface.func_a = WRAPIT(foo); 
    my_interface.func_b = WRAPIT(bar); 

    std:: cout << my_interface.func_a(1,1) << std::endl; 
    std:: cout << my_interface.func_b('a','b', 1) << std::endl; 

    return 0; 
} 

thấy http://rextester.com/ZZD18334

+0

Cảm ơn bạn! Tôi thay đổi điều này thành câu trả lời được chấp nhận vì nó ngắn hơn của @TC, chỉ sử dụng một hàm chứ không phải cấu trúc, sử dụng 'std :: forward' và (như là một ý kiến) dễ hiểu hơn vì bạn không sử dụng chuyên môn đối số mẫu. – Eric

+0

Mặc dù tôi phải thừa nhận std :: forward chỉ có trong đó bởi vì tôi đã thử một cái gì đó mà sẽ biên dịch theo MSVC và sau đó nghĩ "ah vít nó, sẽ không xảy ra ... nhưng hey, giữ phía trước" ;-) – VolkerK

+0

Điều gì là cách thích hợp để khai báo một bí danh cho trình bao bọc sẽ trông giống như một hàm bình thường để gọi? Một cái gì đó như 'sử dụng a_wrapped = wrapper ;' (nhưng cái này không hoạt động). – eudoxos

0

có thể bạn cần một cái gì đó giống như

template <typename F> 
class Wrapper { 
public: 
    Wrapper(F *func) : function(func) {} 
    operator F*() { return function; } 
    F *function; 
}; 

Mà bạn có thể sử dụng như void (*funcPtr)(int) = Wrapper<void(int)>(&someFunction);

+0

tôi đánh giá cao phản ứng, nhưng điều này không làm những gì tôi yêu cầu trong bài gốc . Trong giải pháp của bạn, nếu tôi gọi 'funcPtr()' nó gọi 'someFunction'. Tôi muốn nó gọi một hàm hoạt động trước và sau khi gọi 'someFunction'. – Eric

+1

@Eric, bây giờ tôi hiểu những gì bạn muốn và không hiểu tại sao bạn đang tìm kiếm một cách khó khăn như vậy.Hàm lambda mà không bắt được có thể cast thành c con trỏ hàm: 'void (* funcPtr) (int) = [] (int a) {/ * smth * /};' - gọi hàm bọc và trước/sau thường trình bên trong lambda –

+0

I đã thử điều đó lúc đầu. Tôi tin rằng lambdas chỉ có thể cast để trỏ con trỏ nếu chúng không nắm bắt được, và để tạo ra cái chung này, tôi cần viết một lambda để nắm bắt hàm mà nó sẽ gọi, nhưng có thể có một cách để thực hiện công việc mà tôi đang thiếu . – Eric

0

tôi nghĩ rằng sẽ là cách súc tích để làm những gì bạn muốn:

template <typename F> 
F* wrapper(F* pFunc) 
{ 
    return pFunc; 
} 

và sử dụng nó như thế này:

my_interface.func_a = wrapper(foo); 
my_interface.func_a(1, 3); 
+0

Cảm ơn bạn đã phản hồi, nhưng điều này không làm những gì tôi yêu cầu. Tôi cần để có thể làm một số công việc trong hàm wrapper trước và sau khi tôi gọi hàm bọc (trong trường hợp này là 'foo'). Do đó hàm wrapper cần phải có chữ ký hàm ** giống như hàm bọc **. – Eric

+0

Tôi không chắc chắn bạn có thể lấy chữ ký từ các đối số mẫu (cụ thể là 'Ts ... Args') nhưng bạn có thể lưu chúng cho cuộc gọi trì hoãn như được hiển thị [ở đây] (http://stackoverflow.com/questions/15537817/c -làm thế nào để lưu trữ-một-tham số-gói-như-một-biến) và sau đó trong 'Wrapper :: operator()' làm công cụ trước và sau khi gọi đến func cơ bản với đối số đã lưu –

7
template<class F, F f> struct wrapper_impl; 
template<class R, class... Args, R(*f)(Args...)> 
struct wrapper_impl<R(*)(Args...), f> { 
    static R wrap(Args... args) { 
     // stuff 
     return f(args...); 
    } 
}; 

template<class F, F f> 
constexpr auto wrapper = wrapper_impl<F, f>::wrap; 

Sử dụng làm wrapper<decltype(&foo), foo>.

+0

Cảm ơn bạn rất nhiều, điều này đã làm việc hoàn hảo! :) Đối với những người khác: biến mẫu là một C++ 14 tính năng tôi tin rằng, do đó có thể không hoạt động trên tất cả các trình biên dịch. Tôi đã định nghĩa một macro '#define wrapper (func) wrapper_impl :: wrap' mà hoàn thành gần như giống nhau. – Eric

+0

Điều này có thể được điều chỉnh để phù hợp với các đối số tùy chọn không? Có nghĩa là args tùy chọn trong hàm bọc cũng là tùy chọn trong trình bao bọc. Trong biến thể này, tất cả các đối số phải được chỉ định. – eudoxos

0

bạn có thể thử một cái gì đó như thế (Ugly, nhưng hoạt động)

#include <iostream> 
#include <functional> 

struct wrapper_ctx 
{ 
    wrapper_ctx() 
    { 
    std::cout << "Before" << std::endl; 
    } 
    ~wrapper_ctx() 
    { 
    std::cout << "after" << std::endl; 
    } 
}; 

template <typename F, typename... Args> 
auto executor (F&& f, Args&&... args) -> typename std::result_of<F(Args...)>::type 
{ 
    wrapper_ctx ctx; 
    return std::forward<F>(f)(std::forward<Args>(args)...); 

} 

template <typename F> 
class wrapper_helper; 


template<typename Ret, typename... Args> 
class wrapper_helper <std::function<Ret(Args...)>> 
{ 
    std::function<Ret(Args...)> m_f; 
public: 
    wrapper_helper(std::function<Ret(Args...)> f) 
     : m_f(f) {} 
    Ret operator()(Args... args) const 
    { 
    return executor (m_f, args...); 
    } 
}; 

template <typename T> 
wrapper_helper<T> wrapper (T f) 
{ 
    return wrapper_helper <T>(f); 
} 


int sum(int x, int y) 
{ 
    return x + y; 
} 


int main (int argc, char* argv []) 
{ 

    std::function<int(int, int)> f = sum; 
    auto w = wrapper (f); 

    std::cout << "Executing the wrapper" << std::endl; 
    int z = w(3, 4); 

    std::cout << "z = " << z << std::endl; 
} 
Các vấn đề liên quan