Allowing function pointer code to work on a member function

  • Thread starter Enquiries, Hopkins Research
  • Start date
E

Enquiries, Hopkins Research

Hi all

I have a conundrum that is puzzling me.

I have a large codebase in C that I am converting to C++ as fast as possible
(i.e. slowly because I keep learning new idioms and stumbling with C++
'features'). One part of the C code is some optimisation functions that
expect a pointer to a function which is sent an array of doubles and returns
a double i.e. simplified..

void optimise( double (*funk)( double* ) );

I now have a bunch of classes with member functions which also take double*
and return double that I want to use in this optimiser, but the compiler
won't let me because:

error: argument of type 'double (namespace::class::)(double*)' does not
match 'double (*)(double*)'

OK - I hadn't thought about that but felt there must be an easy answer.
However, I have tried declaring the functions in question as friends (but
the class members that the functions use are no longer available) and
sending 'pointers' to them a la BS TCPPPL p.419:

typedef double (class::*Pstd_mem)( double* pv );
Pstd_mem p = &class::eek:pt_ll;

...but nothing I have tried so far works and I am running out of ideas. Can
any C++ gurus out there suggest a solution (that is hopefully reliable and
efficient)?

TIA and please CC replies here

Michael


_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/

_/ _/ _/_/_/ Hopkins Research Ltd
_/ _/ _/ _/
_/_/_/_/ _/_/_/ http://www.hopkins-research.com/
_/ _/ _/ _/
_/ _/ _/ _/ 'touch the future'

_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/



[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
 
V

Victor Bazarov

I have a large codebase in C that I am converting to C++ as fast as
possible (i.e. slowly because I keep learning new idioms and
stumbling with C++ 'features'). One part of the C code is some
optimisation functions that expect a pointer to a function which is
sent an array of doubles and returns a double i.e. simplified..

void optimise( double (*funk)( double* ) );

I now have a bunch of classes with member functions which also take
double* and return double that I want to use in this optimiser, but
the compiler won't let me because:

error: argument of type 'double (namespace::class::)(double*)' does
not match 'double (*)(double*)'

They are incompatible.

See FAQ about pointers to members. You can find the FAQ here:
http://www.parashift.com/c++-faq-lite/

If you'll have questions after reading the FAQ, do ask them.

V
 
J

Jonathan Mcdougall

Hi all

I have a conundrum that is puzzling me.

I have a large codebase in C that I am converting to C++ as fast as possible
(i.e. slowly because I keep learning new idioms and stumbling with C++
'features'). One part of the C code is some optimisation functions that
expect a pointer to a function which is sent an array of doubles and returns
a double i.e. simplified..

void optimise( double (*funk)( double* ) );

I now have a bunch of classes with member functions which also take double*
and return double that I want to use in this optimiser, but the compiler
won't let me because:

error: argument of type 'double (namespace::class::)(double*)' does not
match 'double (*)(double*)'

OK - I hadn't thought about that but felt there must be an easy answer.
However, I have tried declaring the functions in question as friends (but
the class members that the functions use are no longer available) and
sending 'pointers' to them a la BS TCPPPL p.419:

typedef double (class::*Pstd_mem)( double* pv );
Pstd_mem p = &class::eek:pt_ll;

..but nothing I have tried so far works and I am running out of ideas. Can
any C++ gurus out there suggest a solution (that is hopefully reliable and
efficient)?

Free functions and member functions are different. See
http://www.parashift.com/c++-faq-lite/pointers-to-members.html,
especially 33.2. While you're there, read the whole faq, it may help
you.


Jonathan


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
 
I

Ismail Pazarbasi

If I got it correct, you did this:

class SomeClass
{
public:

double OptimizeCallback(double*) { /* ... */ }
};

typedef double (SomeClass::*Pstd_mem)( double* pv );

// somewhere in the code
Pstd_mem p = &SomeClass::OptimizeCallback;
optimise(p);

As previous 3 posters replied, there is a distinction between a
non-member function and a member function. The distinction is that
latter one needs an additional "this" pointer (something like, "mov
ecx, this", in x86 assembly). In C++, there is no (standard and
reliable) way to know, at compile time, where the function is going to
be located.

Valentin's answers are cool. I'm not keen with libraries, and I didn't
know whether boost has such a thing. But in that case, you need to use
boost, and probably you'll need to ship boost libary (.so or .dll)
along with your binary.

There ways, arguably efficient and reliable, but they are compiler and
platform dependent. If you don't have a portability concern, I think we
can solve this problem. But again, these ways are not supported by
standard, so generated executable may not run properly, next time you
compile it with newer version of your current compiler.

What I thought, for example, is first defining an "interface", which
could be thought as a "struct of function pointers" in C, derive
classes from this interface, write a template adapter class that
returns pointer to the member function (forcibly and tricky, through
vtable offset), pass this pointer to optimise() as if it is the double
(funk*)(double*). Since standard doesn't define vtable layout, the code
won't be portable, and it may need some more tailoring, if your classes
have different inheritance model (multiple, virtual, etc). As Valentin
pointed out, this may become difficult to implement for more complex
scenarios. Again, I want to point out that I am not really sure how
safe it is! Test good and ask your compiler vendor, if you intend to
use this:

For Visual C++: (I guess it works as of VC++ 6.0)

typedef double(*PFUNK)(double*);

void optimise(PFUNK pf)
{
double d = 3.0;
pf(&d);
}


// Interface definition
struct IOptimizable
{
virtual double OptimizeCallback(double* p) = 0;
};

// your class will derive from IOptimizable interface
class SomeClass : public IOptimizable
{
public:
virtual double OptimizeCallback(double* pdbl)
{
cout << *pdbl << endl;
return *pdbl;
}
};

// adapter class, returns a pointer to the member function.
template<class T>
class OptimizeAdapter
{
const T* m_pt;
public:
explicit OptimizeAdapter(const T* pt)
:m_pt(pt)
{
}
operator PFUNK()
{
IOptimizable const* popt = static_cast<IOptimizable
const*>(m_pt);
void* pvtbl = ((void**)popt)[0]; // vtable is at offset 0
return (PFUNK)((void**)pvtbl)[0]; // one-and-the-only function
is at offset 0
}
};

int main()
{
SomeClass r;
OptimizeAdapter<SomeClass> adapt(&r);
optimise(adapt);
return 0;
}

One more point; the object of type SomeClass should live until
optimise() returns. Otherwise, all bets are off (undefined), as far as
I know.

I used an adapter class, because you may need further additions and
fine tuning before returning function pointer.

Please let me know, if you intend to use and whether it works :)

