2011-01-29 2 views
9

특정 시간 동안 매개 변수로 주어진 다른 함수를 저장하고 반복하는 함수를 만들려고합니다. 또는 반복합니다. 그러나 매개 변수로 함수를 전달하려면 먼저 모든 매개 변수를 알아야합니다. 함수를 매개 변수로 전달하고 매개 변수를 다른 매개 변수로 전달하려면 어떻게해야합니까? 사전에C++ : 매개 변수를 모른 채 함수를 다른 함수로 전달하려면 어떻게합니까?

void AddTimer(float time, int repeats, void (*func), params); // I know params has no type and that (*func) is missing parameters but it is just to show you what I mean 

감사

답변

3

현대의 C++과 관련하여 드레 이아스의 대답은 정확합니다.

관심을 끌기 위해 C 언어의 간단한 기술 솔루션이 C++에서 작동합니다. 임의의 매개 변수를 허용하는 대신 함수를 void (*func)(void*)으로 정의하고 "매개 변수"를 void*으로 만듭니다. 그런 다음 매개 변수를 포함 할 구조체를 정의하고 해당 수명주기를 관리하는 것은 호출자의 임무입니다. 일반적으로 발신자는 정말 호출 할 데 필요한 기능에 대한 간단한 래퍼 작성합니다 실제로

void myfunc(int, float); // defined elsewhere 

typedef struct { 
    int foo; 
    float bar; 
} myfunc_params; 

void myfunc_wrapper(void *p) { 
    myfunc_params *params = (myfunc_params *)p; 
    myfunc(params->foo, params->bar); 
} 

int main() { 
    myfunc_params x = {1, 2}; 
    AddTimer(23, 5, myfunc_wrapper, &x); 
    sleep(23*5 + 1); 
} 

당신을 "화재와 잊지"원하는 타이머를, 그래서 당신은이 방식을 사용하는 경우는 방법이 필요합니다 수 있습니다 모든 발사가 완료되면 타이머가 사용자 데이터 포인터를 비우도록 관리합니다.

분명히 이것은 유형 안전성이 제한적입니다. 원칙적으로 함수 포인터와 사용자 데이터 포인터를 제공하는 사람이 일치하는 것을 보장하는 데 많은 어려움이 있어서는 안되기 때문에 중요하지 않아야합니다. 실제로 사람들은 컴파일러가 버그에 대해 말하지 않았기 때문에 버그를 작성하는 방법과 방법을 찾습니다.

15

당신이 할 수있는 최선은 아니라 함수와 인수를 바인딩 std::bind 또는 boost::bind과 함께 인수로 std::function 또는 boost::function을 사용할 수 있습니다 :

void foo() { std::cout << "foo" << std::endl; } 
void bar(int x) { std::cout << "bar(" << x << ")" << std::endl; } 
struct test { 
    void foo() { std::cout << "test::foo" << std::endl; } 
}; 
void call(int times, boost::function< void() > f) 
{ 
    for (int i = 0; i < times; ++i) 
     f(); 
} 
int main() { 
    call(1, &foo);     // no need to bind any argument 
    call(2, boost::bind(&bar, 5)); 
    test t; 
    call(1, boost::bind(&test::foo, &t)); // note the &t 
} 

완전히 일반적인 함수 포인터를 전달할 때 본질적으로 잘못된 점이 있음을 유의하십시오. 어떻게 사용합니까? 호출 함수의 본문이 알 수없는 유형의 정의되지 않은 수의 인수를 전달할 수있는 것처럼 보이는 방법은 무엇입니까? 그것이 bind 템플릿이 해결하는 것입니다. 호출 할 때 사용할 인수 복사본과 함께 함수 포인터 (구체적인 함수 포인터)를 저장하는 클래스 펑터를 만듭니다 (이 예제에서는 &t을 참고하여 개체가 아닌 포인터가 복사되도록합니다).). bind의 결과는 알려진 인터페이스를 통해 호출 할 수있는 펑터입니다.이 경우에는 function< void() > 내부에 바인딩되어 인수없이 호출 될 수 있습니다.

+0

은 void 함수를 호출합니까? – Ninja

+0

@ 닌자 물론 그렇습니다. 호출자는 반환 유형이 무엇인지 모르는 경우 함수를 어떻게 사용할 수 있습니까? 당신은'boost :: function '을 사용하고 리턴 타입을 원하는 것으로 캐스팅 할 수있다. 현대적인 C++ 대안을 원한다면'boost :: function '을 사용하여 어떤 형식의 삭제로 재생할 수 있습니다. – kizzx2

