How to define and use boost::function with "optional arguments"? - c++

I am using a class that needs some kind of callback method, so i'm using boost::function to store the function pointers.
i need the callback to have one optional argument, but i found out that boost::function won't let me define optional arguments kind of type, so i tried the following code and it worked..
//the second argument is optional
typedef boost::function< int (int, char*)> myHandler;
class A
{
public:
//handler with 2 arguments
int foo(int x,char* a) {printf("%s\n",a); return 0;};
//handler with 1 argument
int boo(int x) {return 1;};
}
A* a = new A;
myHandler fooHandler= boost::bind(&A::foo,a,_1,_2);
myHandler booHandler= boost::bind(&A::boo,a,_1);
char* anyCharPtr = "just for demo";
//This works as expected calling a->foo(5,anyCharPtr)
fooHandler(5,anyCharPtr);
//Surprise, this also works as expected, calling a->boo(5) and ignores anyCharPtr
booHandler(5,anyCharPtr);
I was shocked that it worked, question is should it work, and is it legit?
is there a better solution?

Arguably a type safety hole in the bind -> function conversion.
boost::bind doesn't return an std::function but a function object of a very complicated type. In the case of
boost::bind(&A::boo,a,_1);
as seen above, the returned object has type
boost::_bi::bind_t<
int,
boost::_mfi::mf1<int,A,int>,
boost::_bi::list2<boost::_bi::value<A*>, boost::arg<1> >
>
std::function only checks that the supplied function object is "compatible", in this case, whether it is callable with an int as the first argument and a pointer to char as the second argument. Upon examining the *boost::bind_t* template, we see that it does indeed have a matching function call operator:
template<class A1, class A2> result_type operator()(A1 & a1, A2 & a2)
Inside this function the second argument ends up being silently discarded. This is by design. From the documentation: Any extra arguments are silently ignored (...)

Related

no matching function call when using lambda function as an argument of a template function

I defined a class that receives an lambda function through constructor. The code is as follows. Why did the definition of t0 pass compilation after using the std::forward, and t1 incur an error?
#include <iostream>
template <typename Func>
class Test {
public:
Test(Func &&func) : m_func(std::forward<Func &&>(func)) {}
void Run() { m_func(); }
private:
Func &&m_func;
};
template <typename Func>
class Foo {
public:
Foo(Func &func) : m_func(func) {}
void Run() { m_func(); }
private:
Func &m_func;
};
int main() {
const auto print = []() { std::cout << "Hello" << std::endl; };
using Print = decltype(print);
Test<decltype(print)> t0(std::forward<Print&&>(print));
t0.Run();
Test<void()> t1(Print{});
t1.Run();
Foo<decltype(print)> t3(std::forward<Print&&>(print));
t3.Run();
Foo<void()> t4(Print{});
t4.Run();
}
[Update]
The definition of t1 should be as following. thx for #JaMiT.
Test<void(*)()> t1([]() { std::cout << "Hello" << std::endl; });
But I'm still confused about the definition of t0. If I deletes the std::forward, it incurs a compilation error.
[Update]
It works if I change the definition of t0 to Test<void (*)()> t0(print);. What's the difference between Test<decltype(print)> t0(print); that causes a compilation error?
Why did the definition of t0 pass compilation after using the std::forward,
Because that is how you declared the constructor of Test. The constructor takes as its parameter an rvalue reference to the template parameter. You explicitly provided decltype(print) as the template argument, so the constructor takes an rvalue of that type. There will be no copying (no pass by value), and an lvalue reference will not cut it. You must provide an rvalue.
By adding std::forward<Print&&>, you converted print to an rvalue. (It would have been simpler to add std::move instead. Rule of thumb: use std::forward when dealing with a "placeholder" type, such as a template parameter, and use std::move when dealing with a fixed type.)
Caution: After using std::forward<Print&&>(print) or std::move(print), you should treat print as uninitialized. That is, your initialization of t3 is a potential bug.
Another tweak that would make this compile is to specify decltype(print)& (with the ampersand at the end) as the template argument. When Func is an lvalue reference, Func&& collapses to Func, which means the constructor would take an lvalue reference instead of an rvalue reference. (Reference collapsing is a key component of forwarding references, on which perhaps you based your code? However, forwarding references would require the constructor to itself be a template.)
and t1 incur an error?
For t1, you specified the template argument as void(), which is the type of a function. Lambdas are objects, not functions, so there is a type mismatch.
On the other hand, a lambda with no captures (nothing inside the []) can implicitly convert to a pointer to a function. This is a place where confusion lurks, because functions also decay to pointers so people can get used to interchanging function types and pointer to function types. To specify a pointer to a function, use void(*)() instead of void().
Caution: Implicit conversions can wreak havoc when combined with references. Then again, you were already in the danger zone when you combined temporary objects (Print{}) with references. Your code would be safer if you changed the data member to Func m_func;. In addition to avoiding dangling references, this would be more efficient (less indirection) when storing a pointer-to-function.
template <typename Func>
class Test {
public:
// Constructor can still take a reference and forward it to the member.
Test(Func &&func) : m_func(std::forward<Func &&>(func)) {}
void Run() { m_func(); }
private:
Func m_func; // Not necessarily a reference
};
There are still potential issues (e.g. Func could be specified as a reference type), but at least this is safer. I choose to treat the remaining issues as out-of-scope for this question about syntax.
It works if I change the definition of t0 to Test<void (*)()> t0(print);.
This combines some concepts I presented earlier. The template argument is now a pointer to a function, so your lambda (print) will undergo an implicit conversion, similar to the t1 case. The result of an implicit conversion is an rvalue, which is what your constructor expects (no need to forward or move).
Caution: By "works", you really mean "compiles". The fact that you asked this question suggests you already know the following, but for the benefit of others: getting code to compile is a necessary step, but that by itself does not mean the code is correct and works as intended. Don't be satisfied when a tweak you do not understand makes your code compile – ask questions!

