Generalizing a function template

S

Szabolcs

I am looking for a way to generalize the function template below, so
that it will work with any type, not just double. Is this at all
possible in C++? I'd like to replace double (*fun)(double) with a
generalized A (*fun)(B).


template<double (*fun)(double)>
array<double> apply(const array<double> &source) {
array<double> result(source.size());
for (int i = 0; i < source.size(); i++)
result = fun(source);
return result;
}
 
S

Salt_Peter

I am looking for a way to generalize the function template below, so
that it will work with any type, not just double. Is this at all
possible in C++? I'd like to replace double (*fun)(double) with a
generalized A (*fun)(B).

template<double (*fun)(double)>
array<double> apply(const array<double> &source) {
array<double> result(source.size());
for (int i = 0; i < source.size(); i++)
result = fun(source);
return result;

}



I don't think a generalized A (*fun)(B) is a good idea, you'll be
exposing yourself (ie: int (*fun)(double) ).

To pass an array by reference you'ld have to deduce its Size:

template< typename T, const std::size_t Size >
void pass_by_ref(T(& array)[Size]) { ... }

Which then begs the question: Why not use a std::vector instead?

#include <iostream>
#include <ostream>
#include <vector>
#include <algorithm>
#include <iterator>

template< typename T >
std::vector<T> apply(const std::vector<T>& vt, T(*fun)(const T t))
{
std::vector< T > vresult(vt);
std::transform(vt.begin(), vt.end(), vresult.begin(), fun);
return vresult;
}

template< typename T >
T square(const T t)
{
return t * t;
}

template< typename T >
std::eek:stream& operator<<(std::eek:stream& os, const std::vector< T >& vt)
{
std::copy( vt.begin(),
vt.end(),
std::eek:stream_iterator< T >(os, "\n") );
return os;
}

int main()
{
std::vector< double > v(5, 1.1);
std::cout << v << std::endl;
std::vector< double > result = apply(v, square);
std::cout << result << std::endl;
}

/*
1.1
1.1
1.1
1.1
1.1

1.21
1.21
1.21
1.21
1.21
*/
 
G

Gianni Mariani

Szabolcs said:
I am looking for a way to generalize the function template below, so
that it will work with any type, not just double. Is this at all
possible in C++? I'd like to replace double (*fun)(double) with a
generalized A (*fun)(B).


template<double (*fun)(double)>
array<double> apply(const array<double> &source) {
array<double> result(source.size());
for (int i = 0; i < source.size(); i++)
result = fun(source);
return result;
}


Is this what you mean ?

template<typename T, typename S, T (*fun)(S)>
array<T> apply(const array<S> &source) {
array<T> result(source.size());
for (int i = 0; i < source.size(); i++)
result = fun(source);
return result;
}

double fx( float );

int main()
{
array< float > x;
apply< double, float, fx >( x );
}
 
S

SzH

Szabolcs said:
I am looking for a way to generalize the function template below, so
that it will work with any type, not just double. Is this at all
possible in C++? I'd like to replace double (*fun)(double) with a
generalized A (*fun)(B).
template<double (*fun)(double)>
array<double> apply(const array<double> &source) {
array<double> result(source.size());
for (int i = 0; i < source.size(); i++)
result = fun(source);
return result;
}


Is this what you mean ?

template<typename T, typename S, T (*fun)(S)>
array<T> apply(const array<S> &source) {
array<T> result(source.size());
for (int i = 0; i < source.size(); i++)
result = fun(source);
return result;

}

double fx( float );

int main()
{
array< float > x;
apply< double, float, fx >( x );

}


Thanks for the reply!

Yes, that's what I mean, except that I would like to avoid having to
explicitly specify the types "double" and "float". I would just like
to write apply<fx>(x);

Maybe this is problematic if fx is allowed to have a different return
type than its argument's type. But let us consider the simpler case
when they are the same:

template<typename T, T (*fun)(T)>
array<T> apply(const array<T> &source) {
array<T> result(source.size());
for (int i = 0; i < source.size(); i++)
result = fun(source);
return result;
}

Here the compiler should be able to deduce what type T actually is
from the argument passed to apply(). Is is possible to somehow
exchange the order of template parameters, like

template<T (*fun)(T), typename T>
array<T> apply(const array<T> &source) { ... }

, to make it possible to call apply() as apply<fx>(some_array) ?
 
J

Jerry Coffin

I am looking for a way to generalize the function template below, so
that it will work with any type, not just double. Is this at all
possible in C++? I'd like to replace double (*fun)(double) with a
generalized A (*fun)(B).


template<double (*fun)(double)>
array<double> apply(const array<double> &source) {
array<double> result(source.size());
for (int i = 0; i < source.size(); i++)
result = fun(source);
return result;
}


As long as you only want to support pointers to functions (not functors)
it's pretty simple -- just templatize the input and output types:

