Is a pointer to a function which have unknown number of parameters possible?
I am writing a simple class to measure performance of a function in terms of time . The user should be able to send a pointer to his function, parameters of the function, times to call the function and i will call the function, return the time elapsed. Here my problem is I dont know how many parameters the user's function takes! I thought to use variadic functions to get unknown number of parameters,开发者_如何学编程 however I still have the problem of declaring the function pointer that user passes as a parameter(because it doesnt have constant variable number) and not knowing the types of variables I recieve by using a variadic function.
It should not be hard, i guess :) All i want to do is to call a function which is not defined by me by using a function pointer.
Is there any way to solve these problems or?
It doesn't really make sense to have a function-pointer to a function with unknown arguments. If you don't know how many arguments there are (let alone their types), how are you going to fill out the arguments at run-time?
The best you can do is require that the user's functions all have the same prototype, namely to take a va_list
as a parameter, and require that your library provide your library with that same va_list
(see also http://c-faq.com/varargs/handoff.html).
e.g.:
// Function-pointer type
typedef void (*func_t)(int, va_list);
// Your timer library function
void timer(func_t *p_func, ...)
{
va_list arg;
va_start(arg, fmt);
p_func(0, arg);
va_end(arg);
}
// User's function
void user_function(int first_arg, va_list args)
{
...
};
// Invoke the timer library function
timer(&user_function, arg1, arg2, arg3);
I think lambda functions can be used to do this:
template< typename Func >
unsigned int measure(Func f)
{
// take time
f();
// take time
return result;
}
void test_func_1(int i) { std::cout << i; }
void test_func_2(std::ostream& os) { os << 42; }
int main()
{
auto lambda_func_1 = [](){ test_func_1(42); };
const unsigned int time_1 = measure( lambda_func_1 );
std::cout << "calling test_func_1(42) took " << time_1 << " <unit>\n";
auto lambda_func_2 = [](){ test_func_2(std::cerr); };
const unsigned int time_2 = measure( lambda_func_2 );
std::cout << "calling test_func_2(std::cout) took " << time_2 << " <unit>\n";
return 0;
}
Of course, lambda functions will be part of C++ only after the next standard is released (hopefully this year), but quite a few compilers (among them GCC and VC) already implement them, so you have a chance to do it this way.
One might also make a function template employing variadic template arguments and perfect forwarding, passing the arguments of the function to be measured to the measuring function, which then passes them on. But I haven't played with this, so I can't write this up.
If you want a solution that works without the need of variadic templates or lambdas (for which you will have to wait for the upcoming standard release), you can use boost::bind
to make the function into one that takes no parameters by pre-binding all the parameters to the function:
#include <boost/bind.hpp>
#include <iostream>
template <typename Func>
int time_call(Func f) {
int start_time = some_get_current_time(); //record the start time
f(); // call the function with no parameters
return some_get_current_time() - start_time; //return the time difference.
};
void my_algorithm(int x, float w, std::string s) {
std::cout << x << w << s << std::endl;
};
int main() {
int time_taken = time_call(boost::bind(&my_algorithm, 42, 0.3, "Hello World!"));
std::cout << "The function took " << time_taken << " time-units to execute!" << std::endl;
return 0;
};
The above will work on all compilers and doesn't require any of the features of C++0x. Note also, that in the call to boost::bind
, you can place any variables (they don't have to be literal constants). And, also, boost::bind
can work with pointer to member functions too.
EDIT: If you are wondering how boost::bind
can accomplish this without variadic templates, well, it's simple, they just made one function template overload for all possible numbers of parameters (I think the default limit is 10 parameters, but it can be extended).
Unfortunately, current C++ requires you to write a bunch of templates with varying lengths, one for each possible argument count. In principle, C++0x would allow you to use variadic templates like so:
template<typename Rv, typename Wrapper, typename... Args>
struct impl_wrapper {
std::function<Rv (Args...)> func;
Rv operator()(Args... args) const {
Wrapper w;
return func(args...);
}
impl_wrapper(const std::function<Rv (Args...)> f)
: func(f)
{
}
};
template<typename Wrapper>
struct wrap_func_ {
template<typename Rv, typename... Args>
impl_wrapper<Rv, Args...> operator()(const std::function<Rv (Args...)> &f)
{
return impl_wrapper<Rv, Wrapper, Args...>(f);
}
};
template<typename Wrapper>
static wrap_func_<Wrapper> wrap_func;
struct test_wrapper {
test_wrapper() {
std::cout << "Begin call!\n";
}
~test_wrapper() {
std::cout << "End call!\n";
}
};
int test_call(int x, char *y) {
std::cout << y << x << std::endl;
return x + 1;
}
int main() {
std::function<int (int, char *)> f = test_call;
f = wrap_func<test_wrapper>(f);
std::cout << "Returned: " << f(42, "Prior to increment: ") << std::endl;
return 0;
}
However, this requires support for features not yet implemented in G++, nor, most likely, in any other extant C++ compiler:
test.cpp:21: sorry, unimplemented: cannot expand ‘Args ...’ into a fixed-length argument list
Therefore, you must instead use template overloading for each possible argument count, up to some reasonable maximum.
精彩评论