Get pointer to overloaded function that would be called

Please refer to the following:
struct functorOverloaded
{
void operator()(const int& in_, ...) const {}
void operator()(short in_) {}
};
// helper to resolve pointer to overloaded function
template <typename C, typename... OverloadArgs>
auto resolve_overload(
std::invoke_result_t<C, OverloadArgs...> (C::* func)(OverloadArgs..., ...) const
)
{ return func; };
int main(int argc, char **argv)
{
using C = const functorOverloaded;
// works with exact function type
using myT = decltype(resolve_overload<C, const int&>(&C::operator()));
// can call with something convertible to const int&
static_assert(std::is_invocable_v<C,int>, "!!!");
// how to get the pointer to the overload that would be called when passed int (or double)?
// the next line doesn't compile (error C2672: 'resolve_overload': no matching overloaded function found)
using myT2 = decltype(resolve_overload<C, int>(&C::operator()));
return 0;
}
The above code allows retrieving a pointer to a specific overload of a function (operator() in this case), see here. One must know the exact argument type (const int&) in this case to get the pointer, even though i can just call the specific overload with a plain int, or even double. Is it possible to get a pointer to the overload that would be called with the specific argument (assuming the call is resolvable / not ambiguous)?
Edit: adding context:
I am writing a invocable_traits library for introspecting a callable. E.g., given a Callable, it will tell you the return type, arity and argument types, amongst some other properties. To support functors (including lambdas) with overloaded (or templated) operator(), the API of invocable_traits allows specifying call arguments to disambiguate which overload is to be used (or to instantiate the template). However, one must know the exact argument type (const int& in the example above), simply specifying int won't do in that case as there is no function with signature R operator()(int). Ideally, I'd like to allow discovering the signature of the exact overload/instantiation that gets called given the provided input argument types, ideally even taking into account any implicit conversions that are applied. Is this possible?
There is no way to get the function of an overload-set which would be called with the given arguments, unless you already know its signature.
And if you know, what's the point?
The problem is that for any given arguments, taking into account implicit conversions, references, cv-qualifiers, noexcept, old-style vararg, default arguments, and maybe also literal 0 being a null pointer constant, there are an infinite number of function-signatures which would match. And there is currently no facility for "just" listing all candidates.

Why function pointer of overloaded function need static_cast?

