C++ 在std::is_Base_of<;这里&燃气轮机;

C++ 在std::is_Base_of<;这里&燃气轮机;,c++,templates,inheritance,c++14,dynamic-cast,C++,Templates,Inheritance,C++14,Dynamic Cast,我想创建一个库,其中: 用户通过addCallback(callback*callback)添加回调(通常在第一个时间步) 之后,通常在不同的.cpp中,每当用户调用actor():- 如果Bx继承自Base,则调用callback->callback() 否则什么也不做 (信息)我确信每个Bx总是从A继承 以下是初始代码:- #include <iostream> class A{}; class Callback{ public: virtual void call

我想创建一个库,其中:

  • 用户通过
    addCallback(callback*callback)
    添加回调(通常在第一个时间步)
  • 之后,通常在不同的
    .cpp
    中,每当用户调用
    actor()
    :-

    • 如果
      Bx
      继承自
      Base
      ,则调用
      callback->callback()
    • 否则什么也不做
  • (信息)我确信每个
    Bx
    总是从
    A
    继承

  • 以下是初始代码:-

    #include <iostream>
    class A{};
    class Callback{
         public: virtual void callback()=0; 
    };
    template<class Base> void addCallback(Callback* callback){  
        //???
    };
    template<class Bx> void actor(){
        //???
    }
    //^^^^^^ my library end here
    
    class B : public A{};
    class B1 : public B{};
    class C : public A{};
    class CallbackCustom1: public Callback{
        public: virtual void callback(){std::cout<<"A"<<std::endl;}
    };
    class CallbackCustom2: public Callback{
        public: virtual void callback(){std::cout<<"B"<<std::endl;}
    };
    int main(){
        CallbackCustom1 call1;
        CallbackCustom2 call2;
        addCallback<A>(&call1); 
        addCallback<B>(&call2); 
        //vvv  below is usually in another .cpp
        actor<B1>(); // should print "A" and "B"
        actor<C>(); // should print "A" only
    }
    
    这是可行的,但也有一些缺点:-

    • 我必须在
      A
      中添加虚拟析构函数,使其具有多态性。我觉得这是一个令人讨厌的黑客行为
    • 在我的游戏中,在某些时间步中,
      A::~A()
      可能每秒调用100000次以上。
      我可以通过制作
      B1
      C
      final并通过派生类进行批量删除来降低成本,但在某些地方,这样做不合适,也不方便
    • 我必须创建
      Bx
      的实例,仅用于动态\u cast检查。
      如果它的构造函数做了一些特殊的事情,这可能会导致一些复杂性

    还有更好的办法吗

    您是否可以要求用户指定允许的
    Base
    类型集?在这种情况下,任务变得简单():

    用法如下:

    MyCallbacks::addCallback<A>(&call1);
    MyCallbacks::addCallback<B>(&call2);
    // MyCallbacks::addCallback<B1>(&call2);// compile error (good)
    
    
    //vvv  below is usually in another .cpp
    std::cout << R"(should print "A" and "B":)" << std::endl;
    MyCallbacks::actor<B1>();
    
    std::cout << R"(should print "A" only:)" << std::endl;
    MyCallbacks::actor<C>();
    
    MyCallbacks::addCallback(&call1);
    MyCallbacks::addCallback(&call2);
    //MyCallbacks::addCallback(&call2);//编译错误(良好)
    //下面的vvv通常位于另一个.cpp中
    
    std::cout我想到了一个更糟糕的黑客:
    throw
    catch
    可能被滥用,在运行时测试非多态类之间的公共继承关系。如果你敢这么做,你可以使用一些
    std::type_index
    来缓存结果,并确保每遇到一个新类型,这一步只执行一次。@aschepper-like-in?一个类似的想法,是的,但对于你的用例,
    throw
    catch
    需要具有不同的功能。我盯着代码看了一会儿。这很难测试,您能提供一些MVCE(例如)吗?它看起来很有用。谢谢。@CPP初学者:添加了在线演示的链接(见我答案的第一段)。单例样式可能是糟糕的设计,但约束类型的思想也可以应用于非单例:
    CallbackSystem callbacks{};callbacks.add_callback(&call1);/*。。。传递实例(通过引用)callbacks.actor()
    在您答案的第一个版本中(我更喜欢),用户需要额外键入。它有点容易出错,而且不易维护。(它比CRTP更干净、更好。)在这个版本中,用户必须使用正确的回调来调用actor,例如
    CallbackSystem::actor()
    。尽管有
    MyCallbacks
    alias,回调的有用性还是降低了。顺便说一下,为了简化MCVE,我使用了单例。我实际上并不使用这种模式。谢谢。:)@CPP初学者:是的,我同意我的建议(用户必须指定每个类的基)是容易出错的。此外,为了收集用户提供的信息,还需要某种程度的元编程。我还同意,如果使用
    CallbackSystem::actor()
    而不是
    MyCallbacks::actor(),则当前版本是脆弱的。请注意,如果回调是在非单例实例中处理的,则这不是问题:
    voiddo_stuff(MyCallbacks&callbacks){actor(callbacks);}
    static_assert(__cplusplus >= 201703L, "example for C++17, but C++14 possible");
    
    #include <iostream>
    #include <type_traits>
    #include <vector>
    
    struct Callback {
      virtual void callback() = 0;
    };
    
    template<class... RegisteredBases>
    struct CallbackSystem {
    
      template<class Base>
      static auto& callbacks_for() {
        static std::vector<Callback*> callbacks_for_base_{};
    //
    // For each `Base`, the callbacks are stored in a different vector.
    // This way, we can avoid the in-loop branch (see `actor_impl`).
    //
    // TODO: consider performance cost of bad memory locality (can be
    // improved if necessary).
    //
        return callbacks_for_base_;
      }
    
      template<class Base>
      static void addCallback(Callback* callback) {
        static_assert((... || std::is_same<Base, RegisteredBases>{}));
        callbacks_for<Base>().push_back(callback);
      }
    
      template<class Derived, class RegisteredBase>
      static void actor_impl() {// called from `actor` for each RegisteredBase
        if(std::is_base_of<RegisteredBase, Derived>{}) {// branch outside loop
          // if `RegisteredBase` matches then process all its callbacks
          for(Callback* callback : callbacks_for<RegisteredBase>()) {
            callback->callback();
          }
        }
      }
    
      template<class Derived>
      static void actor() {
        (actor_impl<Derived, RegisteredBases>(), ...);
      }
    };
    
    using MyCallbacks = CallbackSystem<A, B> {};
    
    MyCallbacks::addCallback<A>(&call1);
    MyCallbacks::addCallback<B>(&call2);
    // MyCallbacks::addCallback<B1>(&call2);// compile error (good)
    
    
    //vvv  below is usually in another .cpp
    std::cout << R"(should print "A" and "B":)" << std::endl;
    MyCallbacks::actor<B1>();
    
    std::cout << R"(should print "A" only:)" << std::endl;
    MyCallbacks::actor<C>();