views:

292

answers:

5

I am reluctant to say I can't figure this out, but I can't figure this out. I've googled and searched Stack Overflow, and come up empty.

The abstract, and possibly overly vague form of the question is, how can I use the traits-pattern to instantiate member functions? [Update: I used the wrong term here. It should be "policies" rather than "traits." Traits describe existing classes. Policies prescribe synthetic classes.] The question came up while modernizing a set of multivariate function optimizers that I wrote more than 10 years ago.

The optimizers all operate by selecting a straight-line path through the parameter space away from the current best point (the "update"), then finding a better point on that line (the "line search"), then testing for the "done" condition, and if not done, iterating.

There are different methods for doing the update, the line-search, and conceivably for the done test, and other things. Mix and match. Different update formulae require different state-variable data. For example, the LMQN update requires a vector, and the BFGS update requires a matrix. If evaluating gradients is cheap, the line-search should do so. If not, it should use function evaluations only. Some methods require more accurate line-searches than others. Those are just some examples.

The original version instantiates several of the combinations by means of virtual functions. Some traits are selected by setting mode bits that are tested at runtime. Yuck. It would be trivial to define the traits with #define's and the member functions with #ifdef's and macros. But that's so twenty years ago. It bugs me that I cannot figure out a whiz-bang modern way.

If there were only one trait that varied, I could use the curiously recurring template pattern. But I see no way to extend that to arbitrary combinations of traits.

I tried doing it using boost::enable_if, etc.. The specialized state information was easy. I managed to get the functions done, but only by resorting to non-friend external functions that have the this-pointer as a parameter. I never even figured out how to make the functions friends, much less member functions. The compiler (VC++ 2008) always complained that things didn't match. I would yell, "SFINAE, you moron!" but the moron is probably me.

Perhaps tag-dispatch is the key. I haven't gotten very deeply into that.

Surely it's possible, right? If so, what is best practice?

UPDATE: Here's another try at explaining it. I want the user to be able to fill out an order (manifest) for a custom optimizer, something like ordering off of a Chinese menu - one from column A, one from column B, etc.. Waiter, from column A (updaters), I'll have the BFGS update with Cholesky-decompositon sauce. From column B (line-searchers), I'll have the cubic interpolation line-search with an eta of 0.4 and a rho of 1e-4, please. Etc...

UPDATE: Okay, okay. Here's the playing-around that I've done. I offer it reluctantly, because I suspect it's a completely wrong-headed approach. It runs okay under vc++ 2008.

#include <boost/utility.hpp>
#include <boost/type_traits/integral_constant.hpp>

namespace dj {

struct CBFGS {
    void bar() {printf("CBFGS::bar %d\n", data);}
    CBFGS(): data(1234){}
    int data;
};

template<class T>
struct is_CBFGS: boost::false_type{};

template<>
struct is_CBFGS<CBFGS>: boost::true_type{};

struct LMQN {LMQN(): data(54.321){}
    void bar() {printf("LMQN::bar %lf\n", data);}
    double data;
};

template<class T>
struct is_LMQN: boost::false_type{};

template<>
struct is_LMQN<LMQN> : boost::true_type{};

// "Order form"
struct default_optimizer_traits {
    typedef CBFGS update_type; // Selection from column A - updaters
};

template<class traits> class Optimizer;

template<class traits>
void foo(typename boost::enable_if<is_LMQN<typename traits::update_type>, 
         Optimizer<traits> >::type& self) 
{
    printf(" LMQN %lf\n", self.data);
}

template<class traits>
void foo(typename boost::enable_if<is_CBFGS<typename traits::update_type>,  
         Optimizer<traits> >::type& self) 
{
    printf("CBFGS %d\n", self.data);
}

template<class traits = default_optimizer_traits>
class Optimizer{
    friend typename traits::update_type;
    //friend void dj::foo<traits>(typename Optimizer<traits> & self); // How?
public:
    //void foo(void); // How???
    void foo() {
        dj::foo<traits>(*this);
    }
    void bar() {
        data.bar();
    }
//protected: // How?
    typedef typename traits::update_type update_type;
    update_type data;
};

} // namespace dj



int main() {
    dj::Optimizer<> opt;
    opt.foo();
    opt.bar();
    std::getchar();
    return 0;
}
+1  A: 

I think template specialization is a step in the right direction. This doesn't work with functions so I switched to classes. I changed it so it modifies the data. I'm not so sold on the protected members and making friends. Protected members without inheritance is a smell. Make it public or provide accessors and make it private.

template <typename>
struct foo;

template <>
struct foo<LMQN>
{
    template <typename OptimizerType>
    void func(OptimizerType& that)
    {
        printf(" LMQN %lf\n", that.data.data);
        that.data.data = 3.14;
    }
};

