Несколько классов C ++ должны использовать один и тот же статический метод с другой реализацией - PullRequest
4 голосов
/ 30 августа 2010

Мне нужно несколько классов C ++, чтобы иметь статический метод «register», однако реализация регистра варьируется между этими классами.

Это должно быть статично, потому что моя идея состоит в том, чтобы "зарегистрировать" все эти классы с помощью Lua (конечно, только один раз).

Очевидно, я не могу объявить интерфейс со статической чисто виртуальной функцией.Что вы, ребята, предлагаете мне сделать?Простота приветствуется, но я думаю, что какой-то шаблон может работать.

Пример того, чего я хотел бы достичь

class registerInterface
{
public:
    static virtual void register() = 0; //obviously illegal
};

class someClass: public registerInterface
{
    static virtual void register()
    {
        //I register myself with Lua
    }
}

class someOtherClass: public registerInterface
{
    static virtual void register()
    {
        //I register myself with Lua in a different way

    }
}

int main()
{
    someClass::register();
    someOtherClass::register();

    return 0;
}

Ответы [ 5 ]

6 голосов
/ 30 августа 2010

Исходя из того, как вы описали проблему, мне непонятно, зачем вам нужен «виртуальный статический метод» в классах.Это должно быть совершенно законно.

class SomeClass {
  static void register(void) {
    ...
  }
}

class SomeOtherClass {
  static void register(void) {
    ...
  }
}

int main(int argc, char* argv[]) {
  SomeClass::register();
  SomeOtherClass::register();

  return 0;
}

Удалите RegisterInterface, я не думаю, что вам это нужно.

2 голосов
/ 30 августа 2010

Если это поможет, вы можете взять ответ Хитеша и добавить:

struct luaRegisterManager {
    template <typename T>
    void registrate() {
        T::registrate();
        // do something else to record the fact that we've registered - 
        // perhaps "registrate" should be returning some object to help with that
    }
};

Затем:

int main() {
    luaRegisterManager lrm;
    lrm.registrate<someClass>();
    lrm.registrate<someOtherClass>();
}

В целом, если вы хотите ввести какой-либо динамический полиморфизм в C ++,тогда вам нужен объект, а не просто класс.Итак, еще раз, возможно, различные функции register должны возвращать объекты с некоторым базовым интерфейсом базового класса registeredClass, или classRegistrationInfo, или что-то в этом роде.

Может привести пример того, что вы чувствуетеэто для чего нужен динамический полиморфизм?Насколько я вижу, код Hitesh точно соответствует вашему единственному примеру, поэтому этот пример не должен охватывать все ваши ожидаемые варианты использования.Если вы напишете код, который будет использовать его, возможно, вам станет ясно, как его реализовать, или, возможно, кто-то посоветует.

Что-то еще, что может помочь:

#include <iostream>
#include <string>
#include <vector>

struct Registered {
    virtual std::string name() = 0;
    virtual ~Registered() {}
    Registered() {
        all.push_back(this);
    }
    static std::vector<Registered*> all;
};

std::vector<Registered*> Registered::all;
typedef std::vector<Registered*>::iterator Iter;

template <typename T>
struct RegisteredT : Registered {
    std::string n;
    RegisteredT(const std::string &name) : n(name) { T::registrate(); }
    std::string name() { return n; }
    // other functions here could be implemented in terms of calls to static
    // functions of T.
};

struct someClass {
    static Registered *r;
    static void registrate() { std::cout << "registering someClass\n"; }
};
Registered *someClass::r = new RegisteredT<someClass>("someClass");

struct someOtherClass {
    static Registered *r;
    static void registrate() { std::cout << "registering someOtherClass\n"; }
};
Registered *someOtherClass::r = new RegisteredT<someOtherClass>("someOtherClass");

int main() {
    for (Iter it = Registered::all.begin(); it < Registered::all.end(); ++it) {
        std::cout << (*it)->name() << "\n";
    }
}

С этим кодом возникают всевозможные проблемы, если вы попытаетесь разделить его на несколько модулей компиляции.Кроме того, такого рода вещи приводят к ложным отчетам от детекторов утечки памяти, если вы не написали какой-то код, чтобы все разрушить в конце, или использовали вектор shared_ptr, вектор указателя усиления и т. Д. Но вы видите общую идею, чтокласс может «регистрировать себя», и вам нужен объект для виртуальных вызовов.

В C ++ вы обычно стараетесь избегать статической инициализации, хотя в пользу какого-либо внедрения / установки зависимостей в началевашей программы.Поэтому обычно вы просто перечисляете все классы, которые вам нужны (вызывая функцию для каждого из них), а не пытаетесь сделать это автоматически.

