如何组合lambda,vairadic函数和函数指针?

How to combine lambda, vairadic functions and function pointers?

本文关键字:函数 vairadic 指针 lambda 何组合 组合      更新时间:2023-10-16

我有一个好主意,它简化了(对我来说(很多事情。

假设你有一个函数,它接受一个带有 x 参数的函数,如果事件发生,该函数将被执行。为了简化这一点,你用typedef定义了一个新的类型,这是一个函数指针。

typedef void (*HandlerFunction)(...);
void setHandler(HandlerFunction fun) {...}

现在,您可以使用 setHandler 函数来...使用带有其他 sepsific 参数的 lambda 函数设置处理程序,因为您知道将始终使用这些特定参数调用此函数。

setHandler([](int i, std::string arg) {
std::cout << "Event with i=" << i << " and arg=" << arg << "!" << std::endl;
});

现在的问题是...这在C++甚至可能吗,什么时候是,如何?

可变参数

我想通了,如何使用可变参数列表来做到这一点。诀窍是在可更改函数周围使用包装器并使用va_copy

#include <iostream>
#include <functional>
#include <cstdarg>
std::function<void (int, va_list)> foo = [] (int numargs, va_list args)
{
va_list args_copy;
va_copy(args_copy, args);
std::cout << "Old Foo " <<va_arg(args_copy,int) << std::endl;
va_end(args_copy);
};
void set_foo(std::function<void (int, va_list)> new_foo)
{
foo = new_foo;
}
void wrapper(int numargs, ...)
{
va_list args;
va_start(args, numargs);
foo(numargs, args);
va_end(args);
}
int main() {
wrapper(123,456);
set_foo([](int numargs, va_list args)
{
va_list args_copy;
va_copy(args_copy, args);
std::cout << "New Foo " << va_arg(args_copy,int) << std::endl;
va_end(args_copy);
});
wrapper(123,456);
return 0;
}

这导致:

Old Foo 456
New Foo 456

我不知道为什么需要包装器,也许有人知道。但它:P有效


已知参数

您可以使用initializer_list将特定类型的可变参数传递给变量 lambda。

#include <iostream>
#include <functional>
#include <initializer_list>
std::function<void (std::initializer_list<int>)> foo = [](std::initializer_list<int> args) 
{
for (auto i: args) std::cout << "Old Foo: "<< i << 'n';
};
void setFoo(std::function<void (std::initializer_list<int>)> new_foo) {
foo = new_foo;
};

int main()
{
foo({2, 4, 5});
std::cout << std::endl;
setFoo([](std::initializer_list<int> args) 
{
for (auto i: args) std::cout << "New Foo: "<< i << 'n';
});
foo({2, 4});
}

这将返回:

Old Foo: 2
Old Foo: 4
Old Foo: 5
New Foo: 2
New Foo: 4

在我看来,我已经找到了最好的解决方案。

我使用此类作为函数的容器:

#define C(...) __VA_ARGS__
#define F(a, ...) (std::function<void(__VA_ARGS__)>)[ a ](__VA_ARGS__)
#pragma once
#include <memory>
#include <functional>
class HandlerFunc {
private:
struct function_b {
virtual ~function_b() {};
};
template<typename... Args>
struct func : function_b {
typedef std::function<void(Args...)> func_t;
func_t funct;
func(func_t f) : funct(f) {};
};
std::unique_ptr<function_b> fb;
public:
template <typename... Args>
HandlerFunc(std::function<void(Args...)> f) : fb(new func<Args...>(f)) { };
template <typename... Args>
HandlerFunc& operator=(std::function<void(Args...)> f) {
fb = std::unique_ptr<function_b>(new func<Args...>(f));
return *this;
};
template <typename... Args>
void operator()(Args... args) const {
func<Args...>* f = dynamic_cast<func<Args...>*>(fb.get());
if (f)
f->funct(args...);
else
throw std::runtime_error("Invalid arguments to function object call!");
};
};

现在可以编写这样的代码:

std::vector<HandlerFunc> funcs;
template<typename... Args>
void add(std::function<void(Args...)> func) {
funcs.push_back(func);
}
int main() {
int a = 321;
int b = 123;
add(F(C(a, b), int i){
std::cout << a << b << i << std::endl;
});
add(F(C(=), std::string str){
std::cout << b << a << str << std::endl;
});
int i = 0;
for (auto& func : funcs) {
if (i < 1) func(1234);
else func("meep");
}
}

我唯一想要的一件事,是一个转换这样的东西的 #define

add(F([=], int i, std::string str){
...
});

进入这个

add((std::function<void(int, std::string))[=](int i, std::string str){
...
});

我对预处理器之类的东西很陌生^^

我想象你想做一些看起来像这样的事情:

Delegator delegator;
delegator.set_handler(handler);

不幸的是,在声明Delegator时,必须知道handler的类型或其类型签名(带std::function(。 根据你想做什么,这还不错。 如果库的用户可以一次性定义一堆处理程序,并且您可以向用户枚举处理程序应该处理的类型,例如

//The library user defines this
struct MyHandlers {
void operator()(int){ 
std::cout << "Event with i=" << i << std::endl;
}
template<class T>
void operator()(std::string str, T t_){  
std::cout << "Event with str=" << str << " and some class=" << t_ << "!" << std::endl;
}
void operator()(std::string str){ /** more code **/ }
};

然后,您可以执行类似于您最初想要做的事情。

//This would go in the library
template<class Handlers>
struct Delegator_t {
Handlers handlers;
template<class T>
void do_stuff_which_eventually_calls_handlers(T t_) {
std::string string = "string";
if (/** condition **/) {
handlers(5);
} else {
handlers(string, t_);
}
//The user's third overload never gets called.
}
}
template<class Handlers>
Delegator_t<Handlers> Delegator(Handlers handlers){ return{std::move(handlers)}; }

最后,用户可以通过以下方式使用您的库及其MyHandlers

auto delegator = Delegator(MyHandlers{});
SomeClass some_class(/** some args **/);
delegator.do_stuff_which_eventually_calls_handlers(some_class);

当然,如果用户必须在不同的时间点向同一个委托人注册各种处理程序,则上述方法是无用的。

你可以做的一些事情

#include <iostream>
#include <functional>
void exec_handler(std::function<void(int, std::string)> cb) {
static int cnt = 0;
cb(cnt++, "from_exec_handler");
}
void handler1(int count, std::string name) {
std::cout << "handler1t" << count << " " << name << std::endl;
}
void handler2(int count, std::string name, std::string name2) {
std::cout << "handler2t" << count << " " << name << " " << name2 << std::endl;
}

int main(int argc, char *argv[]) {
auto handler_lambda = [](int count, std::string name) {
std::cout << "lambdatt" << count << " " << name << std::endl;
};
auto clojure = [](int count, std::string name) {
return handler2(count, name, "clojure");
};
auto partial = std::bind(handler2, std::placeholders::_1, std::placeholders::_2, "partial");
exec_handler(handler1);
exec_handler(handler_lambda);
exec_handler(clojure);
exec_handler(partial);
}

输出:

handler1        0 from_exec_handler
lambda          1 from_exec_handler
handler2        2 from_exec_handler clojure
handler2        3 from_exec_handler partial