template <>
struct foo<CBFGS>
{
    template <typename OptimizerType>
    void func(OptimizerType& that)
    {
        printf(" CBFGS %lf\n", that.data.data);
    }
};

template<class traits = default_optimizer_traits>
class Optimizer{
public:
    typedef typename traits::update_type update_type;
    void foo() {
        dj::foo<typename traits::update_type>().func(*this);
    }
    void bar() {
        data.bar();
    }
    update_type data;
};
Eddy Pronk
There's no way for the updater foo<CBFGS> to update the protected members of class Optimizer or to read its state. The function to be instantiated should be a member of class Optimizer, or failing that, a friend that gets passed the this-pointer of the Optimizer as an argument. Look at the try I made at it. The functions work, but only if I make everything in class Optimizer public, because I have not figured out how to make declare the functions as friends.
Jive Dadson
(Commenting again because Stackoverflow won't let me edit the previous one.)I think you're on the right track with explicit specialization. But this example misses the point. There's no way for the updater foo<CBFGS> to update the protected members of class Optimizer or to read its state. [... as before].
Jive Dadson
It looks like this approach is a dead-end. In the accepted answer here, http://stackoverflow.com/questions/2097811/c-syntax-for-explicit-specialization-of-a-template-function-in-a-template-classit says, "You can't specialize a member function without explicitly specializing the containing class." So we get into the "multiple dispatch" problem with the Chinese menu.But there is hope. It goes on to say, "What you can do however is forward calls to a member function of a partially specialized type." I'll be looking into that.(#defines, #ifdef's, and macros are looking better and better. :-))
Jive Dadson
I see you modified the code. Now you're to the point where I was stumped. How to declare the function as a friend? But you have it wrapped in a class, and I can declare the class as a friend. So this is close. I really want to make the function a proper member. But see the comment above.I just want to tell the compiler, if you see "CBFGS" in the traits struct, cut and paste this code for the member function "foo" (or better, "update").It's frustrating that something that would be so simple with the pre-processor is mind-boggling with templates. But I can't give up on a puzzle.
Jive Dadson
A: 

Your use of enable_if is somewhat strange. I've seen it used it only 2 ways:

  • in place of the return type
  • as a supplementary parameter (defaulted)

Using it for a real parameter might cause the havoc.

Anyway, it's definitely possible to use it for member functions:

template<class traits = default_optimizer_traits>
class Optimizer{
  typedef typename traits::update_type update_type;
public:

  typename boost::enable_if< is_LQMN<update_type> >::type
  foo()
  {
    // printf is unsafe, prefer the C++ way ;)
    std::cout << "LQMN: " << data << std::endl;
  }

  typename boost::enable_if< is_CBFGS<update_type> >::type
  foo()
  {
    std::cout << "CBFGS: " << data << std::endl;
  }


private:
  update_type data;
};

Note that by default enable_if returns void, which is eminently suitable as a return type in most cases. The "parameter" syntax is normally reserved for the constructor cases, because you don't have a return type at your disposal then, but in general prefer to use the return type so that it does not meddle with overload resolution.

EDIT:

The previous solution does not work, as noted in the comments. I could not find any alternative using enable_if, only the "simple" overload way:

namespace detail
{
  void foo_impl(const LMQN& data)
  {
    std::cout << "LMQN: " << data.data << std::endl;
  }

  void foo_impl(const CBFGS& data)
  {
    std::cout << "CBFGS: " << data.data << std::endl;
  }
} // namespace detail

template<class traits = default_optimizer_traits>
class Optimizer{
  typedef typename traits::update_type update_type;

public:
  void foo() { detail::foo_impl(data); }

private:
  update_type data;
};

It's not enable_if but it does the job without exposing Optimizer internals to everyone. KISS ?

Matthieu M.
Are you quite sure that the code works? From similar code I'm getting real errors, not substitution failures (which are not errors only in function templates).
UncleBens
I think I understand: we are not testing the template parameters proper to the methods, but instead the template parameter is fixed as we instantiate the class which will play havoc with SFINAE I guess. I'll have to try and come up with a better alternative then. That's what happens when you don't compile I guess.
Matthieu M.
Haha, time has passed mate! You still need to fix it xD
Johannes Schaub - litb
The only alternative I found was not to use templates :/ I'll add it anyway.
Matthieu M.
+2  A: 

A simple solution might be to just use tag-based forwarding, e.g. something like this:

template<class traits>
void foo(Optimizer<traits>& self, const LMQN&) {
    printf(" LMQN %lf\n", self.data.data);
}

template<class traits>
void foo(Optimizer<traits>& self, const CBFGS&) {
    printf("CBFGS %d\n", self.data.data);
}

template<class traits = default_optimizer_traits>
class Optimizer {
    friend class traits::update_type;
    friend void dj::foo<traits>(Optimizer<traits>& self, 
                            const typename traits::update_type&);
public:
    void foo() {
        dj::foo<traits>(*this, typename traits::update_type());
    }
    void bar() {
        data.bar();
    }
protected:
    typedef typename traits::update_type update_type;
    update_type data;
};

Or if you want to conveniently group several functions together for different traits, maybe something like this:

template<class traits, class updater=typename traits::update_type> 
struct OptimizerImpl;

template<class traits>
struct OptimizerImpl<traits, LMQN> {
    static void foo(Optimizer<traits>& self) {
        printf(" LMQN %lf\n", self.data.data);
    }
};

template<class traits> 
struct OptimizerImpl<traits, CBFGS> {
    static void foo(Optimizer<traits>& self) {
        printf("CBFGS %d\n", self.data.data);
    }
};

template<class traits = default_optimizer_traits>
class Optimizer{
    friend class traits::update_type;
    friend struct OptimizerImpl<traits>;
public:
    void foo() {
        OptimizerImpl<traits>::foo(*this);
    }
    // ...
};
Georg Fritzsche
That's what I was going to write somehow. I've been focused on the use of `enable_if` when the problem at hand certainly does not require it. A simple `Strategy` pattern or `Policy`-based design seems much more suitable.
Matthieu M.
I am sort of on that same track. The idea came to me as I slept. That happens to me a lot. My idea is, since the member functions cannot be specialized unless the containing class is specifically specialized, I'll have a template that splits the manifest into its component parts then invokes another template that has them separated like what you show. I have dogs and the IRS to mollify today. But I'll get around to it.
Jive Dadson
@Jive: Let us know how it goes. From what you have shown, i don't see the necessity to insert another template layer though - but then you might have something else in mind.
Georg Fritzsche
A: 

It would be trivial to define the traits with #define's and the member functions with #ifdef's and macros. But that's so twenty years ago.

Although it may be worth learning new methods, macros are often the simplest way to do things and shouldn't be discarded as a tool just because they're "old". If you look at the MPL in boost and the book on TMP you'll find much use of the preprocessor.

Noah Roberts
I'm just considering it to be a puzzle at this point. I am definitely old school. I have no allergic reaction to pre-processor macros. Ironically, I campaigned for some of the very template features that I am struggling with. That had no effect though. Bjarne agreed about the features, but said at that point he had turned it over to the committee and was pretty much staying out of it.
Jive Dadson
A: 

Here's what I (the OP) came up with. Can you make it cooler?

The main Optimizer template class inherits policy-implementation classes. It gives those classes access to the Optimizer's protected members that they require. Another Optimizer template class splits the manifest into its constituent parts and instantiates the main Optimizer template.

#include <iostream>
#include <cstdio>

using std::cout;
using std::endl;

namespace dj {

// An updater.
struct CBFGS {
    CBFGS(int &protect_)
        : protect(protect_)
    {}

    void update () {
        cout << "CBFGS " << protect << endl;
    }   

    // Peek at optimizer's protected data
    int &protect;

};

// Another updater
struct LMQN {
    LMQN(int &protect_)
        : protect(protect_)
    {}

    void update () {
        cout << "LMQN " << protect << endl;
    }

    // Peek at optimizer's protected data
    int &protect;

};

// A line-searcher
struct cubic_line_search {
    cubic_line_search (int &protect2_)
        : protect2(protect2_)
    {}

    void line_search() {
        cout << "cubic_line_search  " << protect2 << endl;
    }   

    // Peek at optimizer's protected data
    int &protect2;

};

struct default_search_policies {
    typedef CBFGS update_type;
    typedef cubic_line_search line_search_type;
};

template<class Update, class LineSearch>
class Opt_base: Update, LineSearch
{
public:
    Opt_base()
        : protect(987654321) 
        , protect2(123456789)
        , Update(protect)
        , LineSearch(protect2)
    {}
    void minimize() {
        update();
        line_search();
    }

protected:
    int protect;
    int protect2;
};

template<class Search_Policies=default_search_policies>
class Optimizer: 
    public Opt_base<typename Search_Policies::update_type
                  , typename Search_Policies::line_search_type
                    >
{};

} // namespace dj



int main() {
    dj::Optimizer<> opt; // Use default search policies
    opt.minimize();

    struct my_search_policies {
        typedef dj::LMQN update_type;
        typedef dj::cubic_line_search line_search_type;
    };

    dj::Optimizer<my_search_policies> opt2;
    opt2.minimize();

    std::getchar();
    return 0;
}
Jive Dadson