1 голос
/ 30 августа 2010

Я знаю, что вы уже приняли ответ, но я решил написать это в любом случае. Вы можете иметь саморегистрационные классы, если используете статическую инициализацию и CRTP:

#include <vector>
#include <iostream>

using namespace std;

class RegisterableRoot // Holds the list of functions to call, doesn't actually need
                       // need to be a class, could just be a collection of globals
{
  public:
  typedef void (*registration_func)();
  protected:
  static std::vector<registration_func> s_registery;
  public:
  static void do_registration()
  {
    for(int i = 0; i < s_registery.size(); ++i)
      s_registery[i]();
  }
  static bool add_func(registration_func func) // returns something so we can use it in
                                               // in an initializer
  {
     s_registery.push_back(func);
     return true;
  }
};



template<typename RegisterableType>          // Doesn't really need to inherit from
class Registerable : public RegisterableRoot // RegisterableRoot
{
   protected:
   static const bool s_effect;
};


class A : public Registerable<A> // Honestly, neither does A need to inherit from 
                                 // Registerable<T>
{
   public:
   static void Register()
   {
     cout << "A" << endl;
   }
};

class B : public Registerable<B>
{
   public:
   static void Register()
   {
     cout << "B" << endl;
   }
};

int main()
{

  RegisterableRoot::do_registration();
  return 0;
}


std::vector<RegisterableRoot::registration_func> RegisterableRoot::s_registery;

template <typename RegisterableType> // This is the "cute" part, we initialize the 
                                     // static s_effect so we build the list "magically"
const bool Registerable<RegisterableType>::s_effect = add_func(&RegisterableType::Register);

template class Registerable<A>; // Explicitly instantiate the template
                                // causes the equivalent of
                                // s_registery.push_back(&A::Register) to
                                // be executed
template class Registerable<B>;

Это выводит

 A
 B

хотя я бы не стал полагаться на этот приказ на вашем месте. Обратите внимание, что template class Registerable<X> не обязательно должен быть в той же единице перевода, что и вызов do_registration, вы можете поместить его с остальной частью вашего определения Foo. Если вы унаследовали от Registerable<> и не написали функцию static void Register() для своего класса, вы получите (предположительно, вероятно, загадочную) ошибку компилятора, очень похожую на ожидаемую, если бы действительно существовала такая вещь, как «статические виртуалы» , «Волшебство» просто добавляет функцию, специфичную для класса, в вызываемый список, что позволяет избежать нескольких ловушек при выполнении фактической регистрации в статическом инициализаторе. Вам все еще нужно позвонить do_registration, чтобы что-то произошло.

1 голос
/ 30 августа 2010

Ваши намерения благородны, но ваше решение склоняется к "переобработке" (если я не пропущу очевидное решение).

Вот одна из возможностей: Вы можете использовать функцию Virtual Friend, идиома Например,

class RegisterInterface{
   friend void register(RegisterInterface* x){x->do_real_register();}
 protected:
   virtual void do_real_register();
}

class Foo : public RegisterInterface{
 protected:
  virtual void do_real_register(){}
};

class Bar : public RegisterInterface{
 protected:
  virtual void do_real_register(){}
};

int main(int argc, char* argv[]) {
  BOOST_FOREACH(RegisterInterface* ri, registered_interfaces)
  {
    register(ri);
  }
  return 0;
}
0 голосов
/ 30 августа 2010

Как насчет этого?Определите класс интерфейса:

// IFoobar.h
class IFoobar{
    public:
        virtual void Register(void) = 0;
}

Затем определите класс, который обрабатывает регистр ..

// RegisterFoobar.h
class RegisterFoobar{
    public:
        // Constructors etc...
        IFoobar* fooBar;
        static void RegisterFoobar(IFoobar&  fubar){
             foobar = &fubar;
        }
    private:
        void Raise(void){ foobar->Register(); }
}

Теперь, затем определите другой класс, подобный этому

// MyFuBar.h
class MyFuBar : IFoobar{
    public:
        // Constructors etc...
        void Register(void);
    private:
        RegisterFoobar* _regFoobar;
}

Назовите код следующим образом:

//MyFuBar.cpp
MyFuBar::MyFuBar(){
    _regFoobar = new Foobar();
    _regFoobar->RegisterFoobar(this);
}
void MyFuBar::Register(void){
    // Raised here...
}

Может быть, я неправильно понял ваши требования ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...