目录
- 0 前言
- 1 Function Pointer in C/C++ type
- 1.1 ordinary function Pointer
- 1.2 non-static member function of class
- 1.3 Lambda To Function Pointer
- 1.4 总结什么是指针
- 2 Returning a function pointer from a function in C/C++
- 3. C – Variable Arguments (Variable length arguments)
- 4. Variadic Template
- 5 Variadic Template with member function pointer
- 6 最终解析
- X.Refference
0 前言
就像C++其他类型一样,函数也拥有指针,不过不得不说C++和C的函数指针非常抽象,语法空前绝后。加之C++有C的一面,有面向对象的一面,还有面向模板的一面,在《Effective C++》里,作者第一条就点明题意,不能把C++当成1种语言来看,而是4种,每种语言都有独特的风情,而混合起来,你甚至得学习一点密码学…
接下来这段代码(来自小彭老师),核心功能是注册GLFW的回调函数,即接受用户的键盘输入,变换相机位姿进行模型显示。
但看起来却让人望而却步。下面将对此代码进行解读。
template static void (*_impl_glfw_input_callback(void (InputCtl::*pFn)(Ts...)))(GLFWwindow *, Ts...) { static void (InputCtl::*gpFn)(Ts...); gpFn = pFn; return [] (GLFWwindow *window, Ts ...args) -> void { auto game = (Game *)glfwGetWindowUserPointer(window); if (game) [[likely]] { (game->m_inputCtl.*gpFn)(args...); } };}template static auto glfw_input_callback(FpFn fpFn) { return _impl_glfw_input_callback(fpFn());}// usageglfwSetCursorPosCallback(window, glfw_input_callback([] { return &InputCtl::cursor_pos_callback; }));
1 Function Pointer in C/C++ type1.1 ordinary function Pointer
以下这段代码来自 Author Vysandeep3
// C++ program for the above approach#include using namespace std;void demo(int& a){ a += 10;} // Driver Codeint main(){ int num = 20; // Now ptr contains address of demo // function or void void (*ptr)(int*) = &demo; // or (*ptr)(num); ptr(num); cout << num << endl; return 0;}
returnType (*function_pointer_name)(Type a, Type b, Type ... n)
其中 function_pointer_name
定义了一个变量,他可以存储类似 returnType XXXX(Type a, Type b, Type ... n)
这种形式函数的指针。
但是有些时候我们有多个这种类型的函数,例如
int add(int a, int b);int sub(int a, int b);int mul(int a, int b);int rat(int a, int b);int (*ptr)(int, int) = NULL;if(a == b) {ptr = &add;}else{ptr = &mul;}
我们需要在main()函数里决定什么时间什么条件一个这种类型的指针指向的函数,需要一段代码来完成这种操作。
问题是,我们可不可以写一个函数来完成这种操作呢?这也是一种重构的思想,当一段代码可能需要用到多次的时候,为什么不把他写成一个函数呢?
1.2 non-static member function of class
Its type is int (Fred::*)(char,float)
if a non-static member function of class Fred
Note: if it’s a static member function of class Fred, its type is the same as if it were an ordinary function: “int (*)(char,float)”.
https://isocpp.org/wiki/faq/pointers-to-members
float (SomeClass::*my_memfunc_ptr)(int, char *);// For const member functions, it's declared like this:float (SomeClass::*my_const_memfunc_ptr)(int, char *) const;my_memfunc_ptr = &SomeClass::some_member_func;// This is the syntax for operators:my_memfunc_ptr = &SomeClass::operator !;// There is no way to take the address of a constructor or destructor
给出一篇学习资料: Member Function Pointers and the Fastest Possible C++ Delegates by Don Clugston
1.3 Lambda To Function Pointer
#include using namespace std;#define PI(x) x, #x, x##xauto noCapture = [](int res) -> float { std::cout << "No capture lambda called with " << res << "\n"; return 99.9f; }; typedef float(*NormalFuncType)(int);int main(){ NormalFuncType noCaptureLambdaPtr = noCapture; //----------- (1) float res = noCaptureLambdaPtr(100); //----------- (2) return 0;}// COUT// No capture lambda called with 100
注意这东西的地址需要用 auto noCapture = [](int res) -> float{}
来接。除此之外,就当成一个普通的函数指针就行。
给出一篇学习资料: How To Bind Lambda To Function Pointer
1.4 总结什么是指针
int* pInt;char* pChar;
一个指针,指向一块内存中的地址(存储地址)。但是同时他又有对应的类型,char*
意为从这个地址开始读取1个字节,int*
意为从这个地址开始读取4个字节。这就是指针的核心。指针类型决定了程序如何对待一个地址。
另外C语言可以通过2个指针实现面向对象编程。当然正常的面向对象编程也是需要2个指针(*this
, *underThis
)。想要深入了解的话,可以搜索 opaque-pointers 这方面的知识。
给出一篇学习资料: Practical Design Patterns: Opaque Pointers and Objects in C
2 Returning a function pointer from a function in C/C++
以下这段代码来自 Author Vysandeep3
#include using namespace std; int add(int a, int b) { return a + b;} int subtract(int a, int b) { return a - b;} int (*get_operation(char op))(int, int) { if (op == '+') { return &add; } else if (op == '-') { return &subtract; } else { return NULL; }} int main() { int (*op)(int, int) = get_operation('+'); int result = op(3, 4); cout << "Result: " << result << endl; return 0;}
int (*get_operation(char op))(int, int)
:
- 其中 get_operation(char op) 是一个返回函数指针的函数
- int (*) (int, int) 是返回的函数指针所指向的函数类型
这东西看起来确实很怪…, 但是我们只能接受。
这里给出一种理解方式, 首先一个指针需要两个标识符 Type*
ptr_name
:
int* ptr; // ptr is a pointer to an integerint(*)(int, int);// key idea: function pointer type// ptr lost a pointerType like int*int (*ptr)(int, int);// ptr is a pointer to a function that takes that takes two arguments and returns an integer// int(*)(int, int) ptr;//---------------------------------------------------------------------//int ptr(char op); // ptr is a function that takes that takes one char type argument and returns an integer// ptr() lost a returnType like intint (*ptr(char op))(int, int){};// ptr() is a function that takes one char argument returns a pointer to a function which two arguments and returns an integer.// int(*)(int, int) ptr(char op) {};
3. C – Variable Arguments (Variable length arguments)
printf("Some values: %d, %s, %c!", 4, "foo", 'z')
#include void my_printf(char* format, ...){ va_list argp; va_start(argp, format); while (*format != '\0') { if (*format == '%') { format++; if (*format == '%') { putchar('%'); } else if (*format == 'c') { char char_to_print = va_arg(argp, int); putchar(char_to_print); } else { fputs("Not implemented", stdout); } } else { putchar(*format); } format++; } va_end(argp);}
The C library macro void va_start(va_list ap, last_arg)
initializes ap variable to be used with the va_arg and va_end macros. The last_arg is the last known fixed argument being passed to the function i.e. the argument before the ellipsis.
https://www.tutorialspoint.com/cprogramming/c_variable_arguments.htm
https://jameshfisher.com/2016/11/23/c-varargs/
https://www.tutorialspoint.com/c_standard_library/c_macro_va_start.htm
4. Variadic Template
C++ Primer P700.
这个东西说白了,就是类似C – Variable Arguments,可以接收任意长度的函数参数,不过与C – Variable Arguments这种需char* format
来自己告知函数对应参数的类型。Variadic Template 会自动生成相应的函数定义以及声明,这是模板编程的优势。详情看下面的实例代码。
// Args is a template parameter pack; rest is a function parameter pack// Args represents zero or more template type parameters// rest represents zero or more function parameterstemplate void foo(const T &t, const Args& ... rest);int i = 0; double d = 3.14; string s = "how now brown cow";foo(i, s, 42, d); // three parameters in the packfoo(s, 42, "hi"); // two parameters in the packfoo(d, s); // one parameter in the packfoo("hi"); // empty pack
the compiler will instantiate four different instances of foo:
void foo(const int&, const string&, const int&, const double&);void foo(const string&, const int&, const char(&)[3]);void foo(const double&, const string&);void foo(const char(&)[3]);
In each case, the type of T is deduced from the type of the first argument. The
remaining arguments (if any) provide the number of, and types for, the additional
arguments to the function.
#includeusing namespace std;template void g(Args ... args) { cout << sizeof...(Args) << endl; // number of type parameters cout << sizeof...(args) << endl; // number of function parameters}int main(){ g(1,2,3,4); return 0;}/**4*4*/
5 Variadic Template with member function pointer
当 Variadic Template 来接收 member function pointer时,不需要显式的声明成员函数的参数类型,编译器会自动推导。
#include class A{ public: void func(int xpos, int ypos);};void A::func(int xpos, int ypos){ printf("Hello World!");}template void (* Test(void (A::*pFn)(Ts...)))(Ts ...){return nullptr;};/* First instantiated from: insights.cpp:19 */#ifdef INSIGHTS_USE_TEMPLATEtemplatevoid (*Test(void (A::*pFn)(int, int)))(int, int){ return nullptr;}#endif;int main(){ A a; Test(&A::func); // line == 19 return 0;}
https://cppinsights.io/
https://adroit-things.com/programming/c-cpp/how-to-bind-lambda-to-function-pointer/
https://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible
6 最终解析
template static void (*_impl_glfw_input_callback(void (InputCtl::*pFn)(Ts...)))(GLFWwindow *, Ts...) { static void (InputCtl::*gpFn)(Ts...); gpFn = pFn; return [] (GLFWwindow *window, Ts ...args) -> void { auto game = (Game *)glfwGetWindowUserPointer(window); if (game) [[likely]] { (game->m_inputCtl.*gpFn)(args...); } };}template static auto glfw_input_callback(FpFn fpFn) { return _impl_glfw_input_callback(fpFn());}// usageglfwSetCursorPosCallback(window, glfw_input_callback([] { return &InputCtl::cursor_pos_callback; }));
glfw_input_callback([] { return &InputCtl::cursor_pos_callback; })
传入一个lambda
函数指针, 类型使用template
的FpFn
自动定义,函数指针值使用fpFn
承接。_impl_glfw_input_callback(fpFn());
fpFn()
调用匿名函数,返回&InputCtl::cursor_pos_callback
成员函数指针。Variadic Template with member function pointer
template static void (*_impl_glfw_input_callback(void (InputCtl::*pFn)(Ts...)))(GLFWwindow *, Ts...)
_impl_glfw_input_callback(void (InputCtl::*pFn)(Ts...))
使用模板自动承接相应的成员函数指针,不必明确指出函数的参数等信息。
- 函数调用
return [] (GLFWwindow *window, Ts ...args) -> void {// Game class 的 *this 指针 auto game = (Game *)glfwGetWindowUserPointer(window); if (game) [[likely]] {// 成员函数调用 (game->m_inputCtl.*gpFn)(args...); } };
注册回调函数的核心无非就是执行回调函数中的代码。
X.Refference
- Author Vysandeep3
- https://isocpp.org/wiki/faq/pointers-to-members
- Member Function Pointers and the Fastest Possible C++ Delegates by Don Clugston
- How To Bind Lambda To Function Pointer
- Practical Design Patterns: Opaque Pointers and Objects in C
- Author Vysandeep3
- https://www.learncpp.com/cpp-tutorial/introduction-to-pointers/
- https://www.tutorialspoint.com/cprogramming/c_variable_arguments.htm
- https://jameshfisher.com/2016/11/23/c-varargs/
- https://www.tutorialspoint.com/c_standard_library/c_macro_va_start.htm
- https://cppinsights.io/
- https://adroit-things.com/programming/c-cpp/how-to-bind-lambda-to-function-pointer/
- https://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible
- 小彭老师 OPENGL 课程实验源代码
如果我的工作对您有帮助,您想回馈一些东西,你可以考虑通过分享这篇文章来支持我。我非常感谢您的支持,真的。谢谢!
作者:Dba_sys (Jarmony)
转载以及引用请注明原文链接:https://www.cnblogs.com/asmurmur/p/17826429.html
本博客所有文章除特别声明外,均采用CC 署名-非商业使用-相同方式共享 许可协议。