passing functor or function to template class

C

christof.warlich1

Hi,

I'm working at a generic library that must accept either function pointers or functors to be passed to a template class. This works quite straight forward as long as both are being passed by value. But as the functors that need to be passed may not be copyable, I'd like to pass functors by reference.. But doing this breaks the function pointer case. Although I could fix this by partial specialization (see code below), this looks rather clumpsy to me: It there a better way to deal with this situation? Take into account that my template class is quite big, so that specializing causes quite some code duplication.

Thanks for any ideas,

Chris

#include <iostream>
// A function.
void *function() {
std::cout << "function" << std::endl;
return 0;
}
// A functor.
struct Functor {
void *operator()() {
std::cout << "functor" << std::endl;
return 0;
}
} functor;
// Both classes (struct Value and struct Reference) may
// be instantiated with either a function or a functor.
template<typename T> struct Value {
Value(T t):f(t) {}
T f;
};
template<typename T> struct Reference {
Reference(T &t):f(t) {}
T &f;
};
// Partial specialization of struct Reference for
// (function) pointers).
template<typename T> struct Reference<T *> {
Reference(T t):f(t) {}
T *f;
};
// Test if it works.
int main() {
Value<void *(*)()> vf(function);
Value<Functor> vF(functor);
vf.f();
vF.f();
Reference<void *(*)()> rf(function);
Reference<Functor> rF(functor);
rf.f();
rF.f();
return 0;
}
 
Z

Zoltan Juhasz

Hi,

I'm working at a generic library that must accept either
function pointers or functors to be passed to a template class.
This works quite straight forward as long as both are being
passed by value. But as the functors that need to be passed
may not be copyable, I'd like to pass functors by reference.

I think the correct approach is a bit different than your
solution.

- you should have a generic function wrapper (see std::function),
that is only concerned providing wrapper functionality on
a callable object.
- auxiliary facility to wrap non-copy-able objects ( see std::ref
and std::cref) by storing a reference - when possible.

Luckily C++11 (or Boost if you do not have C++11 compile) gives
you all the tools. See the modified version of your example:

#include <iostream>
#include <functional>

void *function()
{
std::cout << "function" << std::endl;
return 0;
}

struct Functor
{
private:
Functor( const Functor& );

public:
Functor(){}

void *operator()()
{
std::cout << "functor" << std::endl;
return 0;
}
} functor;



int main() {
std::function<void ()> vf = function;
std::function<void ()> vF = std::ref( functor );
vf();
vF();
std::function<void ()> rf = std::ref( function );
std::function<void ()> rF = std::ref( functor );
rf();
rF();
return 0;
}

Note: of course in this case you would not write

std::ref( function )

but it is possible, and will not yield to an error.


Ofc. you can replicate these tools if needed, and
by separating the concerns as described above,
should be able to get rid of the problematic
code-duplication you mentioned.


