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
спасибо заранее
5 ответов
dribeas' ответ правильный Что касается современный c++.
ради интереса, есть также простое решение lo-tech из мира C, которое, насколько это возможно, работает на C++. Вместо произвольных параметров, определяющих функцию void (*func)(void*)
, и сделать "params"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);
}
на практике вы хотите, чтобы" огонь и забыть " таймеры, так что если вы используете эту схему, вам также может понадобиться способ для таймера управлять, чтобы освободить указатель userdata после завершения всех стрельб.
очевидно, что это имеет ограниченную безопасность типа. В принципе in не должен иметь значения, потому что тот, кто поставляет указатель функции и указатель пользовательских данных, не должен иметь больших трудностей с обеспечением их соответствия. На практике конечно людей найти способы написания ошибок и способы обвинить вас в том, что их компилятор не сказал им об ошибках; -)
лучшее, что вы можете сделать, это использовать std::function
или boost::function
в качестве аргумента, вместе с std::bind
или boost::bind
, ну, связать аргументы с функцией:
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() >
и вызван без аргументов.
это просто пример того, как вы можете передать указатель функции другой функции, а затем вызвать его:
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);
аналогично, вы можете написать свой код, если ваша функция принимает другой тип или / и количество параметров!
В 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;
}