2

I've been looking for examples that relate to what my question is about and I still cannot find a solution. The closest thing I've found is

Template function as a template argument

I will try to post a working example in case it is needed but so far part of my code involves the following:

template<class InterfaceType, class T> 
inline void write_info(InterfaceType& interface, T& t) {
    InterfaceType::write_info(interface, t);
}

template<class InterfaceType, class T> 
inline void write_data(InterfaceType& interface, T& t) {
    InterfaceType::write_data(interface, t);
}

template<class InterfaceType, class T> 
inline void write_definition(InterfaceType& interface, T& t) {
    InterfaceType::write_definition(interface, t);
}

Notice that the templates write_info depend on an interface type which has a method called write_info (A static method). The reason this is done is because the write_info function can be specialized later on for an specific datatype without having to redefine anything on the InterfaceType.

The simple question is: Can we reduce the above code with a template that names the function as a function parameter? Keep in mind that I really want this to be possible so that I can avoid defining all those 3 function for a specialized datatype, i.e.

Suppose that foo is a structure with two attributes int a and double b. Then I can specialize the above functions like this:

template<class InterfaceType> 
inline void write_info(InterfaceType& interface, foo& t) {
    InterfaceType::write_info(interface, t.a);
    InterfaceType::write_info(interface, t.b);
}

template<class InterfaceType> 
inline void write_data(InterfaceType& interface, foo& t) {
    InterfaceType::write_data(interface, t.a);
    InterfaceType::write_data(interface, t.b);
}

template<class InterfaceType> 
inline void write_definition(InterfaceType& interface, foo& t) {
    InterfaceType::write_definition(interface, t.a);
    InterfaceType::write_definition(interface, t.b);
}

As you can see I'm writing the same code over and over again. Here I'm assuming that the InterfaceType already has define write_info, write_data and write_definition for int and double. Any ideas?

Community
  • 1
  • 1
jmlopez
  • 4,853
  • 4
  • 40
  • 74

1 Answers1

5

Turn the logic around: rather than writing specialized write_thing overloads for each type, write a single apply function that applies an arbitrary function to an object of each type, then have a single overload of each write_thing that simply delegates to the apply:

// Define a catch-all apply that handles "everything else"
template <typename Interface, typename Function, typename Object>
void apply(Interface& i, Function f, Object& x) {
    f(i, x);
}

// Define overloads for "apply" that handle special cases
template <typename Interface, typename Function>
void apply(Interface& i, Function f, foo& x) {
    f(i, x.a);
    f(i, x.b);
}

// Define polymorphic adapters for your write_[thing] functions:
struct write_info_impl {
    template <typename Interface, typename Object>
    void operator()(Interface& i, Object& x) const {
        Interface::write_info(i, x);
    }
};

// Then implement your write_[thing] functions in terms of the above:
template <typename Interface, typename Object>
void write_info(Interface& interface, Object& x) {
    apply(i, write_info_impl(), x);
}
James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • I like this logic but it seems that the compiler doesn't know which `&Interface::write_info` to use. Obviously we want to use the one that takes care of the type we are using in `apply`. Is there a way to tell it explicitly that we are using `&Interface::write_info`? Otherwise it g++ tells `` in the place of the function. – jmlopez Sep 26 '12 at 18:39
  • @jmlopez: Ah, true. You can use a generic functor to encapsulate the overload set. See the update. It's a bit more code, but if the `write_[thing]` functions are all as similar as they appear, you can trivially generate them using a macro. – James McNellis Sep 26 '12 at 18:44
  • Nice answer. 1 hour without upvotes? The Lounge must be with recess – sehe Sep 26 '12 at 18:46
  • Sorry it took me a while to check this new code. Now it compiles and it works up to a certain point. It works for the basic types: `int` and `double`. But for the special case `foo`, and doesn't use the right apply. Any ideas why the compiler would ignore the overload `apply` for the special case when `Object` is `foo`? – jmlopez Sep 26 '12 at 19:02
  • Never mind!! :) Seems that I don't have a good understanding of `::`. I was writing `::apply` instead of just `apply`. I changed it to the regular and it works. Will this give me some trouble if I put all the code under a namespace? – jmlopez Sep 26 '12 at 19:06
  • 1
    Right, the call must be unqualified (i.e., without any `::`'s) in order for argument-dependent lookup to be used based on the template argument types. Each of the `apply` functions needs to be in either (a) the namespace in which `Interface` is defined or (b) the namespace in which `Object` is defined (or an enclosing namespace of one of those, though generally it's best to place the overloads in the same namespace). – James McNellis Sep 26 '12 at 19:07
  • Thanks James. This really simplifies the code. I hope that the namespaces doesn't case me trouble later on. I will require all the overloads of the objects to be defined in the same namespace. – jmlopez Sep 26 '12 at 19:13