PS: Btw. I'd like to note that the usual definition of
functors have the requirement of assignable (http://www.sgi.com/tech/stl/Assignable.html), but of course
I accept that in some cases you might want to store it by
reference, but that probably indicate some design issues...

-- Zoltan
 
C

Christof Warlich

Many thanks for your suggestions; you are right, this avoids the code duplication caused by the specialization. But the price to pay is that now, the user of the interface has to provide the appropriate functionality to handle noncopyable objects: The example below shows that everything is fine as long as the callable object is either a function or a copyable functor, but that (from the user's point of view) quite some hackery is required for noncopyable functors.

Any ideas how this could be avoided, if possible not using more than tr1?

#include <iostream>
#include <tr1/functional>
// A function.
const char *function() {return "function\n";}
// A copyable functor.
struct CopyableFunctor {
const char *operator()() {return "copyableFunctor\n";}
} copyableFunctor;
// A non-copyable functor.
struct NoncopyableFunctor {
NoncopyableFunctor() {}
const char *operator()() {return "noncopyableFunctor\n";}
private:
NoncopyableFunctor(const NoncopyableFunctor &);
} noncopyableFunctor;
// May be instantiated with either a function or a copyable functor.
template<typename T> struct Value {
Value(T t):callable(t) {}
T callable;
};
// Test if it works.
int main() {
Value<const char *(*)()> f1(function);
Value<CopyableFunctor> f2(copyableFunctor);
// If the functor is non-copyable, the hackery below is required.
Value<std::tr1::function<const char *()> > f3(std::tr1::ref(noncopyableFunctor));
std::cout << f1.callable();
std::cout << f2.callable();
std::cout << f3.callable();
return 0;
}
 
Z

Zoltan Juhasz

Many thanks for your suggestions; you are right, this avoids the
code duplication caused by the specialization. But the price to
pay is that now, the user of the interface has to provide the
appropriate functionality to handle noncopyable objects

I'd like to note that your original example also suffers from
that problem, you had to separately handle the store-by-value
and store-by-reference case (using Value and Reference class
templates).

I think using std::ref on the caller side is not hack, but
explicit expression of intention, which might have important
consequences. The caller needs to realize that the provided object will
be stored as reference, therefore he must ensure that the argument
outlives the wrapper object.


How about storing the argument as reference (possibly using std::ref)
or as a pointer in all cases? You can store copy-, and non-copy-able
objects in the same way. Ofc. the caller must abide certain lifetime
requirements in that case (e.g. temporary anonymous objects
will not be usable).


Alternative C++11 solution could be this: if I understand correctly,
you'd like to completely hide the decision on storage from user.

Currently the decision (whether to indirectly store the argument
by value or reference) is explicitly expressed by the caller by
wrapping the non-copy-able objects into std::ref.

I think this decision could be done automatically, by checking
if the object is copy-assignable (see std::is_copy_assignable in
<type_traits>). If it is copy-assignable, then you can
store by value, store by reference otherwise.

This means you'll have two 'Value' specialization, one with

T callable;

and

std::reference_wrapper<T> callable;

in a partial specialization of 'Value'.


The only problem is that this solution requires a C++11
compiler with support for <type_traits> (20.9 Metaprogramming
and type traits), and I am not sure if that exists at
this point at all; might be good idea to keep in mind
for future refinement of your code.


As far as I know you cannot implement is_copy_assignable in C++98/03;
e.g. the ellipsis failover technique does not work, since access
checking happens before SFINAE could be triggered and produces an error
(instead of failing-over to the ellipsis):

typedef char false_type;
struct true_type
{
char s[2];
};


template < typename T >
true_type is_copyable_impl ( T );
false_type is_copyable_impl ( ... );

int main()
{
std::cout << sizeof( is_copyable_impl( function ) ) << std::endl;
std::cout << sizeof( is_copyable_impl( copyableFunctor ) ) << std::endl;

// triggers access violation when copy-ctor is private
std::cout << sizeof( is_copyable_impl( noncopyableFunctor ) ) << std::endl;
}


I think that the cleanest approach is still std::func and explicit
std::ref.

-- Zoltan
 
C

Christof Warlich

Am Mittwoch, 11. Juli 2012 21:10:18 UTC+2 schrieb Zoltan Juhasz:
On Wednesday, 11 July 2012 03:58:26 UTC-4, Christof Warlich wrote:
&gt; Many thanks for your suggestions; you are right, this avoids the
&gt; code duplication caused by the specialization. But the price to
&gt; pay is that now, the user of the interface has to provide the
&gt; appropriate functionality to handle noncopyable objects

I'd like to note that your original example also suffers from
that problem, you had to separately handle the store-by-value
and store-by-reference case (using Value and Reference class
templates).
Yeah, but the distinction is done on the library side, being transparent inthe calling code.
I think using std::ref on the caller side is not hack, but
explicit expression of intention, which might have important
consequences. The caller needs to realize that the provided object will
be stored as reference, therefore he must ensure that the argument
outlives the wrapper object.
Right, this a second reason (besides the code duplication) why I feel uncomfortable with my initial approach.
How about storing the argument as reference (possibly using std::ref)
or as a pointer in all cases? You can store copy-, and non-copy-able
objects in the same way. Ofc. the caller must abide certain lifetime
requirements in that case (e.g. temporary anonymous objects
will not be usable).
This is what I tried initially, as it would by far be the cleanest solution.. But I couldn't get the same interface to work with both functors and functions when using either references or pointers: It always worked well (for both references and pointers) for functors, but gave compile-time errors for functions. I could get along with specialization though, but this throws me back to my initial point. Any advice on how to get this right would be very much welcomed.
Alternative C++11 solution could be this: if I understand correctly,
you'd like to completely hide the decision on storage from user.

Currently the decision (whether to indirectly store the argument
by value or reference) is explicitly expressed by the caller by
wrapping the non-copy-able objects into std::ref.

I think this decision could be done automatically, by checking
if the object is copy-assignable (see std::is_copy_assignable in
&lt;type_traits&gt;). If it is copy-assignable, then you can
store by value, store by reference otherwise.

This means you'll have two 'Value' specialization, one with

T callable;

and

std::reference_wrapper&lt;T&gt; callable;

in a partial specialization of 'Value'.


The only problem is that this solution requires a C++11
compiler with support for &lt;type_traits&gt; (20.9 Metaprogramming
and type traits), and I am not sure if that exists at
this point at all; might be good idea to keep in mind
for future refinement of your code.


As far as I know you cannot implement is_copy_assignable in C++98/03;
e.g. the ellipsis failover technique does not work, since access
checking happens before SFINAE could be triggered and produces an error
(instead of failing-over to the ellipsis):

typedef char false_type;
struct true_type
{
char s[2];
};


template &lt; typename T &gt;
true_type is_copyable_impl ( T );
false_type is_copyable_impl ( ... );

int main()
{
std::cout &lt;&lt; sizeof( is_copyable_impl( function ) ) &lt;&lt; std::endl;
std::cout &lt;&lt; sizeof( is_copyable_impl( copyableFunctor ) ) &lt;&lt; std::endl;

// triggers access violation when copy-ctor is private
std::cout &lt;&lt; sizeof( is_copyable_impl( noncopyableFunctor ) ) &lt;&lt; std::endl;
}
I already searched the Web for some is_copyable implementation for C++ 2003, but without success. Thanks for sheding some light on why I didn't find one :).
I think that the cleanest approach is still std::func and explicit
std::ref.
Agreed, except if the interface could be done with either references or pointers for both functors and functions as discussed above.

Anyway, again many thanks for your very valuable help.
 
C

Christof Warlich

After the inspiring discussion, and particularly encouraged by Zoltan's SFINAE example, I finally found the solution myself. I'm sharing it here for the record and in case someone else might be interested.

#include <iostream>
template<typename T> struct isFunctionPointer {
template<typename U> static char is_ptr(U (*)());
static double is_ptr(...);
static T t;
enum {value = sizeof(is_ptr(t)) == sizeof(char)};
};
template<typename T, bool = isFunctionPointer<T>::value> struct Type {typedef T & U;};
template<typename T> struct Type<T, true> {typedef T U;};
// A function.
void *function() {
std::cout << "function" << std::endl;
return 0;
}
// A functor.
class Functor {
public:
Functor() {}
void *operator()() {
std::cout << "functor" << std::endl;
return 0;
}
private:
Functor(const Functor &);
} functor;
template<typename T> struct Reference {
Reference(typename Type<T>::U t):f(t) {}
typename Type<T>::U f;
};
// Test if it works.
int main() {
Reference<void *(*)()> rf(function);
Reference<Functor> rF(functor);
rf.f();
rF.f();
return 0;
}
 
C

Christof Warlich

Excellent solution, thanks for sharing this.
Thanks, I got excellent help :).

But unfortunately, the solution is still unusable for me as it strangely doesn't work any longer if the functor uses the Curiously Recurring Template Pattern, which I need in my real implementation.

Below is a sample implementation showing the issue: As long as SMART isn't defined, the initial "dumb" implementation is used and everything compiles fine even when CRTP is involved, but using the new, "smart" solution, I getthe following compiler errors:

noncopyFunctorOrFunction.cpp: In instantiation of »isFunctionPointer<X>«:
noncopyFunctorOrFunction.cpp:25:5: instantiated from »Reference<X>«
noncopyFunctorOrFunction.cpp:41:31: instantiated from here
noncopyFunctorOrFunction.cpp:5:14: Error: »isFunctionPointer<X>::t« hasincomplete type
noncopyFunctorOrFunction.cpp:41:8: Error: forward declaration of »struct X«

#include <iostream>
template<typename T> struct isFunctionPointer {
template<typename U> static char is_ptr(U (*)());
static double is_ptr(...);
static T t;
enum {value = sizeof(is_ptr(t)) == sizeof(char)};
};
template<typename T, bool = isFunctionPointer<T>::value> struct Type {typedef T & U;};
template<typename T> struct Type<T, true> {typedef T U;};
// A function.
void function() {std::cout << "function" << std::endl;}
// A functor.
class Functor {
public:
Functor() {}
void operator()() {std::cout << "functor" << std::endl;}
private:
Functor(const Functor &);
} functor;
//#define SMART
#ifdef SMART
// Unfortunately, this does not work together with
// the Curiously Recurring Template Pattern ....
template<typename T> struct Reference {
Reference(typename Type<T>::U t):f(t) {}
typename Type<T>::U f;
};
#else
// ... while the simple-minded approach still works fine.
template<typename T> struct Reference {
Reference(T &t):f(t) {}
T &f;
};
// Partial specialization of struct Reference for
// (function) pointers).
template<typename T> struct Reference<T *> {
Reference(T t):f(t) {}
T *f;
};
#endif
struct X: public Reference<X> {
X(): Reference<X>(*this) {}
void operator()() {std::cout << "functor with CRTP" << std::endl;}
} x;
// Test if it works.
int main() {
Reference<void (*)()> rf(function);
Reference<Functor> rF(functor);
rf.f();
rF.f();
Reference<X> rX(x);
rX.f();
return 0;
}

Any ideas what's going wrong and how to solve it?

Chris
 
C

Casey

Any ideas what's going wrong and how to solve it?

Your implementation of isFunctionPointer<T> requires T to be a complete type. At
the point when Reference<X> is instantiated to be a base class of X, X is
incomplete. If you replace your implementation with:

template<typename T> struct isFunctionPointer {
static const bool value = false;
};
template<typename T> struct isFunctionPointer<T(*)()> {
static const bool value = true;
};

Then the compiler will be smart enough to know that incomplete types can't be
function pointers.
 
Z

Zoltan Juhasz

Christof,

Yes, that indeed breaks the CRTP as the Reference indirectly
tries to instantiate the object when it checks for ptr.

It turns out that you actually do not need the above-mentioned
ellipsis, but a simple partial specialization works. That should
work even the existence of CRTP.

Note: I noticed that now you essentially store every functor as
reference, I guess the life-time requirements then has to be
made clear to the caller side.

-- CODE --

#include <iostream>

template < typename T >
struct StorageSelector
{
typedef T & type;
};

template < typename T >
struct StorageSelector < T * >
{
typedef T * type;
};

// A function.
void *function() {
std::cout << "function" << std::endl;
return 0;
}
// A functor.
class Functor {
public:
Functor() {}
void *operator()() {
std::cout << "functor" << std::endl;
return 0;
}
private:
Functor(const Functor &);
} functor;


template<typename T>
struct Reference
{
typedef typename StorageSelector< T >::type StorageT;
Reference( StorageT t ) : f( t ) {}
StorageT f;
};

struct X: public Reference<X>
{
X(): Reference<X>(*this) {}
void operator()() {std::cout << "functor with CRTP" << std::endl;}
} x;


int main() {
Reference<void *(*)()> rf(function);
Reference<Functor> rF(functor);
rf.f();
rF.f();
return 0;
}

-- /CODE --
 
Z

Zoltan Juhasz

Yes, that indeed breaks the CRTP as the Reference indirectly
tries to instantiate the object when it checks for ptr.

It turns out that you actually do not need the above-mentioned
ellipsis, but a simple partial specialization works. That should
work even the existence of CRTP.

If you happen to have TR1 libs, it is actually even
more simple (would be the same with C++11):


#include <iostream>
#include <tr1/functional>

// A function.
void *function() {
std::cout << "function" << std::endl;
return 0;
}
// A functor.
class Functor {
public:
Functor() {}
void *operator()() {
std::cout << "functor" << std::endl;
return 0;
}
private:
Functor(const Functor &);
} functor;


template< typename T >
struct Callable
{
typedef std::tr1::function< T > StorageT;
template < typename TT >
Callable( TT & callable )
: invoke( std::tr1::ref( callable ) )
{}
StorageT invoke;
};

struct X: public Callable< void() >
{
typedef Callable< void() > BaseT;
X(): BaseT( *this ) {}
void operator()() {std::cout << "functor with CRTP - not anymore :)" << std::endl;}
} x;

int main()
{
Callable< void() > rf( function );
Callable< void() > rF( functor );
X xF;

rf.invoke();
rF.invoke();
xF();

// that fails - by pure coincident - but this
// is good for us as Callable cannot be used
// with temporary objects
// Callable< void() > invalid_rF( Functor() );
// invalid_rF.invoke();

return 0;
}


My only concern with these solutions that they store
everything as reference, thus disallow one of the most
frequent use of functors (passing them as temporary rvalue).

-- Zoltan
 
C

Christof Warlich

If you replace your implementation with:
template&lt;typename T&gt; struct isFunctionPointer {
static const bool value = false;
};
template&lt;typename T&gt; struct isFunctionPointer&lt;T(*)()&gt; {
static const bool value = true;
};

That's great: It is shorter, easier to understand and more generic than my solution. Thanks a lot.
 
C

Christof Warlich

template &lt; typename T &gt;
struct StorageSelector
{
typedef T &amp; type;
};

template &lt; typename T &gt;
struct StorageSelector &lt; T * &gt;
{
typedef T * type;
};

And this makes it even simpler :).
 