// warning: only minimally tested
//
// This depends on 'array; defining size_type, though size_t would also
// work better than int without that requirement.
//
template <class A, class B>
array<A> apply(const array<B> &source, A (*fun)(B)) {
array<A> result(source.size());
for (array<A>::size_type i=0; i<source.size(); ++i)
result = fun(source);
return result;
}

You'll have to work a bit harder if you want to support functors. OTOH,
std::transform already provides the same basic capability with slightly
differetnt syntax and more flexibility, so I don't see much point in
working much more on this...
 
S

SzH

I am looking for a way to generalize the function template below, so
that it will work with any type, not just double. Is this at all
possible in C++? I'd like to replace double (*fun)(double) with a
generalized A (*fun)(B).
template<double (*fun)(double)>
array<double> apply(const array<double> &source) {
array<double> result(source.size());
for (int i = 0; i < source.size(); i++)
result = fun(source);
return result;


I don't think a generalized A (*fun)(B) is a good idea, you'll be
exposing yourself (ie: int (*fun)(double) ).

To pass an array by reference you'ld have to deduce its Size:

template< typename T, const std::size_t Size >
void pass_by_ref(T(& array)[Size]) { ... }

Which then begs the question: Why not use a std::vector instead?

#include <iostream>
#include <ostream>
#include <vector>
#include <algorithm>
#include <iterator>

template< typename T >
std::vector<T> apply(const std::vector<T>& vt, T(*fun)(const T t))
{
std::vector< T > vresult(vt);
std::transform(vt.begin(), vt.end(), vresult.begin(), fun);
return vresult;

}

template< typename T >
T square(const T t)
{
return t * t;

}

template< typename T >
std::eek:stream& operator<<(std::eek:stream& os, const std::vector< T >& vt)
{
std::copy( vt.begin(),
vt.end(),
std::eek:stream_iterator< T >(os, "\n") );
return os;

}

int main()
{
std::vector< double > v(5, 1.1);
std::cout << v << std::endl;
std::vector< double > result = apply(v, square);
std::cout << result << std::endl;

}

/*
1.1
1.1
1.1
1.1
1.1

1.21
1.21
1.21
1.21
1.21
*/


Thanks for the reply!

Of course it is not difficult to get it working if (*fun) is an
argument to the function (and not a template parameter), but I would
like to keep it a template parameter so that it can be inlined when
the function is small. gcc (with -O3) does not optimize away the
function calls when (*fun) is an argument to the function.
 
J

Jerry Coffin

I am looking for a way to generalize the function template below, so
that it will work with any type, not just double. Is this at all
possible in C++? I'd like to replace double (*fun)(double) with a
generalized A (*fun)(B).


template<double (*fun)(double)>
array<double> apply(const array<double> &source) {
array<double> result(source.size());
for (int i = 0; i < source.size(); i++)
result = fun(source);
return result;
}


As long as you only want to support pointers to functions (not functors)
it's pretty simple -- just templatize the input and output types:

// warning: only minimally tested
//
// This depends on 'array; defining size_type, though size_t would also
// work better than int without that requirement.
//
template <class A, class B>
array<A> apply(const array<B> &source, A (*fun)(B)) {
array<A> result(source.size());
for (typename array<A>::size_type i=0; i<source.size(); ++i)
result = fun(source);
return result;
}

One way to make it work with function objects looks like this:

template <class F>
array<typename F::result_type>
apply(const array<typename F::argument_type> &source, F f)
{
typedef typename array<typename F::argument_type>::size_type s_type;

array<typename F::result_type> results(source.size());

for (s_type i=0; i<source.size(); ++i)
results = f(source);
return results;
}

Since this uses result_type and argument_type, you have to define those,
which is usually done with std::unary_function, something like this:

struct func : public std::unary_function<int, double> {
double operator()(int input) { return sqrt(double(input)); }
};

FWIW, you can include both of these to overload apply to work with both
functions and unary_function objects.
 
G

Gianni Mariani

SzH wrote:
....
, to make it possible to call apply() as apply<fx>(some_array) ?

Yeah, that kind of syntax would be nice, I can't see how you would do it
with just template parameters. To do that you would need a way of
specifying a function template parameter without any "known" types e.g.

typename <THING fun>
..... apply( .... )

The only "thing" template parameter type is a "typename" (or class).

Not even partial template specialization of a class will work since it
is illegal to have template parameters not used in the partial template
specialization

e.g. ... illegal:

template <int>
class apply;

template <typename T, typename S, T (*fun)(S) > // illegal pts
class apply< fun >
{
public:

const array<S> & source;

apply( const array<S> & isource )
: source( isource )
{}

operator array<T> ()
{
array<T> result(source.size());
for (int i = 0; i < source.size(); i++)
result = fun(source);
return result;
}
};
 

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,769
Messages
2,569,581
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top