views:

187

answers:

4

I want to somehow merge templates like these into one:

template <class Result, class T1, class T2>
class StupidAdd
{
public:
    T1 _a; T2 _b;
    StupidAdd(T1 a, T2 b):_a(a),_b(b) {}
    Result operator()() { return _a+_b; }
};

template <class Result, class T1, class T2>
class StupidSub
{
public:
    T1 _a; T2 _b;
    StupidSub(T1 a, T2 b):_a(a),_b(b) {}
    Result operator()() { return _a-_b; }
};

(followed by the same code for Mul, Div, etc) where all the code is the same, except for the actual "+", "-" (and "StupidAdd", "StupidSub", etc).

These Stupid "functors" are then used by another template. How can I avoid the repetition, WITHOUT the preprocessor? (The reason I got into templates was to avoid the preprocessor)

That is, how can I pass arithmetic operators into a template?

+6  A: 

Maybe you could use std::plus<T>, std::minus<T>, std::multiplies<T> and std::divides<T>. However, these will work only if both operands are of the same type, or probably if the left one can be converted to the type of the first one.

I don't see any way to achieve what you're trying to do, except by using the preprocessor. Any good reasons for not wanting macros ?

If you want to make sure the return type is large enough to contains the result, you could do something along this way:

#include <functional>
#include <boost/mpl/if_.hpp>

// Metafunction returning the largest type between T and U
// Might already exist in Boost but I can't find it right now...maybe 
// boost::math::tools::promote_args
template <typename T, typename U>
struct largest :
    boost::mpl::if_<
        boost::mpl::bool_<(sizeof(T) > sizeof(U))>,
        T,
        U
    >
{};

template <typename T, typename U, template <typename S> class Op>
struct Foo
{
    typedef typename largest<T, U>::type largeType;

    largeType bar(const T & t, const U & u)
    {
        return Op<largeType>()(t, u); // Applies operator+
    }
};

int main()
{
    Foo<int, double, std::plus> f;
    double d = f.bar(12, 13.0); // takes int and double, returns double
}

Here, I used Boost MPL to write the largest metafunction, but you could write your own if metafunction if you cannot use Boost (class template parameterized by two types and a bool, specialized for true and false).

To determine the return type of an expression, you could also have a look at boost::result_of which, if I understand correctly, is equivalent to the upcoming decltype operator in C++0x.

Luc Touraille
I can't - I am depending on the fact that operator+(int, double) returns a double.std::plus needs both args to be of the same type.
OldCoder
My basic reason for hating "cpp" is that it makes debugging a living hell - the moment you start using macros is the moment you lose the ability to do a step-by-step inside gdb...
OldCoder
I was just editing to reflect this fact :). Well then, I don't see any way of doing this. Unless it is possible to take the address of a built-in operator, which I doubt.
Luc Touraille
+2  A: 

Thanks Luc, this is very cool. I finally did it in a simpler way:

#include <functional>

template <
    class Result, 
    class T1, 
    class T2,
    template <class ReturnType> class BinaryOp>
class Stupido
{
public:
    T1 _a; T2 _b;
    Stupido(T1 a, T2 b):_a(a),_b(b) {}
    Result operator()() { return BinaryOp<Result>()((Result)_a,(Result)_b); }
};

And used the "plus", "minus" when instantiating Stupido. The cast to "Result" was enough for my needs (int + double => double + double => double)

OldCoder
A: 

I'd use the C++ 0x decltype and the new definition of auto. You'll still need to define the classes, but using these, you can do a nicer job of defining them. They'll still be about as much work to define, but at least using them will be considerably cleaner. In particular, you can use decltype/auto to deduce the correct return type instead of having to specify it explicitly.

These are available with a fair number of recent compilers -- Intel C++, g++, Comeau, the beta of VC++ 2010, and even the most recent iteration of Borland/Inprise/Embarcadero/this week's new name.

Jerry Coffin
+2  A: 

I think there's an improvement to OldCoder's solution:

#include <functional>

template <class Result,
          template <class Result> class BinaryOp>
struct Stupido
{
  template <typename T1, typename T2>
  Result operator()(const T1& a, const T2& b) { return BinaryOp<Result>()((Result)a,(Result)b); }
};

This way the call can be made as:

Stupido<int, std::plus > stup;
int result = stup(3.0f, 2.0);

and the same function object can be used with multiple operands, so it could be passed in to a std::transform call.

I believe there must be a way to remove one Result from the template declaration, but I am unable to find it.

Mark Ruzon
template <class Result, template <class> class BinaryOp> struct Stupido {...};
jon hanson