Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > passing functor or function to template class

Reply
Thread Tools

passing functor or function to template class

 
 
christof.warlich1@gmail.com
Guest
Posts: n/a
 
      07-10-2012
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;
}
 
Reply With Quote
 
 
 
 
Zoltan Juhasz
Guest
Posts: n/a
 
      07-10-2012
On Tuesday, 10 July 2012 10:14:58 UTC-4, Christof Warlich wrote:
> Hi,
>
> I&#39;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
 
Reply With Quote
 
 
 
 
Christof Warlich
Guest
Posts: n/a
 
      07-11-2012
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;
}


 
Reply With Quote
 
Zoltan Juhasz
Guest
Posts: n/a
 
      07-11-2012
On Wednesday, 11 July 2012 03:58:26 UTC-4, Christof Warlich wrote:
> 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
 
Reply With Quote
 
Christof Warlich
Guest
Posts: n/a
 
      07-18-2012
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&#39;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&#39;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&#39;ll have two &#39;Value&#39; specialization, one with
>
> T callable;
>
> and
>
> std::reference_wrapper&lt;T&gt; callable;
>
> in a partial specialization of &#39;Value&#39;.
>
>
> 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.
 
Reply With Quote
 
Christof Warlich
Guest
Posts: n/a
 
      07-18-2012
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;
}

 
Reply With Quote
 
Zoltan Juhasz
Guest
Posts: n/a
 
      07-18-2012
Excellent solution, thanks for sharing this.

-- Zoltan

On Wednesday, 18 July 2012 07:15:01 UTC-4, Christof Warlich wrote:
> After the inspiring discussion, and particularly encouraged by Zoltan&#39;s SFINAE example, I finally found the solution myself. I&#39;m sharing it here for the record and in case someone else might be interested.
>
> #include &lt;iostream&gt;
> template&lt;typename T&gt; struct isFunctionPointer {
> template&lt;typename U&gt; static char is_ptr(U (*)());
> static double is_ptr(...);
> static T t;
> enum {value = sizeof(is_ptr(t)) == sizeof(char)};
> };
> template&lt;typename T, bool = isFunctionPointer&lt;T&gt;::value&gt; struct Type {typedef T &amp; U;};
> template&lt;typename T&gt; struct Type&lt;T, true&gt; {typedef T U;};
> // A function.
> void *function() {
> std::cout &lt;&lt; &quot;function&quot; &lt;&lt; std::endl;
> return 0;
> }
> // A functor.
> class Functor {
> public:
> Functor() {}
> void *operator()() {
> std::cout &lt;&lt; &quot;functor&quot; &lt;&lt; std::endl;
> return 0;
> }
> private:
> Functor(const Functor &amp;
> } functor;
> template&lt;typename T&gt; struct Reference {
> Reference(typename Type&lt;T&gt;::U t):f(t) {}
> typename Type&lt;T&gt;::U f;
> };
> // Test if it works.
> int main() {
> Reference&lt;void *(*)()&gt; rf(function);
> Reference&lt;Functor&gt; rF(functor);
> rf.f();
> rF.f();
> return 0;
> }


 
Reply With Quote
 
Christof Warlich
Guest
Posts: n/a
 
      07-18-2012
> 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
 
Reply With Quote
 
Casey
Guest
Posts: n/a
 
      07-18-2012
On Wednesday, July 18, 2012 10:22:11 AM UTC-5, Christof Warlich wrote:
> 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.
 
Reply With Quote
 
Zoltan Juhasz
Guest
Posts: n/a
 
      07-18-2012
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 --

On Tuesday, 10 July 2012 10:14:58 UTC-4, Christof Warlich wrote:
> Hi,
>
> I&#39;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&#39;d like to pass functors byreference. 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 &lt;iostream&gt;
> // A function.
> void *function() {
> std::cout &lt;&lt; &quot;function&quot; &lt;&lt; std::endl;
> return 0;
> }
> // A functor.
> struct Functor {
> void *operator()() {
> std::cout &lt;&lt; &quot;functor&quot; &lt;&lt; std::endl;
> return 0;
> }
> } functor;
> // Both classes (struct Value and struct Reference) may
> // be instantiated with either a function or a functor.
> template&lt;typename T&gt; struct Value {
> Value(T t):f(t) {}
> T f;
> };
> template&lt;typename T&gt; struct Reference {
> Reference(T &amp;t):f(t) {}
> T &amp;f;
> };
> // Partial specialization of struct Reference for
> // (function) pointers).
> template&lt;typename T&gt; struct Reference&lt;T *&gt; {
> Reference(T t):f(t) {}
> T *f;
> };
> // Test if it works.
> int main() {
> Value&lt;void *(*)()&gt; vf(function);
> Value&lt;Functor&gt; vF(functor);
> vf.f();
> vF.f();
> Reference&lt;void *(*)()&gt; rf(function);
> Reference&lt;Functor&gt; rF(functor);
> rf.f();
> rF.f();
> return 0;
> }

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
parse error in gcc but success in vc.net, call a non_template class's template member function from a template class's member function! ken C++ 2 06-28-2005 06:57 AM
A parameterized class (i.e. template class / class template) is not a class? christopher diggins C++ 16 05-04-2005 12:26 AM
Allowing a functor or a function to be stored in a class James Aguilar C++ 11 03-30-2005 12:52 AM
Help Please: Passing Functor Object with Template type parameter CoolPint C++ 3 12-29-2003 03:25 PM
functor object in template class Chandra Shekhar Kumar C++ 5 06-26-2003 04:55 AM



Advertisments