1

실제로 함수 포인터에 관한 규칙이 없다면 void *를 사용하십시오.

+5

'void (*) (void)'타입의 값을 올바른 함수 포인터 타입으로 캐스팅하면'void (*) (void)'는'void *'보다 "generic" 원래 함수 포인터 값이 유지된다는 보장이 있습니다. 함수 포인터 값을'void *'로 캐스팅하고 리턴한다면 이것은 보장되지 않습니다. –

+0

@Charles : 어디에서 표준을 읽었습니까? –

+3

@DanielTrebbien : 5.2.10 [expr.reinterpret.cast]/6. –

2

그것은 당신이 다른 함수에 함수 포인터를 통과 한 다음 전화를 수있는 방법 그냥 예제 : 함수가 다른 유형 및/또는 매개 변수의 숫자를 걸리는 경우

마찬가지로
void AddTimer(float time, int repeats, void (*func)(int), int params) 
{ 
    //call the func 
    func(params); 
} 

void myfunction(int param) 
{ 
    //... 
} 

AddTimer(1000.0, 10, myfunction, 10); 

, 당신은 당신의 코드를 작성할 수 있습니다!

+0

+1 질문을 다시 읽은 후, 인수의 수와 유형이 알려지지 않았 음을 알고 있으며, 이것이 알려진 경우 내 것보다 더 간단한 해결책. –

+0

이것은 내가 실제로 물어 본 것이 아닙니다. 다윗의 답은 더 많이 사용 된 것처럼 보입니다. – Ninja

0

C++ 11에서는 모든 것이 필요한만큼 간단합니다. 타이머를 구현하십시오.

바운드 함수 호출을 전달하는 가장 간결한 방법은 람다 구문을 사용하여 생성 된 펑터를 전달하는 것입니다 (예 : []{ std::cout << "Hello, world!" << std::endl; }).이렇게 생성 된 객체는 컴파일러에만 알려진 유형이지만 유형은 std::function<void()>으로 변환됩니다.

#include <functional> 
#include <list> 
#include <chrono> 
#include <thread> 
#include <iostream> 

template <typename Clock = std::chrono::high_resolution_clock> 
class Timers { 
public: 
    using clock = Clock; 
    using duration = typename clock::duration; 
    using time_point = typename clock::time_point; 
private: 
    struct Timer { 
     duration const period; 
     std::function<void()> const call; 
     int repeats; 
     time_point next; 
     Timer(duration $period, int $repeats, std::function<void()> && $call) : 
     period($period), call(std::move($call)), repeats($repeats) {} 
    }; 
    std::list<Timer> m_timers; 
public: 
    Timers() {} 
    Timers(const Timers &) = delete; 
    Timers & operator=(const Timers &) = delete; 
    template <typename C> void add(std::chrono::milliseconds period, 
            int repeats, C && callable) 
    { 
     if (repeats) m_timers.push_back(Timer(period, repeats, callable)); 
    } 
    enum class Missed { Skip, Emit }; 
    void run(Missed missed = Missed::Emit) { 
     for (auto & timer : m_timers) timer.next = clock::now() + timer.period; 
     while (! m_timers.empty()) { 
     auto next = time_point::max(); 
     auto ti = std::begin(m_timers); 
     while (ti != std::end(m_timers)) { 
      while (ti->next <= clock::now()) { 
       ti->call(); 
       if (--ti->repeats <= 0) { 
        ti = m_timers.erase(ti); 
        continue; 
       } 
       do { 
        ti->next += ti->period; 
       } while (missed == Missed::Skip && ti->next <= clock::now()); 
      } 
      next = std::min(next, ti->next); 
      ++ ti; 
     } 
     if (! m_timers.empty()) std::this_thread::sleep_until(next); 
     } 
    } 
}; 

int main(void) 
{ 
    Timers<> timers; 
    using ms = std::chrono::milliseconds; 
    timers.add(ms(1000), 2, []{ std::cout << "Hello, world!" << std::endl; }); 
    timers.add(ms(100), 20, []{ std::cout << "*" << std::endl; }); 
    timers.run(); 
    std::cout << std::endl; 
    return 0; 
} 
관련 문제