c++ - A circular dependency involving comparison functors -


suppose need store information labeled e-mail messages. each message can assigned many labels. also, able retrieve messages assigned given label. here design:

class message; class label { public:     ... private:     std::string name_;     std::set<std::shared_ptr<message>,               std::function<bool(...)>> messages_; // message incomplete! };  class message { public:     ... private:     std::string title_;     std::set<label *,               std::function<bool(...)>> labels_; // fine }; 

each label stores set of messages label assigned. since set needs searchable message title, pass std::function comparison second template parameter of std::set. the problem: function object needs able access message's members. however, message incomplete type @ point.

the situation cannot fixed putting definition of message before definition of label, because have similar problem std::function passed set of labels (the line commented being fine in above code), needs searchable label name.

is there fix or better design this?

first, way map projection ordering:

template<class f> struct order_by_t {   f f;   using is_transparent = std::true_type;   template<class lhs, class rhs>   auto operator()(lhs&& lhs, rhs&& rhs)const   -> decltype (     static_cast<bool>(f(std::declval<lhs>()) < f(std::declval<rhs>())   )   {     return f(std::forward<lhs>(lhs)) < f(std::forward<rhs>(rhs));   } }; template<class f> order_by_t<std::decay_t<f>> order_by(f&& f) {   return {std::forward<f>(f)}; } 

a projection takes type x , "projects" onto type y. trick here type y type of field want order our xs (in case, string, , projection takes x name of x).

this means have define projection (the mapping our type, part of type want order by), , feed order_by_t , generate ordering function us.

order_by_t seems stateful, doesn't have be. if f stateless, can order_by_t be! stateless means don't have initialize f, , can use it, , can lead compiler understanding code better (tracking state hard compilers, stateless things easy optimize).

or, in short, stateless better stateful. here stateless type wraps function call:

template<class sig, sig* f> struct invoke_func_t; template<class r, class...args, r(*f)(args...)> struct invoke_func_t<r(args...), f> {   r operator()(args...args)const {     return f(std::forward<args>(args)...);   } }; 

example use:

void println( std::string const& s ) {   std::cout << s << '\n'; } using printer = invoke_func_t< void(std::string const&), println >; 

and printer type instance of call println when use operator(). store pointer-to-println in type of printer, instead of storing copy of pointer inside of it. makes each instance of printer stateless.

next, stateless order_by wraps function call:

template<class sig, sig* f> struct order_by_f:   order_by_t< invoke_func_t<sig, f> > {}; 

which 1 line, side effect of above being pretty polished.

now use it:

class message; class label;  // impl elsewhere: std::string const& getmessagename( std::shared_ptr<message> const& ); std::string const& getlabelname( std::shared_ptr<label> const& );  class label { private:   std::string name_;   using message_name_order = order_by_f<       std::string const&(std::shared_ptr<message> const&),       getmessagename     >;   std::set<std::shared_ptr<message>, message_name_order > messages_; }; 

where jumped through bunch of hoops make clear std::set ordering calling getmessagename , calling < on returned std::string const&s, 0 overhead.

this can done simpler more directly, each of onion layers wrote above (especially order_by).


the shorter version:

class message; bool order_message_by_name( std::shared_ptr<message> const&, std::shared_ptr<message> const& );  class label { private:   std::string name_;   std::set<std::shared_ptr<message>,           bool(*)(std::shared_ptr<message>const&, std::shared_ptr<message>const&)    > messages_; // message incomplete!   label(std::string name):name_(std::move(name)),     messages_(&order_messages_by_name)   {} }; 

where store function pointer in our set tells class how order it.

this has run time costs (the compiler have difficulty proving function pointer points same function, have store , dereference on each ordering call), forces write order_messages_by_name (an ugly specific-purpose function), , has maintenance costs (you have prove function pointer never changes whenever think set).

plus, doesn't give cool order_by function, you'll love every time want sort std::vector except <.


Comments

Popular posts from this blog

Fail to load namespace Spring Security http://www.springframework.org/security/tags -

sql - MySQL query optimization using coalesce -

unity3d - Unity local avoidance in user created world -