2017-07-06 161 views
0

我想实现访问某一类:C++虚方法,不需要“this”指针 - 优化

class A { some properties and methods }; 

的问题是有多种状态A可以在与方法需要相应的行为。其中一种方法是:

class A 
{ 
    void Method1() { 
     if (A is in state 1) { do something } 
     else if (A is in state 2) { do something else } 
     ... 
    } 
}; 

如果多次调用这些方法,那显然不是很理想。因此,一个解决方案,这是简单的实现,将是为不同状态创建几类:

class A 
{ 
    class State1 { 
     virtual void Method1(A& a) { do something; } 
     ... 
    } State1Instance; 

    class State2 { ... } 
    ... 
}; 

然后管理的指针,这取决于当前的状态(例如State1Instance)的对象,并调用该对象的方法。这可以避免CPU消耗情况。

但是状态#方法也收到完全无用的“this”指向状态对象的指针。有没有办法避免这种情况?我知道它们之间的差别很小,但我试图尽可能地优化它,并且使用CPU寄存器来获得完全无意义的值并不理想。这对于“虚拟静态”实际上是一个很好的使用,但是这是被禁止的。

+2

你可以建立自己的thunk表。您可以通过元编程,手动,预处理来完成它...很多选项。这个论坛的范围太大了。你可以开始用谷歌搜索“合作访问者”,它通过模板元编程等实现了一个thunk表。 –

+0

而你确认编译器不会优化它吗? – 2017-07-06 15:53:59

+1

使用开关。会更快。除非你不知道所有的状态。否则,使用虚函数并不打扰这一点,编译器可能会优化它(或可能不),但在宏观方案中,性能惩罚将毫无意义。你更大的担心是内存分配将放缓 - 预先分配一些缓冲区并使用新的放置位置。 –

回答

1

只要使用好的旧函数指针,如果你真的关心重复分支,通常你不应该这样做。

struct A 
{ 
    using StateFn = void (*)(A&); 

    static void State1(A& a) { a.i = 42; } 
    static void State2(A& a) { a.i = 420; } 

    void Method1() { s(*this); } 

    StateFn s = State1; 
    int i; 
}; 

如果您有与每个状态相关的多种方法,方法的一个表可以构建这样

struct A 
{ 
    static void State1M1(A& a) { a.i = 42; } 
    static void State2M1(A& a) { a.i = 420; } 

    static int State1M2(A& a) { return a.i * 42; } 
    static int State2M2(A& a) { return a.i * 420; } 

    // The naming sucks, you should find something better 

    static constexpr struct { 
     void (*Method1)(A&); 
     int (*Method2)(A&); 
    } State[] = {{State1M1, State1M2}, {State2M1, State2M2}}; 

    void Method1() { State[s].Method1(*this); } 
    int Method2() { return State[s].Method2(*this); } 

    int s, i; 
}; 

我很好奇,如果这是连加速超过switch声明,做标杆在你采纳它之前。当你像第二种情况那样开始构建一个方法表时,你实际上没有做过多样化的事情,而是以一种相当不优化的方式进行。

+0

看来OP实际上有多种方法。如果是这样,用一个函数指针表代替它会更好吗? – HolyBlackCat

+1

@HolyBlackCat当然,但是这不是仅仅用一个指针容器来扩展它吗?如同它在代码中所显示的那么远? –

+0

如果您的意思是每个州的单个静态容器,那么是的。将每个对象中的所有指针存储起来听起来都不太好,因为您可以改为使用一个虚拟表指针。 – HolyBlackCat

1

如果你真的想要这样做,使用自由或静态函数,而不是polymorphy,并用::std::function封装它们。你甚至可以在这里使用lambda。

class A { 
    public: 
    ::std::function<void(A*)> state = func1; 
    static void func1(A* that) { 
     ::std::cout << "func1\n"; 
     that->state = func2; 
    } 
    static void func2(A* that) { 
     ::std::cout << "func2\n"; 
     that->state = [](A* that) { ::std::cout << "lambda\n"; that->state = func1; }; 
    } 
    public: 
    void method() { 
     state(this); 
    } 
}; 

然而,在大多数情况下,一个或switchelse if块会更好,因为它可以由编译器,其可以将其翻译成一个跳转表进行优化。如果有疑问,请参考它!

0

在C++ 17开箱即用的最通用解决方案之一,以及之前的升级版本是variant类型和static_visitor的概念。

使用C++ 14和boost::variant我创建了一个非常简单的状态机,它使用基于类型的代码路径切换和自动捕获未考虑状态/事件组合。

如需更全面的解决方案,我会将您推荐给boost fsm标头库。

#include <boost/variant.hpp> 
#include <iostream> 
#include <typeinfo> 

struct event1 { 
}; 

struct event2 { 
}; 

struct state_machine { 

    struct state1 { 
    }; 

    struct state2 { 
    }; 

    struct state3 { 
    }; 

    using state_type = boost::variant<state1, state2, state3>; 


    struct handle_event { 
     // default case for event/state combinations we have not coded for 
     template<class SM, class State, class Event> 
     void operator()(SM &sm, State &state, Event const&event) const { 
      std::cout << "unhandled event " 
         "state=" << typeid(state).name() << " " 
         "event=" << typeid(event).name() << std::endl; 
     } 

     template<class SM> 
     void operator()(SM &sm, state1 &state, event1 const&event) const { 
      std::cout << "received event1 while in state1 - switching to state 2" << std::endl; 
      sm.change_state(state2()); 
     } 

     template<class SM> 
     void operator()(SM &sm, state2 &state, event2 const&event) const { 
      std::cout << "received event2 while in state2 - switching to state 1" << std::endl; 
      sm.change_state(state1()); 
     } 

     template<class SM> 
     void operator()(SM &sm, state1 &state, event2 const&event) const { 
      std::cout << "received event2 while in state1 - switching to state 3" << std::endl; 
      sm.change_state(state3()); 
     } 

    }; 


    template<class Event> 
    auto notify_event(Event const&evt) { 
     return boost::apply_visitor([this, &evt](auto& state) 
            { 
             handle_event()(*this, state, evt); 
            }, state_); 
    } 

    template<class NewState> 
    void change_state(NewState&& ns) { 
     state_ = std::forward<NewState>(ns); 
    } 


private: 

    state_type state_ = state1{}; 
}; 

int main() 
{ 
    state_machine sm {}; 

    sm.notify_event(event1()); 
    sm.notify_event(event2()); 
    sm.notify_event(event2()); 

    // we have not coded for this one 
    sm.notify_event(event2()); 
} 

例如输出(精确的输出将依赖于编译器ABI):

received event1 while in state1 - switching to state 2 
received event2 while in state2 - switching to state 1 
received event2 while in state1 - switching to state 3 
unhandled event state=N13state_machine6state3E event=6event2