You can make this happen in C++11:
#include <type_traits>
#include <iostream>
template <typename Param, typename Arg>
Param take_address_if_necessary_impl (Arg&& arg, std::true_type, std::false_type)
{
return arg;
}
template <typename Param, typename Arg>
Param take_address_if_necessary_impl (Arg&& arg, std::false_type, std::true_type)
{
return &arg;
}
template <typename Param, typename Arg>
Param take_address_if_necessary (Arg&& arg)
{
return take_address_if_necessary_impl <Param> (
arg,
typename std::is_convertible <Arg, Param>::type {},
typename std::is_convertible <typename std::add_pointer <Arg>::type, Param>::type {}
);
}
template <typename Ret, typename... Params, typename... Args>
Ret call_special (Ret (*f) (Params...), Args&&... args)
{
return f (take_address_if_necessary <Params, Args> (args)...);
}
template <typename... Params, typename... Args>
void call_special (void (*f) (Params...), Args&&... args)
{
f (take_address_if_necessary <Params> (args)...);
}
void function (int* i, char* c)
{
std::cout << *i << ' ' << *c << std::endl;
}
int main ()
{
int i = 42;
char c = '%';
call_special (function, 1, 'f');
call_special (function, &i, '?');
call_special (function, &i, &c);
}
The above program yields
1 f
42 ?
42 %
as you'd expect.
There are some caveats here: first, this will fail if you try to use an overloaded function, because C++ can't deduce an overloaded function to a function pointer:
void bar (int);
void bar (float);
call_special (bar, 3.0f); // Compiler error
You might be able to fix this with explicit template arguments:
call_special <float> (bar, 3.0f); // Works
Or of course explicitly typing the function:
call_special ((void (*) (float))bar, 3.0f);
Second, for simplicity's sake, call_special
and its helpers play fast and loose with value classes. It may need more work to be able to handle rvalues, const values, etc. robustly.
Third, arguments that can be passed both as values and as pointers will not work. In fact, conversions in general are probably going to cause headaches:
void quux (long* i)
{
if (i)
std::cout << *i << std::endl;
else
std::cout << "(null)" << std::endl;
}
call_special (quux, NULL); // What does this print?
You may be better off using a function to grab the address explicitly:
template <typename T>
T* foo (T&& t)
{
return &t;
}
function (foo (3), foo ('7'));
Note that whatever method you use, you're going to be dealing with temporaries, which die at the semicolon. So if the library you're using stores a reference to an argument you give it, you have no choice but to explicitly give storage to the argument.