For the below code, I get the error
#include <iostream>
#include <functional>
using namespace std;
class A {};
class B {};
namespace N
{
void func(A a, int z){}
void func(B a, int z){}
}
void func_1(std::function<void(A, int)> x)
{
A a;
x(a, 1);
}
int main()
{
func_1(N::func);
return 0;
}
Error:
main.cpp:23:19: error: cannot resolve overloaded function 'func' based on conversion to type 'std::function<void(A, int)>'
23 | func_1(N::func);
If we do the static cast for the func_1(N::func); as func_1(static_cast<void (&)(A, int)>(N::func));, then this work fine. But I would expect this to work without a cast.
std::function<void(A, int)> is more complicated than void(*)(A, int).
template< class F >
function( F f );
Initializes the target with std::move(f). If f is a null pointer to function or null pointer to member, *this will be empty after the call. This constructor does not participate in overload resolution unless f is Callable for argument types Args... and return type R.
You don't even know what constructors participate in overload resolution until you decide which N::func you mean.
One can conceive of an combined overload resolution and template argument deduction scheme that could "meet in the middle" by trying std::function<void(A, int)>::function<void(*)(A, int)> among (arbitrarily many) other valid instantiations of the constructor.
Problems abound with that.
It has to provably arrive at an answer. In general there are infinite possible instantiations of the templates. You'd also want it to be able to pick int(*)(A, int) if you passed it int g(A, int).
It really should agree with the current scheme where that arrives at an unambiguous answer.
Each of the compiler vendors have to implement it correctly.
As a handy workaround you can provide this kind of func_1 overload.
...
void func_1(void(*x)(A, int))
{
func_1(std::function<void(A, int)>{x});
}
Now it works as desired without static_cast: demo

Calling a static function from a template parameter in a lambda function while ignoring default parameters

Ok, I'm using Visual Studio 2010 to mess with lambdas in templates. VC++ has an odd quirk dealing with template parameters, but I found a workaround for calling a static function of a template parameter (T::magic in this example) by using the auto keyword. However, I have hit another hitch.
Say I have one of many classes with a function "magic", and only the first 2 parameters matter for my call later. It may or may not have some defaulted parameters after the first 2. I don't really care about them for this application, I only need to call magic(start, otherthing). Uhoh is one of these classes:
struct Uhoh
{
static int magic(char* start, int otherthing, bool doom = false);
}
I do the call with this template. I have to use a workaround of using the auto type to get T::magic for it to work in the lambda function.
template<typename T> void example()
{
auto themagic = T::magic;
std::function<int (char*)> test = [=](char* start) -> int
{
return themagic(start, 0);
};
test(0);
}
And then I call it or whatever.
int main()
{
example<Uhoh>();
}
I get an error about "too few arguments for call", even though "magic" can take two arguments. Now, I can't know for sure what the type will be for "otherthing" in any of the various "magic" functions. All I know is that 0 will be a valid value. Passing the type of whatever otherthing will be to the function "example" will be an extreme annoyance at best.
How can I properly type "themagic" so that VC++ doesn't throw an error?
auto themagic = T::magic;
Here the information that the third parameter of T::magic has default value is lost, as the type of themagic is deduced as int (*)(char*, int, bool), which cannot have any default value for the third parameter. So you cannot call themagic with only two arguments. You've to pass third argument as well.
So do this:
std::function<int (char*)> test = [=](char* start) -> int
{
return themagic(start, 0, false);
};
Please note that default-value for a function parameter is not a part of the function signature, which means when you write
auto themagic = T::magic;
then type of themagic cannot be deduced as int (*)(char*, int, bool=false).

Deducing knowledge of original types, whilst simultaneously forwarding

