Class Member Functions Created by Instances [Policy, Actually]

I do not want to say that I cannot understand this, but I cannot understand this. I googled and searched for Stack Overflow, and came up with an empty one.

An abstract and perhaps too vague form of the question: how can I use a feature template to create member elements? [Update: I used the wrong term here. These should be “politicians,” not “traits.” Traits describe existing classes. Politicians prescribe synthetic classes.] The question arose when modernizing the set of optimizers for multidimensional functions that I wrote more than 10 years ago.

All optimizers work by choosing a straight path through the parameter space away from the current best point ("update"), then I find a more accurate point in this line ("line search"), and then testing for the condition "done", and if it is not done , iteration.

There are various methods for performing an update, finding a line, and possibly for a test done and other things. Mix and match. Different update formulas require different state variable data. For example, a vector is required to update LMQN, and a matrix is ​​required to update BFGS. If evaluating gradients is cheap, a line lookup should do it. If not, he should use only evaluation functions. Some methods require more accurate line searches than others. These are just a few examples.

The original version creates several combinations using virtual functions. Some features are selected by setting the mode bits, which are tested at runtime. Ugh. It would be trivial to define C # define features and member functions using #ifdef and macros. But this is so twenty years ago. It seems to me that I can’t understand which one was taken in a modern way.

If only one trait changed, I could use a curiously repeating pattern template. But I do not see the possibility of extending this to arbitrary combinations of signs.

I tried to do this with boost::enable_if etc. Specialized status information was simple. I managed to execute these functions, but only resorting to external external functions that have the this -pointer parameter as a parameter. I did not even understand how to make functions friends, and even more so as member functions. The compiler (VC ++ 2008) always complained that things did not match. I would shout: "SPHINIA, you moron!" but the moron is probably me.

Perhaps the send tag is the key. I did not understand this very deeply.

Of course, this is possible, right? If so, what is the best practice?

UPDATE: Here is another attempt to explain this. I want the user to be able to fill out an order (manifest) for a custom optimizer, something like sorting a Chinese menu - one from column A, one from column B, etc. Waiter from column A (updates), I will have a BFGS update with Cholesky-decpositon sauce. From column B (search lines) I will have a cubic interpolation line search with a sort of 0.4 and rho 1e-4, please. Etc ...

UPDATE: Alright, alright. Here is the game I made. I suggest this reluctantly because I suspect that this is a completely wrong approach. It works fine in 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; } 
+6
c ++ boost templates traits member-functions
source share
5 answers

I think template specialization is a step in the right direction. This does not work with functions, so I switched to classes. I changed it so that it changes the data. I do not sell protected members and made friends. Protected members without inheritance are a smell. Make it public or grant access to it 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; }; 
+1
source share

A simple solution would be to simply use tag-based forwarding, for example. something like that:

 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 combine several functions for different properties, perhaps 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); } // ... }; 
+2
source share

It would be trivial to define C # define features with both C # ifdef member functions and macros. But this is so twenty years ago.

Although it may be useful to learn new methods, macros are often the easiest way to do things and should not be discarded as a tool just because they are “old”. If you look at MPL in boost and a book on TMP, you will find a lot of use of the preprocessor.

+1
source share

Your use of enable_if somewhat strange. I saw that he used it in only two ways:

  • instead of return type
  • as an additional parameter (default)

Using this parameter for a real parameter can lead to chaos.

In any case, you can definitely 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 in most cases is suitable as a return type. The parameter syntax is usually reserved for constructor cases because you do not have a return type at your disposal, but generally prefers to use a return type so that it does not interfere with overload resolution.

EDIT

The previous solution does not work, as noted in the comments. I could not find an alternative using enable_if , only a “simple” way to overload:

 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; }; 

This is not enable_if , but it does the job without exposing the internal Optimizer elements to everyone. KISS?

0
source share

This is what I (OP) came up with. Can you make it cooler?

The main class of the optimizer template inherits the policy implementation classes. This gives these classes access to the protected members of the Optimizer that they require. Another class of Optimizer templates breaks the manifest into its component parts and creates 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 protected data int &protect; }; // Another updater struct LMQN { LMQN(int &protect_) : protect(protect_) {} void update () { cout << "LMQN " << protect << endl; } // Peek at optimizer 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 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; } 
0
source share

All Articles