views:

236

answers:

2

I'm storing images as arrays, templated based on the type of their elements, like Image<unsigned> or Image<float>, etc. Frequently, I need to perform operations on these images; for example, I might need to add two images, or square an image (elementwise), and so on. All of the operations are elementwise. I'd like get as close as possible to writing things like:

float Add(float a, float b) { return a+b; }
Image<float> result = Add(img1, img2);

and even better, things like

complex ComplexCombine(float a, float b) { return complex(a, b); }
Image<complex> result = ComplexCombine(img1, img2);

or

struct FindMax {
    unsigned currentMax;
    FindMax(): currentMax(0) {}
    void operator(unsigned a) { if(a > currentMax) currentMax = a; }
};

FindMax findMax;
findMax(img);
findMax.currentMax;  // now contains the maximum value of 'img'

Now, I obviously can't exactly do that; I've written something so that I can call:

Image<float> result = Apply(img1, img2, Add);

but I can't seem to figure out a generic way for it to detect the return type of the function/function object passed, so my ComplexCombine example above is out; also, I have to write a new one for each number of arguments I'd like to pass (which seems inevitable).

Any thoughts on how to achieve this (with as little boilerplate code as possible)?

+3  A: 

This is why the unary_function, binary_function, etc. types in he STL have the result_type typedefs. If you use std::ptr_fun, you can take advantage of this with Apply,

e.g.

template<typename Func, typename Arg, typename Result = Func::result_type>
Image<Result> Apply(Arg a1, Arg a2, Func f) {
        return Image<Result>(f(a1,a2));
}

And do,

Apply(img1, img2, std::ptr_fun(&Add));


Ok, you caught me, I didn't test it. It is still possible, but more work without being able to directly use some of the STL functionality

#include <iostream>

template<typename T>
struct Image {
  T val_;
  Image(const T& val) : val_(val) {}
};

template<typename Arg1, typename Arg2, typename Result>
struct image_fun_adapter {
  Result (*f)(Arg1, Arg2);
  Image<Result> operator()(const Image<Arg1>& a1, const Image<Arg2>& a2) {
    return Image<Result>(f(a1.val_, a2.val_));
  }
};

template<typename Arg1, typename Arg2, typename Result>
image_fun_adapter<Arg1,Arg2,Result> image_fun_ptr(Result (*f)(Arg1,Arg2)) {
  image_fun_adapter<Arg1,Arg2,Result> rv;
  rv.f = f;
  return rv;
}

float Add(float a, float b) { return a + b; }

int main() {
  Image<float> a(1.0);
  Image<float> b(12.5);
  // this is the cute bit:
  Image<float> c = image_fun_ptr(&Add)(a,b);

  std::cout << c.val_ << std::endl;
  return 0;
}
Logan Capaldo
I get (1) error: expected type-specifier (1) error: expected ‘>’ (2) error: default template arguments may not be used in function templates(I've numbered the lines based on your example.) I'm using gcc v4.2.4.
Jesse Beder
+1  A: 

Template specialization!

template<typename T>
T Add(const T &left, const T &right)
{
    return left + right;
}

template<typename T>
struct AddTwo
{
    T operator()(const T &left, const T &right)
    {
        return Add(left, right);
    }
};

// something like this (not exactly sure)
template<std::vector<typename V>>
std::vector<V> Add(const std::vector<V> &left, const std::vector<V> &right)
{
    assert(left.size() == right.size());

    std::vector ret(left.size());

    std::transform(left.const_begin(), left.const_end(), right.const_begin(), ret.begin(), AddTwo());

    return ret;
}
strager