Ismail


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
 
K

Kai-Uwe Bux

Hi all

I have a conundrum that is puzzling me.

I have a large codebase in C that I am converting to C++ as fast as
possible (i.e. slowly because I keep learning new idioms and stumbling
with C++
'features'). One part of the C code is some optimisation functions that
expect a pointer to a function which is sent an array of doubles and
returns a double i.e. simplified..

void optimise( double (*funk)( double* ) );

I now have a bunch of classes with member functions which also take
double* and return double that I want to use in this optimiser, but the
compiler won't let me because:

error: argument of type 'double (namespace::class::)(double*)' does not
match 'double (*)(double*)'

OK - I hadn't thought about that but felt there must be an easy answer.
However, I have tried declaring the functions in question as friends (but
the class members that the functions use are no longer available) and
sending 'pointers' to them a la BS TCPPPL p.419:

typedef double (class::*Pstd_mem)( double* pv );
Pstd_mem p = &class::eek:pt_ll;

..but nothing I have tried so far works and I am running out of ideas.
Can any C++ gurus out there suggest a solution (that is hopefully reliable
and efficient)?
[snip]

Others have already pointed out the reason for your problem. Here is a
workaround:


#include <iostream>
#include <memory>
#include <stdexcept>

template < typename T >
struct statify {

typedef T value_type;
typedef int ( T::* pmf_type ) ( int );

private:

// helper struct:
struct func_object {

value_type object;
pmf_type func;

func_object ( value_type const & o, pmf_type f )
: object ( o )
, func ( f )
{}

};

// static data:
static std::auto_ptr< func_object > fo_ptr;

// this is a monostate class:
statify ( void );
statify ( statify const & );
statify& operator= ( statify const & );
~statify ( void ) {}

public:

static
void initialize ( value_type const & t, pmf_type f )
{
fo_ptr = std::auto_ptr< func_object >( new func_object( t, f ) );
}

static
int forward ( int i ) {
if ( fo_ptr.get() == 0 ) {
throw( std::logic_error( "calling empty function object.\n" ) );
} else {
return( ((fo_ptr->object).*(fo_ptr->func))( i ) );
}
}

}; // statify

template < typename T >
std::auto_ptr< typename statify<T>::func_object > statify<T>::fo_ptr;



typedef int ( * int_to_int ) ( int );


// this is the API that uses a function pointer:
void print_exec ( int_to_int f, int i ) {
std::cout << i << " --> " << f(i) << '\n';
}