Summary: I want to end up with a function that deduces the exact types it was called with and takes (e.g.) a tuple that forwards them (the types of which will be different from the exact types the function was called with).
I'm stuck trying to "know" via deduction the types of the arguments to a given function, whilst simultaneously forwarding them. I think I might be missing something crucial about how this works.
#include <tuple>
#include <string>
#include <functional>
template <typename ...Args>
struct unresolved_linker_to_print_the_type {
unresolved_linker_to_print_the_type();
};
void f(int,double,void*,std::string&,const char*) {
}
template <typename F, typename ...Args>
void g1(F func, Args&&... args) {
unresolved_linker_to_print_the_type<Args...>();
auto tuple = std::forward_as_tuple(args...);
unresolved_linker_to_print_the_type<decltype(tuple)>();
}
template <typename F, typename T, typename ...Args>
void g2(F func, const T& tuple, Args... args) {
unresolved_linker_to_print_the_type<Args...>();
unresolved_linker_to_print_the_type<decltype(tuple)>();
}
int main() {
int i;
double d;
void *ptr;
std::string str;
std::string& sref = str;
const char *cstr = "HI";
g1(f, i,d,ptr,sref,cstr);
g2(f, std::forward_as_tuple(i,d,ptr,sref,cstr), i,d,ptr,sref,cstr);
}
What I'd like to see is a scenario where when my function (e.g. g1 or g2) gets called it knows and can use both the original types - int,double,void*,std::string&,const char* and the forwarded arugments too.
In this instance I don't seem to be able to find this information from within g1 or g2. The (deliberate, to print out the types) linker error shows me in g1 they are:
int&, double&, void*&, std::string&, char const*&
int&, double&, void*&, std::string&, char const*&
and in g2:
int, double, void*, std::string, char const*
int&, double&, void*&, std::string&, char const*&
There are two thing I don't get here:
Why do none of the printed (via the linker error) types match what I actually passed in? (int,double,void*,std::string&,const char). Can I deduce what I actually was passed? Preferably with "natural" syntax, i.e. everything just once and nothing explicitly written out. I can explicitly write:
g2<decltype(&f),decltype(std::forward_as_tuple(i,d,ptr,sref,cstr)),int,double,void*,std::string&,const char*>(f,std::forward_as_tuple(i,d,ptr,sref,cstr),i,d,ptr,sref,cstr);
but that's "unwieldy" to say the least!
In g1 the presence of && in the function signature declaration seems to alter the types in the template parameter Args itself. Compare that with:
template <typename T>
void test(T t);
Or:
template <typename T>
void test(T& t);
using either of those with:
int i;
test(i);
doesn't change the type of T. Why does the && change the type of T itself when & doesn't?
Answer to first question:
Arguments to functions are expressions, not types. The difference between these two is expressed in chapter 5 [expr], p5:
If an expression initially has the type “reference to T” (8.3.2,
8.5.3), the type is adjusted to T prior to any further analysis.
Thus, there is no difference what-so-ever between g(str) and g(sref). g() always sees a std::string, and never a reference.
Additionally expressions can be lvalue or rvalue (actually that's a simplification of the C++11 rules, but it is close enough for this discussion - if you want the details they're in 3.10 [basic.lval]).
Answer to second question:
Template parameters of the form:
template <class T>
void g(T&&);
are special. They are unlike T, T&, or even const T&& in the following way:
When T&& binds to an lvalue, T is deduced as an lvalue reference type, otherwise T deduces exactly as per the normal deduction rules.
Examples:
int i = 0;
g(i); // calls g<int&>(i)
g(0); // calls g<int>(0)
This behavior is to support so called perfect forwarding which typically looks like:
struct A{};
void bar(const A&);
void bar(A&&);
template <class T>
void foo(T&& t)
{
bar(static_cast<T&&>(t)); // real code would use std::forward<T> here
}
If one calls foo(A()) (an rvalue A), T deduces per normal rules as A. Inside of foo we cast t to an A&& (an rvalue) and call bar. The overload of bar that takes an rvalue A is then chosen. I.e. if we call foo with an rvalue, then foo calls bar with an rvalue.
But if we call foo(a) (an lvalue A), then T deduces as A&. Now the cast looks like:
static_cast<A& &&>(t);
which under the reference collapsing rules simplifies to:
static_cast<A&>(t);
I.e. the lvalue t is cast to an lvalue (a no-op cast), and thus the bar overload taking an lvalue is called. I.e. if we call foo with an lvalue, then foo calls bar with an lvalue. And that's where the term perfect forwarding comes from.
types (even in C++) are mostly a compile type notion (except of course the RTTI in vtables).
If you need entirely dynamic types, then C++ might not be the best language for that.
You might perhaps extend GCC (actually g++, assuming it is at least 4.6) with a plugin or a GCC MELT extension (MELT is a high level domain specific language to extend GCC) which does what you want (e.g. for instance providing an additional builtin which encode the type of its arguments in some constant string, etc...), but that does require some work (and is specific to GCC).
But I don't understand why you want to do such baroque things in C. If dynamic typing is so important to you, why don't you use a dynamically typed language??