How declare a C++ mem_fn(member_function) in function declaration? - c++

I understand the basic problem in passing the address of a member function outside of its class. I get the feeling that mem_fn() might be the solution but I am having trouble with the specifics.
I have a member function in class p that is currently declared as
typedef void (*valNamedFlagsCallback)(const int, const bool);
bool valNamedFlags(const OptBlk *operand, const char *description_of_value_or_NULL, const int subscripts[], const char *names[], valNamedFlagsCallback callBack);
In class e I am trying to call valNamedFlags with
pInstance->valNamedFlags(operand, "Statement types", statementsSubscripts, statementsNames, std::mem_fn(&e::setStatement));
(I started out without the mem_fn() but of course that has the classic "pointers to member functions" problem. I've tried both &e::setStatement and just plain &setStatement.)
FWIW, setStatement is prototyped as
void setStatement(const int ifcid, const bool isAffirmative);
Everything works if I eliminate the mem_fn() and declare setStatement as static. I'm just pointing that out as a way of saying that I have eliminated all of the other possible issues; my only issue is the "pointers to member functions" problem. Unfortunately, setStatement() needs to be a member function, not a static.
The specific error I am getting in MS VS 2010 is
bool p::valNamedFlags(const OptBlk *,const char *,const int [],const char *[],p::valNamedFlagsCallback)' : cannot convert parameter 5 from 'std::tr1::_Mem_fn3<_Rx,_Pmf,_Arg0,_Arg1,_Arg2>' to 'p::valNamedFlagsCallback'
I would like to keep the callback declaration independent of class e; that is, I do not want to go to
typedef void (*e::valNamedFlagsCallback)(const int, const bool);
because I want to keep p more generalized than that.
Is mem_fn() the right solution or am I way off base? If so, how should I be declaring the callback in the valNamedFlags() prototype?
Or should I be pursuing a different approach?

Actually, you'll have a problem doing bind-like stuff as some suggested, since your callback is defined as a simple function pointer, and not a callable.
If you can afford to do it, you can change your
typedef void (*valNamedFlagsCallback)(const int, const bool);
to something like
typedef std::function<void(int, bool)> valNamedFlagsCallback
(also noting that const on value parameters doesn't affect the signature of the function), then you can use std::bind(),
using namespace std::placeholders;
pInstance->valNamedFlags(operand,
"Statement types",
statementsSubscripts,
statementsNames,
std::bind(&E::setStatement, e, _1, _2));
or you can use lambdas:
pInstance->valNamedFlags(operand,
"Statement types",
statementsSubscripts,
statementsNames,
[&](int i, bool b) { e->setStatement(i, b); });
If you must keep it as a simple function, then you'll have to send one which references the right object as a global/static variable.

You need to bind an instance on it to call via a member function pointer. (i.e. std::bind(&e::setStatement, eInstance, _1, _2), suppose eInstance is a pointer to an object of class e).
using namespace std::placeholders; // for _1, _2, _3...
pInstance->valNamedFlags(operand, "Statement types", statementsSubscripts, statementsNames, std::bind(&e::setStatement, eInstance, _1, _2));
Note that the return value of std::bind (which is unspecified) doesn't match the free function pointer type valNamedFlagsCallback, one of the solutions is to use std::function.
typedef std::function<void(const int, const bool)> valNamedFlagsCallback;
Simplified demo

Got it!!! THANKS ALL!!!
Prototype:
bool valNamedFlags(const OptBlk *operand, const char *description_of_value_or_NULL, const int subscripts[], const char *names[], std::function<void(int,bool)> callBack);
Call:
valNamedFlags(..., std::bind(&e::setStatement, this, _1, _2));
Actually runs and calls setStatement with the proper arguments.

Related

Passing in function as an argument in C++ using std::function

I have been trying to pass an interpolation function by reference as an argument to another function but I keep getting the following error:
error: could not convert '&AmrCoreAdv::interp_reta_from_R' from 'double (AmrCoreAdv::*)(double)' to 'std::function<double(double)'
There is a header file called AmrCoreAdv.H where the interpolation function is declared as double interp_reta_from_R(double R) and it is defined in another file. The interpolation function is passed as an argument to the state_rhs function and is called as follows:
state_rhs(i, j, k, rhs_fab, state_fab, eta, dx[0], dx2, deta, eta2, two_over_eta, three_over_eta, e2_over_8pi2, &interp_reta_from_R);
The function state_rhs takes in the following arguments:
state_rhs(int i, int j, int k,
amrex::Array4<amrex::Real> const& rhs_fab,
amrex::Array4<amrex::Real const> const& state_fab,
const amrex::Real eta,
const amrex::Real _dx,
const amrex::Real dx2,
const amrex::Real d_eta,
const amrex::Real eta2,
const amrex::Real two_over_eta,
const amrex::Real three_over_eta,
const amrex::Real e2_over_8pi2,
std::function<double (double)> interp_reta_from_R)
I am relatively new to C++ and am lost as to how to proceed. I have tried using typedef and some other ways of defining the type of the interp_reta_from_R function as an argument in the state_rhs function but to no avail. I would really appreciate some guidance on this. Please let me know if you would require any additional information for debugging this issue. Thank you!
A member function is not like a free function - it has a different type which includes the class, and it can't be called without an instance of the class.
The simplest thing is to pass a lambda function instead, capturing the object that should get its member called.
Assuming that the function is a member of *this, replace the pointer-to-member with
[this](double d) { return interp_reta_from_R(d); }
Pointer to member is not a regular pointer, it needs to come with its object.
Consider the following code:
struct S
{
double f(double);
};
//whatever
S s;
s.f(3.14);
The mental model for the call to f can be sth like the following one (this is just a mental model, it has nothing to do with the standard, calling convention etc.):
f(&s, 3.14); //s address
For that reason, in order to transform a member function into a free function, it's object argument needs to be bound to it.
Modern C++ (C++11 onwards) has two means of doing this: std::bind or lambda expressions. For pre-C++11 one can look-up boost::bind which plays essentially the same role as its std counterpart.
Note that lambdas (since C++11) are somewhat limited when it comes to move-only types, which means one might be stuck with bind; should technical limitations not be the case use whatever suits your needs, taste and code conventions.
Also, note that object might be provided either by value (giving the created function object copy semantics) or reference/address. In the latter case, make sure the functor does not outlive the objects it is supposed to be referring to.
Example:
#include <functional>
struct S
{
double f(double) {return{};}
};
void ff(std::function<double(double)>){}
int main(int, char*[])
{
S s;
ff(std::bind(&S::f, s, std::placeholders::_1)); //bind by value
ff(std::bind(&S::f, &s, std::placeholders::_1)); //bind by address
ff([&s](double x){return s.f(x);}); //lambda with capture by reference
ff([s](double x)mutable{return s.f(x);}); //lambda capturing by value. Note mutable; lambdas by default capture by value as const objects!
}
DEMO: https://godbolt.org/z/M9sGj36jq

std::bind "no matching function for call"

I am trying to set a delegate for a function and have the 2 following classes to achieve that.
On the bottom is the error I'm getting. How do I handle it?
Class A
typedef void (*SocketEventString) (String);
class SocketIO
{
public:
SocketIO();
void onMessage(SocketEventString _cb);
private:
SocketEventString _onMessage;
};
Class B
class BoardManager
{
public:
BoardManager();
void handleAction(String action);
SocketIO io;
};
//Constructor
BoardManager::BoardManager() {
io.onMessage( std::bind( &BoardManager::handleAction, this, std::placeholders::_1 ) );
}
ERROR
sketch/BoardManager.cpp: In member function 'void BoardManager::initializeSocketIO()':
BoardManager.cpp:68: error: no matching function for call to 'SocketIO::onMessage(std::_Bind_helper<false, void (BoardManager::*)(String), BoardManager* const, const std::_Placeholder<1>&>::type)'
io.onMessage( std::bind( &BoardManager::handleAction, this, std::placeholders::_1 ) );
^
sketch/BoardManager.cpp:68:90: note: candidate is:
In file included from sketch/BoardManager.h:10:0,
from sketch/BoardManager.cpp:8:
sketch/SocketIO.h:25:18: note: void SocketIO::onMessage(SocketEventString)
void onMessage(SocketEventString _cb);
The std::bind function return an object that is not compatible or convertible to a pointer to a non-member function.
Instead use std::function:
using SocketEventString = std::function<void(String)>;
With the definition
typedef void (*SocketEventString) (String);
you say that SocketEventString is a pointer to a non-member function (i.e. a function not a member in a class or struct) that takes one argument of type String and returns no value.
The std::bind function returns an object of an unknown class. That object is not the same a the pointer-type you define SocketEventString to be.
The two types (SocketEventString and the object returned by std::bind) are not compatible. You can not convert from one of the types to the other.
The compiler tell you this, because it tries to find a function SocketIO::onMessage which takes the type of the object returned by std::bind and don't find any such overload.
Instead of the SocketEventString type you have defined, you need to use type that is compatible with the object returned by std::bind. That's what I have shown above in my answer, defined SocketEventString to be a different type, a type that is compatible with the type returned by std::bind.
Firstly, you can't use a C function pointer for a C++ function binding like that. Essentially, when you use bind it captures some variables to be used in the function call (such as this), so you need to use std::function which handles capturing variables if you want to bind a member function (because member functions at the very least need the this pointer captured). Also, in my opinion, std::bind is fairly ugly, and I recommend getting familiar the new C++ lambdas.
BoardManager::BoardManager() {
io.onMessage( [&]( String action ) {
handleAction( action );
});
}

Pointer to deque<int>::push_back

#include <iostream>
#include <deque>
using namespace std;
main()
{
typedef void (deque<int>::*func_ptr)(int);
func_ptr fptr = &deque<int>::push_back;
}
Im trying to get pointer to this function but I get a compilation error
error: cannot convert ‘void (std::deque<int>::*)(const value_type&) {aka void (std::deque<int>::*)(const int&)}’ to ‘func_ptr {aka void (std::deque<int>::*)(int)}’ in initialization
func_ptr fptr = &deque<int>::push_back;
I want to do this so that I can get pointer to different member functions on the basis of different conditions.
I referred this link.
As said in the accepted answer, the problem is that the type signature is different -- for a std::deque<T>, push_back only has an overload that accepts T const&, and not T directly. typedef void (deque<int>::*func_ptr)(const int &) is a perfectly concise and cromulent way to write this.
I wanted to address a C++11 way to do this -- type aliases. One may first wonder "why not use auto?" This is not possible because the function being accessed is an overloaded function, and auto does not know which of the overloads to access. Because the original question embeds the knowledge of the function type being void(int), the statement &deque<int>::push_back selects the correct overload. The problem with the simpler statement is that it bakes in knowledge of the container and the type being contained. If you want to change to a std::vector, or from int to short, you'll have to make all new types. With type aliases, we can templatize everything to avoid embedding type knowledge:
using func_ptr = void(Cont::*)(typename Cont::const_reference);
We can do something simple, as before:
func_ptr<deque<int>> fptr = &deque<int>::push_back;
...or we can refer to a container somewhere:
vector<short> container;
...looking up its type at compile time, and storing a pointer to its push_back function, all without caring what the container is:
using container_type = decltype(container);
func_ptr<container_type> fptr2 = &container_type::push_back;
push_back() takes a const T & parameter, as the error message states:
cannot convert ‘void (std::deque::*)(const value_type&) ...
Change your type alias to:
typedef void (deque<int>::*func_ptr)(const int &);
There is a bigger problem with what you are trying to do. Implementations are allowed to add default parameters to any member functions, so the actual signature for push_back() might be different. It can even add additional functions with the same name (but a different signature).
I advise against taking pointers to container members function. You are usually better of without it.

How to pass a function pointer to overloaded member function as parameter

This is related to code generation.
I have a class A which is generated from model, in which I have two overloads of function f like below:
class A
{
public:
void f(int a){}
void f(int a, int b){}
}
I then have a separate part of the system which is not generated from model, but written in hand crafted C++. Here I would like to access the function f on an object. From the model I can pass data to the hand crafted part of the system, but not the other way around, since the generated file is not available for compilation when I build my hand crafted code.
My idea is to pass a function pointer from the model to where I need it. This, as I have understood it so far, includes static_cast to solve the overload issue and then I can pass a pointer to the function as a parameter to some other hand crafted function.
The function used to pass the pointer to the hand crafted part of the system is declared like this (here A is not known):
void passPointer(int, void (*f)(int, int));
My cast and function call looks like:
handCraftedObject->passPointer(17, static_cast<void (A::*)(int, int)> (&A::f) );
My compilation error is:
: no known conversion for argument 2 from 'void (A::*)(int, int)' to 'void (*)(int, int)
I hope this doesn't mean i have to know the class A where the function passing the function pointer is declared. This is not possible in my system.
Member function pointer has different type than function pointer and cannot be converted to it. The simplest way is to use boost/std(C++11) bind and function.
void passPointer(int, const std::function<void(int, int)>&);
than just
handCraftedObject->passPointer
(
17, std::bind(static_cast<void (A::*)(int, int)> (&A::f), std::ref(a_instance),
std::placeholders::_1, std::placeholders::_2)
);
Also you cannot use boost/C++11, you can make function f static, then all will works fine.

Why does C++ allow but ignore the application of const to function types?

I get a real kick out of exploring the unusual corners of C++. Having learned about the real types of functions rather than function pointers from this question, I tried messing around with function typing and came up with this bizarre case:
typedef int Func(int);
int Foo(int x) { return 1; }
int main()
{
const Func*& f = &Foo;
return 0;
}
Since &Foo is an rvalue of type Func*, I figured that I should be able to put it in a const reference, but I get this error from g++ 4.6:
funcTypes.cpp: In function ‘int main()’:
funcTypes.cpp:7:23: error: invalid initialization of non-const reference of type ‘int (*&)(int)’ from an rvalue of type ‘int (*)(int)’
But f is const! It has become apparent to me that the application of const to a function (or reference/reference to pointer etc.) is simply ignored; this code compiles just fine:
template <typename A, typename B>
struct SameType;
template <typename A>
struct SameType<A, A> { };
typedef int Func(int);
int main()
{
SameType<const Func, Func>();
return 0;
}
I'm guessing this is how boost pulls off their is_function type trait, but my question is - why does C++ allow this by ignoring it instead of forbidding it?
EDIT: I realise now that in the first example f is non-const and that const FuncPtr& f = &Foo does work. However, that was just background, the real question is the one above.
But f is const!
No, it's not. You're confusing
const Func*& f = &Foo;
with
Func* const& f = &Foo;
The former is a non-const ref to a const pointer. The latter is a const ref to a non-const pointer.
That's why I always write the const-ness before the */& rather than before the type. I would always write the first case as
Func const*& f = &Foo;
and then read right to left: reference to a pointer to a const Func.
In c++03 it was not ignored, but illformed (and was an sfinae case). I guess they changed that in c++11 because then you can simply have function parameters be const F& and can pass to it rvalue function objects aswell as normal functions.
See this DR which made the change http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#295
&Foo is a pointer. In general, I would suggest avoiding references to pointers (const or no). At least, not unless you know what you're doing.
So you should have:
const Func *f = &Foo;
Or really, you can ditch the const entirely:
Func *f = &Foo;
why does C++ allow this by ignoring it instead of forbidding it?
Because you're talking about two different things.
In C++, there is a difference between a function type and a function pointer. Foo is a function type, specifically int(int). &Foo is a function pointer, of type int(*)(int). A function type degrades into a function pointer, where necessary (much like array types degrade into pointers). But they are distinct (just like arrays).
So your two cases are not the same. Your first case is dealing with a function pointer, and your second case is dealing with a function type (which is what the template argument is deduced as).
As for why function types swallow the const, that's because the values of function types are already implicitly constant. You can't change them. The only operation you can perform on a function type is () (or conversion to function pointer). So a const T is equivalent to T if T is a function type. Visual Studio 2010 actually gives a warning about that.
The following compiles fine:
typedef int Func(int);
int Foo(int x) { return 1; }
int main()
{
Func* const& f = &Foo; //ok
return 0;
}
Be aware that const statements are evaluated along with pointers and references from right to left. The last const to the very left you wrote translates to last possible position right of a Name (C++ FAQ on const placement). Hence
const Func*& f
Is translated by the compiler to
Func const*& f
Hence you get a reference to a constant pointer which is not what you want. Besides I would not use references to function pointer if you do not really have to.
No, f is not const. First of all, it is a reference to some mutable type (that mutable type happens to be a const pointer, i.e. a mutable pointer to something that you promise not to change through that particular pointer). However, with &Foo you are creating a temporary (of type Func*) and you cannot assign a temporary to a mutable reference. You can only assign it to a const reference. And that is precisely what the error message is telling you.
Sometimes the error messages can be a bit cryptic.
I put together an example on ideone to illustrate the types printed by gcc for a variety of things:
#include <iostream>
typedef int(Func)(int);
typedef Func* FuncPtr;
typedef FuncPtr& FuncPtrRef;
typedef FuncPtr const& FuncPtrConstRef;
template <typename T> void DisplayType() { T::foo(); }
int main() {
DisplayType<Func>();
DisplayType<FuncPtr>();
DisplayType<FuncPtrRef>();
DisplayType<FuncPtrConstRef>();
}
The compilation errors give:
Func is of type int ()(int) (not a valid type, should now be fixed in gcc)
FuncPtr is of type int (*)(int)
FuncPtrRef is of type int (*&)(int)
FuncPtrConstRef is of type int (* const&)(int)
In your error message you have int (*&)(int), which is a reference to a non-const pointer to a function type.
You are not allowed to bind an rvalue to a non-const reference.
Note: this has thus nothing to do with const being swallowed, as smparkes correctly diagnosed