// here is our class with suitable member function:
struct xxx {

int factor;

xxx ( int f )
: factor ( f )
{}

int mult ( int i ) {
return( factor*i );
}

};

int main ( void ) {
xxx M ( 5 );
statify< xxx>::initialize ( M, &xxx::mult );
print_exec( statify<xxx>::forward, 4 );
}




Best

Kai-Uwe Bux

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
 
C

Carl Barron

Enquiries said:
Hi all

I have a conundrum that is puzzling me.

I have a large codebase in C that I am converting to C++ as fast as possible
(i.e. slowly because I keep learning new idioms and stumbling with C++
'features'). One part of the C code is some optimisation functions that
expect a pointer to a function which is sent an array of doubles and returns
a double i.e. simplified..

void optimise( double (*funk)( double* ) );

I now have a bunch of classes with member functions which also take double*
and return double that I want to use in this optimiser, but the compiler
won't let me because:

error: argument of type 'double (namespace::class::)(double*)' does not
match 'double (*)(double*)'

OK - I hadn't thought about that but felt there must be an easy answer.
However, I have tried declaring the functions in question as friends (but
the class members that the functions use are no longer available) and
sending 'pointers' to them a la BS TCPPPL p.419:

typedef double (class::*Pstd_mem)( double* pv );
Pstd_mem p = &class::eek:pt_ll;

..but nothing I have tried so far works and I am running out of ideas. Can
any C++ gurus out there suggest a solution (that is hopefully reliable and
efficient)?

TIA and please CC replies here

Michael
boost [www.boost.org] has a function wrapper that wraps function
pointers, functors, and member function calls.

boost::function<double(double)> is a class that should be a drop in
replacement for double(*)(double). It is slower than raw function
pointers but solves your problem and others.

you have optimize(double (*f)(double))

change the prototype and functioin definition to

optimize(boost::function<double(double)> f). Then it should work.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
 
J

Jonathan Mcdougall

Ismail said:
If I got it correct, you did this:

class SomeClass
{
public:

double OptimizeCallback(double*) { /* ... */ }
};

typedef double (SomeClass::*Pstd_mem)( double* pv );

// somewhere in the code
Pstd_mem p = &SomeClass::OptimizeCallback;
optimise(p);

As previous 3 posters replied, there is a distinction between a
non-member function and a member function. The distinction is that
latter one needs an additional "this" pointer (something like, "mov
ecx, this", in x86 assembly).

It would be more accurate to say that you need an object to call a
member function, so you still need an object to call a member function
through a pointer.
In C++, there is no (standard and
reliable) way to know, at compile time, where the function is going to
be located.

This is irrelevant and half wrong. The linker knows where in the
executable the function is located at compile time (and that's all you
need), but of course the exact location in memory is not known until
run-time.
Valentin's answers are cool. I'm not keen with libraries, and I didn't
know whether boost has such a thing. But in that case, you need to use
boost, and probably you'll need to ship boost libary (.so or .dll)
along with your binary.

Depends on what part of boost you are using. Particularily,
boost::function, boost::iterator and boost::bind only need headers.
There ways, arguably efficient and reliable, but they are compiler and
platform dependent. If you don't have a portability concern, I think we
can solve this problem. But again, these ways are not supported by
standard, so generated executable may not run properly, next time you
compile it with newer version of your current compiler.

What I thought, for example, is first defining an "interface", which
could be thought as a "struct of function pointers" in C, derive
classes from this interface, write a template adapter class that
returns pointer to the member function (forcibly and tricky, through
vtable offset), pass this pointer to optimise() as if it is the double
(funk*)(double*). Since standard doesn't define vtable layout, the code
won't be portable, and it may need some more tailoring, if your classes
have different inheritance model (multiple, virtual, etc). As Valentin
pointed out, this may become difficult to implement for more complex
scenarios. Again, I want to point out that I am not really sure how
safe it is! Test good and ask your compiler vendor, if you intend to
use this:

<snip weird non-standard code>

This is illegal in C++ and is not even a behavior you can generally
count on. Please post standard and valid code only. Thounsands of
people read this newsgroup (hopefully) expecting the advices given to
be reliable.


Jonathan


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
 
J

James Dennett

In the presence of virtual functions, the specific function
that would be called can vary at runtime. (It's possible
for a compiler to generate additional functions that wrap
this dispatch, in which case the linker would know, but a
different level of indirection is added.)

-- James

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
 
J

Jonathan Mcdougall

James said:
In the presence of virtual functions, the specific function
that would be called can vary at runtime. (It's possible
for a compiler to generate additional functions that wrap
this dispatch, in which case the linker would know, but a
different level of indirection is added.)

Yes, but the location of virtual functions in the object file is still
known at compile-time. What makes them "virtual" is that a pointer
contains the address of a function and that the contained value may
change at run-time. This is only a second level of indirection.


Jonathan


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
 
M

Michael Hopkins

[snip]

I have two efficiency-related questions:

1) What is the overhead of using the statify<T>::forward function? It
doesn't appear to be significant in use but I am interested in the
theoretical behaviour.


