캡처를 함수 포인터로 사용하는 C ++ 람다
나는 C ++ 람다와 함수 포인터로의 암시 적 변환을 가지고 놀았습니다. 내 시작 예제는 ftw 함수의 콜백으로 사용했습니다. 이것은 예상대로 작동합니다.
#include <ftw.h>
#include <iostream>
using namespace std;
int main()
{
auto callback = [](const char *fpath, const struct stat *sb,
int typeflag) -> int {
cout << fpath << endl;
return 0;
};
int ret = ftw("/etc", callback, 1);
return ret;
}
캡처를 사용하도록 수정 한 후 :
int main()
{
vector<string> entries;
auto callback = [&](const char *fpath, const struct stat *sb,
int typeflag) -> int {
entries.push_back(fpath);
return 0;
};
int ret = ftw("/etc", callback, 1);
for (auto entry : entries ) {
cout << entry << endl;
}
return ret;
}
컴파일러 오류가 발생했습니다.
error: cannot convert ‘main()::<lambda(const char*, const stat*, int)>’ to ‘__ftw_func_t {aka int (*)(const char*, const stat*, int)}’ for argument ‘2’ to ‘int ftw(const char*, __ftw_func_t, int)’
약간 읽은 후. 캡처 를 사용하는 람다는 암시 적 으로 함수 포인터 로 변환 될 수 없다는 것을 배웠습니다 .
이에 대한 해결 방법이 있습니까? "암시 적으로"변환 될 수 없다는 사실은 "명시 적으로"변환 될 수 있다는 것을 의미합니까? (성공하지 않고 캐스팅을 시도했습니다). 람다를 사용하여 항목을 일부 개체에 추가 할 수 있도록 작업 예제를 수정하는 깨끗한 방법은 무엇입니까?
람다 캡처는 상태를 보존해야하므로 단순한 "해결 방법"이 없습니다. 단순한 함수 가 아니기 때문 입니다. 함수 포인터의 요점은 단일 전역 함수를 가리키며이 정보에는 상태에 대한 공간이 없다는 것입니다.
가장 가까운 해결 방법 (기본적으로 상태 저장을 무시 함)은 람다 / 함수에서 액세스 할 수있는 전역 변수 유형을 제공하는 것입니다. 예를 들어, 전통적인 functor 객체를 만들고 고유 한 (전역 / 정적) 인스턴스를 참조하는 정적 멤버 함수를 제공 할 수 있습니다.
그러나 그것은 람다를 포착하는 모든 목적을 무너 뜨리는 것과 같습니다.
이 문제가 발생했습니다.
코드는 람다 캡처없이 잘 컴파일되지만 람다 캡처에는 형식 변환 오류가 있습니다.
C ++ 11의 솔루션은 사용하는 것입니다 std::function
(편집 : 함수 서명을 수정할 필요가없는 다른 솔루션이이 예제 뒤에 표시됨). boost::function
(실제로 훨씬 빠르게 실행 됨)을 사용할 수도 있습니다 . 예제 코드- gcc 4.7.1
다음 과 같이 컴파일되고 컴파일되도록 변경되었습니다 .
#include <iostream>
#include <vector>
#include <functional>
using namespace std;
int ftw(const char *fpath, std::function<int (const char *path)> callback) {
return callback(fpath);
}
int main()
{
vector<string> entries;
std::function<int (const char *fpath)> callback = [&](const char *fpath) -> int {
entries.push_back(fpath);
return 0;
};
int ret = ftw("/etc", callback);
for (auto entry : entries ) {
cout << entry << endl;
}
return ret;
}
편집 : 원래 함수 서명을 수정할 수 없었지만 여전히 람다를 사용해야하는 레거시 코드를 만났을 때 이것을 다시 방문해야했습니다. 원래 함수의 함수 서명을 수정할 필요가없는 솔루션은 다음과 같습니다.
#include <iostream>
#include <vector>
#include <functional>
using namespace std;
// Original ftw function taking raw function pointer that cannot be modified
int ftw(const char *fpath, int(*callback)(const char *path)) {
return callback(fpath);
}
static std::function<int(const char*path)> ftw_callback_function;
static int ftw_callback_helper(const char *path) {
return ftw_callback_function(path);
}
// ftw overload accepting lambda function
static int ftw(const char *fpath, std::function<int(const char *path)> callback) {
ftw_callback_function = callback;
return ftw(fpath, ftw_callback_helper);
}
int main() {
vector<string> entries;
std::function<int (const char *fpath)> callback = [&](const char *fpath) -> int {
entries.push_back(fpath);
return 0;
};
int ret = ftw("/etc", callback);
for (auto entry : entries ) {
cout << entry << endl;
}
return ret;
}
실물
Lambda 함수는 매우 편리하고 코드를 줄입니다. 제 경우에는 병렬 프로그래밍을 위해 람다가 필요했습니다. 그러나 캡처 및 함수 포인터가 필요합니다. 내 솔루션이 여기 있습니다. 그러나 캡처 한 변수의 범위에주의하십시오.
template<typename Tret, typename T>
Tret lambda_ptr_exec(T* v) {
return (Tret) (*v)();
}
template<typename Tret = void, typename Tfp = Tret(*)(void*), typename T>
Tfp lambda_ptr(T& v) {
return (Tfp) lambda_ptr_exec<Tret, T>;
}
예
int a = 100;
auto b = [&]() { a += 1;};
void (*fp)(void*) = lambda_ptr(b);
fp(&b);
반환 값이있는 예
int a = 100;
auto b = [&]() {return a;};
int (*fp)(void*) = lambda_ptr<int>(b);
fp(&b);
최신 정보
개선 된 버전
함수 포인터로 캡처가 포함 된 C ++ 람다에 대한 첫 번째 게시물이 게시 된 지 오래되었습니다. 저와 다른 사람들이 사용할 수 있었기 때문에 약간의 개선이있었습니다.
표준 함수 C 포인터 API는 void fn (void * data) 규칙을 사용합니다. 기본적으로이 규칙이 사용되며 람다는 void * 인수로 선언해야합니다.
향상된 구현
struct Lambda {
template<typename Tret, typename T>
static Tret lambda_ptr_exec(void* data) {
return (Tret) (*(T*)fn<T>())(data);
}
template<typename Tret = void, typename Tfp = Tret(*)(void*), typename T>
static Tfp ptr(T& t) {
fn<T>(&t);
return (Tfp) lambda_ptr_exec<Tret, T>;
}
template<typename T>
static void* fn(void* new_fn = nullptr) {
static void* fn;
if (new_fn != nullptr)
fn = new_fn;
return fn;
}
};
Exapmle
int a = 100;
auto b = [&](void*) {return ++a;};
캡처가있는 람다를 C 포인터로 변환
void (*f1)(void*) = Lambda::ptr(b);
f1(nullptr);
printf("%d\n", a); // 101
이런 식으로도 사용할 수 있습니다.
auto f2 = Lambda::ptr(b);
f2(nullptr);
printf("%d\n", a); // 102
반환 값을 사용해야하는 경우
int (*f3)(void*) = Lambda::ptr<int>(b);
printf("%d\n", f3(nullptr)); // 103
그리고 데이터가 사용되는 경우
auto b2 = [&](void* data) {return *(int*)(data) + a;};
int (*f4)(void*) = Lambda::ptr<int>(b2);
int data = 5;
printf("%d\n", f4(&data)); // 108
로컬 전역 (정적) 방법을 사용하면 다음과 같이 수행 할 수 있습니다.
template <class F>
auto cify_no_args(F&& f) {
static F fn = std::forward<F>(f);
return [] {
return fn();
};
}
우리가 가지고 있다고 가정
void some_c_func(void (*callback)());
그래서 사용법은
some_c_func(cify_no_args([&] {
// code
}));
이는 각 람다에 고유 한 서명이 있으므로 정적으로 만드는 것이 문제가되지 않기 때문에 작동합니다. 가변 개수의 인수와 동일한 메서드를 사용하는 모든 반환 유형이있는 일반 래퍼를 따릅니다.
template <class F>
struct lambda_traits : lambda_traits<decltype(&F::operator())>
{ };
template <typename F, typename R, typename... Args>
struct lambda_traits<R(F::*)(Args...)> : lambda_traits<R(F::*)(Args...) const>
{ };
template <class F, class R, class... Args>
struct lambda_traits<R(F::*)(Args...) const> {
using pointer = std::add_pointer<R(Args...)>::type;
static pointer cify(F&& f) {
static F fn = std::forward<F>(f);
return [](Args... args) {
return fn(std::forward<Args>(args)...);
};
}
};
template <class F>
inline typename lambda_traits<F>::pointer cify(F&& f) {
return lambda_traits<F>::cify(std::forward<F>(f));
}
그리고 비슷한 사용법
void some_c_func(int (*callback)(some_struct*, float));
some_c_func(cify([&](some_struct* s, float f) {
// making use of "s" and "f"
return 0;
}));
헤헤-꽤 오래된 질문이지만 여전히 ...
#include <iostream>
#include <vector>
#include <functional>
using namespace std;
// We dont try to outsmart the compiler...
template<typename T>
int ftw(const char *fpath, T callback) {
return callback(fpath);
}
int main()
{
vector<string> entries;
// ... now the @ftw can accept lambda
int ret = ftw("/etc", [&](const char *fpath) -> int {
entries.push_back(fpath);
return 0;
});
// ... and function object too
struct _ {
static int lambda(vector<string>& entries, const char* fpath) {
entries.push_back(fpath);
return 0;
}
};
ret = ftw("/tmp", bind(_::lambda, ref(entries), placeholders::_1));
for (auto entry : entries ) {
cout << entry << endl;
}
return ret;
}
There is a hackish way to convert a capturing lambda into a function pointer, but you need to be careful when using it:
https://codereview.stackexchange.com/questions/79612/c-ifying-a-capturing-lambda
Your code would then look like this (warning: brain compile):
int main()
{
vector<string> entries;
auto const callback = cify<int(*)(const char *, const struct stat*,
int)>([&](const char *fpath, const struct stat *sb,
int typeflag) -> int {
entries.push_back(fpath);
return 0;
});
int ret = ftw("/etc", callback, 1);
for (auto entry : entries ) {
cout << entry << endl;
}
return ret;
}
My solution, just use a function pointer to refer to a static lambda.
typedef int (* MYPROC)(int);
void fun(MYPROC m)
{
cout << m(100) << endl;
}
template<class T>
void fun2(T f)
{
cout << f(100) << endl;
}
void useLambdaAsFunPtr()
{
int p = 7;
auto f = [p](int a)->int {return a * p; };
//fun(f);//error
fun2(f);
}
void useLambdaAsFunPtr2()
{
int p = 7;
static auto f = [p](int a)->int {return a * p; };
MYPROC ff = [](int i)->int { return f(i); };
//here, it works!
fun(ff);
}
void test()
{
useLambdaAsFunPtr2();
}
Found an answer here: http://meh.schizofreni.co/programming/magic/2013/01/23/function-pointer-from-lambda.html
It converts lambda pointer
to void*
and convert back when needed.
to
void*
:auto voidfunction = new decltype(to_function(lambda))(to_function(lambda));
from
void*
:auto function = static_cast< std::function*>( voidfunction);
참고URL : https://stackoverflow.com/questions/7852101/c-lambda-with-captures-as-a-function-pointer
'Programing' 카테고리의 다른 글
PHP의`post_max_size`를 초과하는 파일을 정상적으로 처리하는 방법은 무엇입니까? (0) | 2020.09.25 |
---|---|
C #에서 C ++ 코드를 호출 할 수 있습니까? (0) | 2020.09.25 |
플롯하는 데 권장되는 방법은 matplotlib 또는 pylab입니까? (0) | 2020.09.25 |
스칼라 밑줄-오류 : 확장 함수에 대한 매개 변수 유형이 없습니다. (0) | 2020.09.25 |
파일을 재귀 적으로 .gitignore하는 방법 (0) | 2020.09.25 |