目錄0 前言1 Function Pointer in C/C++ type1.1 ordinary function Pointer1.2 non-static member function of class1.3 Lambda To Function Pointer1.4 總結什麼是指針2 R ...
目錄
- 0 前言
- 1 Function Pointer in C/C++ type
- 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 <class, class ...Ts>
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 <class FpFn>
static auto glfw_input_callback(FpFn fpFn) {
return _impl_glfw_input_callback<FpFn>(fpFn());
}
// usage
glfwSetCursorPosCallback(window, glfw_input_callback([] { return &InputCtl::cursor_pos_callback; }));
1 Function Pointer in C/C++ type
1.1 ordinary function Pointer
以下這段代碼來自 Author Vysandeep3
// C++ program for the above approach
#include <iostream>
using namespace std;
void demo(int& a)
{
a += 10;
}
// Driver Code
int 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 <iostream>
using namespace std;
#define PI(x) x, #x, x##x
auto 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 <iostream>
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 integer
int(*)(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 int
int (*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) {};
https://www.learncpp.com/cpp-tutorial/introduction-to-pointers/
3. C - Variable Arguments (Variable length arguments)
printf("Some values: %d, %s, %c!", 4, "foo", 'z')
#include <stdarg.h>
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 parameters
template <typename T, typename... Args>
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 pack
foo(s, 42, "hi"); // two parameters in the pack
foo(d, s); // one parameter in the pack
foo("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.
#include<iostream>
using namespace std;
template<typename ... Args> 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 <cstdio>
class A{
public:
void func(int xpos, int ypos);
};
void A::func(int xpos, int ypos){
printf("Hello World!");
}
template <class ...Ts>
void (* Test(void (A::*pFn)(Ts...)))(Ts ...){
return nullptr;
};
/* First instantiated from: insights.cpp:19 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
void (*Test<int, int>(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 <class, class ...Ts>
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 <class FpFn>
static auto glfw_input_callback(FpFn fpFn) {
return _impl_glfw_input_callback<FpFn>(fpFn());
}
// usage
glfwSetCursorPosCallback(window, glfw_input_callback([] { return &InputCtl::cursor_pos_callback; }));
-
glfw_input_callback([] { return &InputCtl::cursor_pos_callback; })
傳入一個lambda
函數指針, 類型使用template <class FpFn>
的FpFn
自動定義,函數指針值使用fpFn
承接。 -
_impl_glfw_input_callback<FpFn>(fpFn());
fpFn()
調用匿名函數,返回&InputCtl::cursor_pos_callback
成員函數指針。 -
Variadic Template with member function pointer
template <class, class ...Ts>
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 署名-非商業使用-相同方式共用 許可協議。