static
int forward ( int i ) {
if ( fo_ptr.get() == 0 ) {
throw( std::logic_error( "calling empty function object.\n" ) );
} else {
return( ((fo_ptr->object).*(fo_ptr->func))( i ) );
}
}

The overhead lies in the check for fo_ptr to be non-empty. Also, there is
some amount of indirection through the v-table of the object. But as soon
as the function does anything non-trivial, the run-time of the member
function should dominate the whole thing.

2) I believe that some hidden object copying goes on to make this work
because I had to change the non-copyable status of a large data member
within the class whose member function I am calling to allow it to
compile. Is this just during initialisation or every time the function is
called?

No. The copy is done in

static
void initialize ( value_type const & t, pmf_type f )
{
fo_ptr = std::auto_ptr< func_object >( new func_object( t, f ) );
}

Individual calls do not get their own copy.

Also, here is a slightly changes version that does not copy at all but just
stores a pointer to the object. Beware that I took out some const
qualifiers so that it works with non-const member functions, too. Also, in
this case the object needs to stay alive to handle all calls. So, you
cannot initialize this from a temporary (which should not compile since I
took out the const-qualifiers).


// pmf_to_static.cc
// ================

#include <iostream>
#include <memory>
#include <stdexcept>

template < typename T >
struct statify {

typedef T value_type;
typedef int ( T::* pmf_type ) ( int );

private:

// helper struct:
struct func_object {

value_type* object;
pmf_type func;

func_object ( value_type & o, pmf_type f )
: object ( &o )
, func ( f )
{}

};

// static data:
static std::auto_ptr< func_object > fo_ptr;

// this is a monostate class:
statify ( void );
statify ( statify const & );
statify& operator= ( statify const & );
~statify ( void ) {}

public:

static
void initialize ( value_type & t, pmf_type f )
{
fo_ptr = std::auto_ptr< func_object >( new func_object( t, f ) );
}

static
int forward ( int i ) {
if ( fo_ptr.get() == 0 ) {
throw( std::logic_error( "calling empty function object.\n" ) );
} else {
return( ((*(fo_ptr->object)).*(fo_ptr->func))( i ) );
}
}

}; // statify

template < typename T >
std::auto_ptr< typename statify<T>::func_object > statify<T>::fo_ptr;



typedef int ( * int_to_int ) ( int );

void print_exec ( int_to_int f, int i ) {
std::cout << i << " --> " << f(i) << '\n';
}


struct xxx {

int factor;

xxx ( int f )
: factor ( f )
{}

int mult ( int i ) {
return( factor*i );
}

};

int main ( void ) {
xxx M ( 5 );
statify< xxx>::initialize ( M, &xxx::mult );
print_exec( statify<xxx>::forward, 4 );
statify<xxx>::initialize( xxx(6), &xxx::mult ); // should be an error!!
}



Best

Kai-Uwe Bux

Thanks again Kai-Uwe - this works well and allows me to go back to
non-copyable objects.

Michael


_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/

_/ _/ _/_/_/ Hopkins Research Ltd
_/ _/ _/ _/
_/_/_/_/ _/_/_/ http://www.hopkins-research.com/
_/ _/ _/ _/
_/ _/ _/ _/ 'touch the future'

_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
 
I

Ismail Pazarbasi

I have already stated this is "not a good nor a standard way".

My intention was making no changes on existing code, not even
overloading optimize, and providing a way to pass various objects to
optimize function through the adapter. I thought using static methods
may introduce locking, if this is a multithreaded application and I
thought using vtable would be better in this case. May be I was wrong,
because never tested or thought long enough to make a final decision,
this was just a solution for my current environment. Nevertheless,
since standard doesn't specify vtable layout, which means it depends on
implementation, I have stated, many times, that the code may not run
even with the next version of the same compiler.

Ismail


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,019
Latest member
RoxannaSta

Latest Threads

Top