C

Christof Warlich

template&lt;typename T&gt; struct isFunctionPointer {
static const bool value = false;
};
template&lt;typename T&gt; struct isFunctionPointer&lt;T(*)()&gt; {
static const bool value = true;
};
That's great! It is shorter, easier to understand and more generic than my solution. Thanks a lot!
 
C

Christof Warlich

If you happen to have TR1 libs, it is actually even
more simple (would be the same with C++11):

....
Hmm - I'm not sure if I find that more simple :), but that's maybe because I'm still not too much into the new C++0x standard.
My only concern with these solutions that they store
everything as reference, thus disallow one of the most
frequent use of functors (passing them as temporary rvalue).

That's right, the library user should know wheather she has to take care of object lifetime. But using the tr1::function framework as you suggested earlier would overstrain my (local) users of the library.

But how about the following sample implementation, where the library user may pass function pointers and copyable objects by value and noncopyable objects by pointer?:

/*****************/
/* Library code: */
/*****************/
#include <iostream>
template<typename T> struct IsPointer {
IsPointer(T f):f(f) {}
T &f;
};
template<typename T> struct IsPointer<T *> {
IsPointer(T *f):f(*f) {}
T &f;
};
// Some generic library function accepting
// all sorts of callable objects.
template<typename Callable> void print(Callable func) {
IsPointer<Callable> p(func);
std::cout << p.f() << std::endl;
// But how can an assignment like the
// following be made to work for all
// types of callable objects?
//Callable *x = &(p.f);
}
/*********************/
/* Application code: */
/*********************/
// A function.
const char *function() {return "function";}
// A copyable functor.
struct CopyableFunctor {
const char *operator()() {return "copyable functor";}
} copyableFunctor;
// A noncopyable functor.
class NonCopyableFunctor {
public:
NonCopyableFunctor() {}
const char *operator()() {return "noncopyable functor";}
private:
NonCopyableFunctor(const NonCopyableFunctor &);
} nonCopyableFunctor;
// Test it:
int main() {
print(function);
print(copyableFunctor);
print(&nonCopyableFunctor);
return 0;
}

Note that I switched to a function template for the "library interface" to show that template deduction works fine as well. The only concern with this approach
is pointed out in the code comments above: How may I make assignments with my callable objects that work for all types (i.e. funtions, functors and pointers to functors)?

Cheers,

Chris
 

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

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top