C++ 返回函数指针的函数

这篇具有很好参考价值的文章主要介绍了C++ 返回函数指针的函数。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录
  • 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的回调函数,即接受用户的键盘输入,变换相机位姿进行模型显示。

C++ 返回函数指针的函数

C++ 返回函数指针的函数

但看起来却让人望而却步。下面将对此代码进行解读。

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; }));
  1. glfw_input_callback([] { return &InputCtl::cursor_pos_callback; })
    传入一个lambda函数指针, 类型使用 template <class FpFn> FpFn自动定义,函数指针值使用 fpFn承接。

  2. _impl_glfw_input_callback<FpFn>(fpFn());
    fpFn()调用匿名函数,返回 &InputCtl::cursor_pos_callback 成员函数指针。

  3. 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...)) 使用模板自动承接相应的成员函数指针,不必明确指出函数的参数等信息。

  1. 函数调用
return [] (GLFWwindow *window, Ts ...args) -> void {
		// Game class 的 *this 指针
        auto game = (Game *)glfwGetWindowUserPointer(window);
        if (game) [[likely]] {
		// 成员函数调用
            (game->m_inputCtl.*gpFn)(args...);
        }
    };

注册回调函数的核心无非就是执行回调函数中的代码文章来源地址https://www.toymoban.com/news/detail-747922.html

X.Refference

  1. Author Vysandeep3
  2. https://isocpp.org/wiki/faq/pointers-to-members
  3. Member Function Pointers and the Fastest Possible C++ Delegates by Don Clugston
  4. How To Bind Lambda To Function Pointer
  5. Practical Design Patterns: Opaque Pointers and Objects in C
  6. Author Vysandeep3
  7. https://www.learncpp.com/cpp-tutorial/introduction-to-pointers/
  8. https://www.tutorialspoint.com/cprogramming/c_variable_arguments.htm
  9. https://jameshfisher.com/2016/11/23/c-varargs/
  10. https://www.tutorialspoint.com/c_standard_library/c_macro_va_start.htm
  11. https://cppinsights.io/
  12. https://adroit-things.com/programming/c-cpp/how-to-bind-lambda-to-function-pointer/
  13. https://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible
  14. 小彭老师 OPENGL 课程实验源代码

到了这里,关于C++ 返回函数指针的函数的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C语言指针操作(八)返回指针值的函数详解

    一个函数可以返回一个整型值、字符值、实型值等,也可以返回指针型的数据,即地址。其概念与以前类似,只是返回的值的类型是指针类型而已。 目录 一、引入 二、返回指针值的函数的定义 2.1举例说明1 2.2举例说明2 一个函数可以返回一个整型值、字符值、实型值等,也

    2024年02月07日
    浏览(43)
  • Golang-指针(pointer)

    指针:指向内存地址的变量,指针用来存储变量的内存地址 Go 语言定义变量必须声明数据类型,因为不同数据类型的数据占用不同的存储空间,导致内存地址分配大小各不相同,所有指针只能存放同一类型变量的内存地址。 指针分为两种:类型指针和切片指针 类型指针允许

    2023年04月26日
    浏览(44)
  • unity异常:InvalidOperationException: Burst failed to compile the function pointer `Int32

    异常信息具体如下: InvalidOperationException: Burst failed to compile the function pointer `Int32 ValidateCollinear$BurstManaged(Unity.Mathematics.float2*, Int32, Single)` Unity.Burst.BurstCompiler.Compile (System.Object delegateObj, System.Reflection.MethodInfo methodInfo, System.Boolean isFunctionPointer, System.Boolean isILPostProcessing) (at Lib

    2024年02月06日
    浏览(44)
  • C++多态与虚拟:函数重载(Function Overloading)

    重载(Overloading):所谓重载是指不同的函数实体共用一个函数名称。例如以下代码所提到的CPoint之中,有两个member functions的名称同为x():    其两个member functions实现代码如下: 函数名称相同,但参数不同(个数不同,型别也不同),实现代码也不相同。C++之所以有function

    2024年04月25日
    浏览(40)
  • 【C++】c++11新特性(二)--Lambda函数及function(包装器)

    目录 Lambda函数 基本概念 基本语法 lambda 捕获(capture) 1. 不捕获任何变量 2. 按值捕获 3. 按引用捕获 4. 混合捕获 5. 捕获this 指针 包装器 function 基本概念 使用场景 1. 给function对象赋值  2. 作为函数参数和返回值   3. 存储在容器中 4. 绑定成员函数和带参数的函数        la

    2024年04月27日
    浏览(42)
  • C++指针解读(7)-- 指针和函数

    前面我们讲过用指针变量作为函数参数。这里讲指向函数的指针变量和返回指针的函数。 1、指向函数的指针变量 跟变量一样,函数也会存储在内存空间中,函数的内存空间有一个起始地址,每次调用函数时就从该入口地址开始执行函数代码。 既然函数有地址,我们就可以定

    2024年02月07日
    浏览(43)
  • 《Git入门实践教程》前言+目录

    版本控制系统(VCS)在项目开发中异常重要,但和在校大学生的交流中知道,这个重要方向并未受到重视。具备这一技能,既是项目开发能力的体现,也可为各种面试加码。在学习体验后知道,Git多样化平台、多种操作方式、丰富的资源为业内人士提供了方便的同时,也造成

    2024年02月10日
    浏览(72)
  • FPGA学习实践之旅——前言及目录

    很早就有在博客中记录技术细节,分享一些自己体会的想法,拖着拖着也就到了现在。毕业至今已经半年有余,随着项目越来越深入,感觉可以慢慢进行总结工作了。趁着2024伊始,就先开个头吧,这篇博客暂时作为汇总篇,记录在这几个月以及之后从FPGA初学者到也算有一定

    2024年02月03日
    浏览(58)
  • c++函数参数和返回值

    c++函数参数和返回值 函数存储位置 函数参数入栈顺序 初始化列表 函数的返回值 用参数引用来返回 返回一个参数指针 返回一个对象 总结 函数的几种变体 inline 函数 函数对象 lambda 函数 c++一直以来是一个关注效率的代码,这样关于函数的参数传递和返回值的接收,是重中之

    2024年02月05日
    浏览(42)
  • Go invalid memory address or nil pointer dereference错误 空指针问题

    Go 指针声明后赋值,出现 panic: runtime error: invalid memory address or nil pointer dereference,这种是内存地址错误。 首先我们要了解 指针,指针地址 在 Go 中 * 代表取指针地址中存的值, 代表取一个值的地址 对于指针,我们一定要明白指针储存的是一个值的地址,但本身这个指针也需

    2024年02月05日
    浏览(59)