1

From here on a (endo)functor is something able to take an object and transform it in another object of the same type. The simplest example of functor is the identity:

struct Identity {
    template <typename T>
    T Apply(T x) {
        return x
    }
};

I need to have a "Functor type" that identifies a generic Functor. What I would like to do is write code like:

class Sum {
  public:
    Sum(Functor* f, Functor* g) :
        f_(f),
        g_(g) {}
    template <typename T>
    T Apply(T x) { return f_->Apply(x) + g_->Apply(x); }
  private
    Functor* f_;
    Functor* g_;
};

The first idea that came to my mind is of course using a virtual class:

struct Functor {
    template <typename T>
    virtual T Apply(T x) = 0;
};

The unsolvable problem with this approach is that templates cannot be virtual.

Then I tried using C++ concepts. But, as stated in Specifying a concept for a type that has a member function template using Concepts Lite and C++ Concepts: Can I define a concept that is itself a template? it is not possible to have a "templated concept".

Finally I have stumbled upon How to achieve "virtual template function" in C++ and therefore I came up with the following possible implementation:

struct Functor {
    template <typename T>
    T Apply(const T& x);  // No more virtual!!!
};

// ... Identity and Sum declarations properly inheriting from Functor ...

template <typename T>
T Functor::Apply(T x) {
    if (Identity* specialized =
            dynamic_cast<Identity*>(this)) {
        return specialized->Apply(x);
    } else if (const Sum* specialized =
            dynamic_cast<const Sum*>(this)) {
        return specialized->Apply(x);
    } else ...
}

Even though this is compiling, it's not the best solution. The main issues are: performance and code repetition. The performance issue comes from the fact that each time Apply is called on a Functor the long if clause inside Functor::Apply must be resolved. This is a big problem as Functor can be deeply nested (so calling Apply may result in multiple call to Functor::Apply). The "code repetition" issue is quite self evident as each time I want to define a new Functor I have also to modify Functor::Apply adding a new if clause.

What I am asking here is whether there is a proper (cleaner) way to define a Functor interface/concept that makes possible creating classes like Sum. C++ concepts and heavy template metaprogramming is accepted.

p.s. All the code snippets have been kept as simple as possible on purpose. Avoid suggesting to use class instead of struct or to add const identifiers or to use unique pointers, it's not the point of this question.

Community
  • 1
  • 1
dario2994
  • 48
  • 6
  • 1
    I would try use std::function as functors. – Mateusz Drost Nov 28 '16 at 00:46
  • 2
    "C++1z" does not have concepts. – Kerrek SB Nov 28 '16 at 01:16
  • If the type `T` is the same for all functions, then you might move it at the class level instead of the function level if you want to use virtual functions... However, it might not be the best solution. As already suggested, I would start by looking if `std::function` could be helpful. – Phil1970 Nov 28 '16 at 01:32
  • This would all be moot if you changed `class Sum` into `template class Sum`. – ildjarn Nov 28 '16 at 01:36
  • I know that templating the Functor on T would make everything easier. But the result is not what I want. – dario2994 Nov 28 '16 at 09:07

1 Answers1

2

Most of the (best) solutions I can think of unfortunately require that you adopt some fairly complex methodologies. Which isn't necessarily a bad thing, of course, but it can make things confusing as you move forward with designing a program. For that reason, I'd probably suggest something a little more straight-forward:

template <typename F, typename G>
class Sum {
  public:          
    Sum(F& f, G& g) :
        f_(f),
        g_(g) {}
    template <typename T>
    inline T Apply(T x) { return f_.Apply(x) + g_.Apply(x); }
  private:
    F& f_;
    G& g_;
};

/*
    For every class like the above, you may want to define an 
    easy-to-use generating function to simplify instantiations: 
*/
template <typename F, typename G>
inline Sum<F, G> MakeSum(F& f, G& g)
{
    return Sum<F, G>(f, g);
}

#include <cmath>

struct SquareRoot {
    template <typename T>
    inline T Apply(T x)
    {
        return std::sqrt(x);
    }
};

struct Triple {
    template <typename T>
    inline T Apply(T x)
    {
        return T(3) * x;
    }
};

//    Example:

#include <iostream>

int main(void)
{
    using namespace std;
    SquareRoot square_root;
    Triple triple; 
    // For g++, don't forget to compile with -std=c++1z     
    auto sum = MakeSum(square_root, triple);
    cout << sum.Apply(1024) << endl;
}

Granted, it isn't as powerful as other techniques, but it may be a good starting point nonetheless.

Sir Galahad
  • 187
  • 10
  • Thank you for the answer. Could you also show one of the "fairly complex methodologies"? – dario2994 Nov 28 '16 at 09:11
  • I can probably post a rough example, but it really wouldn't be practical to put together a full-blown solution (which could easily require several hundred lines of code). Please see my second answer (below) for an elaboration using one such technique. – Sir Galahad Nov 28 '16 at 17:01
  • Sorry, it looks like my second example relied on undefined behavior. I'll try to fix it and post back later... – Sir Galahad Nov 28 '16 at 18:19
  • After having thought about it, I am quite satisfied with your proposed solution. Anyway it's sad that it's not possible to abstract the concept of Functor in C++. Considering that, your solution seems a good compromise: is clean, is usable (only thanks to the auto keyword) and is compiling! – dario2994 Nov 28 '16 at 23:29