2015-09-17 6 views
0

Я хочу сохранить массив функций-членов разных классов. Просто повторение здесь:C++ :: Array of Member Functions

Требования:

  • TYPEOF класса
  • Экземпляр класса, содержащий функцию
  • AddressOf функции члена функции
  • членам параметры

Что я могу хранить:

  • Экземпляр класса, содержащий функцию
  • AddressOf функция член.
  • Функция
  • член параметры

Как правило, вам не нужно хранить тип класса вообще, так как вы можете сделать указатель массива к типу класса. Причина, по которой я не могу этого сделать, состоит в том, что типы классов, которые я принимаю, неизвестны и различны. Класс будет использоваться во многих разных проектах, где типы классов неизвестны.

Мне нужно хранить различные типы классов в массиве/списке, и в этом случае я просто сохранил адреса классов в указателе массива.

Моя проблема: Когда я собираюсь вызвать функцию-член, мне нужно отнести класс-класс к типу класса, но я не знаю, к какому типу нужно приложить.

Пример кода (не проверено - написано в режиме реального быстро):

class A 
{ 
    public: 
     void test1(int i); 
}; 

class B 
{ 
    public: 
     void test2(char c); 
}; 

class Container 
{ 
    long* objects; 
    long* funcs; 
    int index; 

    public: 

     Container() 
     { 
      objects = new long[5]; 
      funcs = new long[5]; 
      index = 0; 
     } 

     template <class C, typename Types ...> 
     void Add(C *obj, void (C::*func)(Types ...)) 
     { 
      objects[index++] = (long)obj; 
      funcs[index++] = (long)func; 
     } 

     typename <Types ...> 
     void Call(int inx, Types ... values) 
     { 
      void (*func)(Types ...) = (void (*)(Types ...))funcs[inx]; 

      // This is where I've got trouble. I don't store the Class 
      // types, so I don't know what pointer Class type to cast 
      // the Class Instance address to. 
      (((*???)objects[inx])->*func)(values ...); 
     } 
}; 

Спасибо заранее. Спросите заранее, есть ли какие-либо дыры или какие-либо вопросы.

+0

Что заставляет вас думать, что 'funcs [index ++] = (long) func;' действительно? Если я не ошибаюсь, вы применяете функцию указателя к члену в 'long'. Это незаконно. –

+0

На ubuntu-15.04, gcc 4.9, указатель для метода класса составляет 10 байтов. Я был очень удивлен. Таким образом, ни void *, ни long * не являются достаточными, чтобы указать на метод. Найдено подтверждение, что оно может быть больше, чем void * в SO. –

ответ

3

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

template<typename... Args> 
class Container { 
public: 
    typedef std::function<void (Args...)> F; 

    template <class C> 
    void Add(C* obj, void (C::*func)(Args ...)) 
    { 
     functions.push_back([=](Args... args) {(obj->*func)(args...);}); 
    } 

    void call(size_t i, Args... args) 
    { 
     functions[i](args...); 
    } 

private: 
    std::vector<F> functions; 
}; 
+0

Вы пропустите 'C ::' для 'C :: * func'. – Jarod42

+0

Это очень чистое и прямое решение моей проблемы, чего я, вероятно, не сделал бы в одиночку. Выражение «Лямбда» было для меня чем-то новым, что очень интересно. Я бы рекомендовал это решение для людей, использующих C++ 11 и выше. Это самое прямое решение по моему вопросу, но я буду изучать ответ DOUGLAS O. MOEN. Большое спасибо, Дженс! PS: C :: * func вместо C * :: func –

1

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

Что я должен решить вашу заявленную проблему:

Когда я собирался вызвать функцию-член, мне нужно бросить адрес класса к типу класса, но я не знаю, что тип должен быть отличен .

принято называть Thunk (назад, когда я впервые столкнулся с ним). В (относительно) недавнем поиске я не нашел эту идею под этим именем (и нашел несколько других вещей, названных thunk).Объяснение имени, которое я когда-то читал, было чем-то вроде того, потому что «оно инкапсулирует то, что я уже умею».

Обратите внимание, что с стуком, не требуется отливка (потому что вы уже thunked его)

Используйте преобразователь в качестве объекта в контейнере.

О, и поскольку вы отметили этот C++, вам действительно нужно использовать std :: vector < PVThunk_t>.

// //////////////////////////////////////////////////////////////////// 
    // Pure Virtual Thunk_t: an abstract base class 
    class PVThunk_t 
    { 
    public: 
    virtual ~PVThunk_t(){} 

    virtual void operator()(void* v) = 0; 
    virtual void exec  (void* v) = 0; 
    }; 
    // pure-virtual requires derived objects to implement both methods 

// /////////////////////////////////////////////////////////////////////// 
// template class - make it easy to create thunk for any class T 
template <class T> 
class Thunk_t : PVThunk_t 
{ 
public: 
    // constructor - takes pointer to an object and pointer to a member and stores 
    // them in two private variables 
    Thunk_t(T* anInstance,  // pointer to an instance of class T 
      void* (T::*aMethod)()) : // pointer to a method of class T, no parameter, returns void 
     m_instance (anInstance), 
     m_method (aMethod) 
     { 
     assert (m_instance); 
     asssert (m_method); 
     } 

    Thunk_t(T* anInstance,  // pointer to an instance of class T 
      T* (T::*aMethod)()) : // pointer to a method of class T, no parameter, returns T* 
     m_instance (anInstance), 
     m_method (aMethod) 
     { 
     assert (m_instance); 
     asssert (m_method); 
     } 

    virtual ~Thunk_t() { } 

    // override operator "()" 
    virtual void* operator()(void* v) { return((*m_instance.*m_method)(v)); } 

    // override function "exec" 
    virtual void* exec(void* v) { return((*m_instance.*m_method)(v)); } 

private: 
    T*  m_instance;   // pointer to object of T 
    void (T::*m_method)(void*); // pointer to method attribute of T with void* param 

}; // template <class T> class Thunk_t : public PVThunk_t 

Обратите внимание на надлежащий механизм для объявления указателей m_instance и m_method.


Если вам необходимо использовать методы с переменным числом параметров, я предлагаю передаю указатель структуры (с соответствующими корректировками или дополнением к стуку. Единственный параметр методы всегда будет указатель на структуры, и его тип или содержимое могут быть затем выведены вызываемым методом.

Я не использовал этот Thunk в течение длительного времени, потому что, ну, полиморфизм превосходит всех, но Thunk все еще компилируется, поэтому он, вероятно, будет работать.

обновление - заметил, что виртуальные методы не совпадают. Fixed

+0

Очень приятный и интересный метод, что-то я не рассматривал. Меня беспокоит, что когда я пытаюсь нажать элемент Thunk_t <> на объект std :: vector , я получаю сообщение об ошибке «не может выделить объект абстрактного типа« PVThunk_t ». Я также был бы признателен за ваше краткое объяснение виртуальных классов и функций, если это возможно, потому что мне, как представляется, не хватает знаний. –

+0

@ Александр Вассилиу - Я совершил ту же ошибку. Ошибка означает, что вы можете только создавать производные классы, которые реализуют все методы, которые облегчают создание шаблона. Правила C++ просто не позволяют экземпляр класса с чистыми виртуальными компонентами (т. Е. Нереализованными). Вы должны создать T и метод, к которому указывает m_method. –

+0

Я перечитал ваш код несколько раз и понял, что именно делает виртуальное ключевое слово и как работает метод Thunk. Мне удалось реализовать ваш метод именно так, как я хотел, чтобы он работал, и я очень доволен результатами. Большое спасибо, DOUGLAS! Вы понятия не имеете, как вы сделали мой день в тот день, когда я закончил свой класс, и все, наконец, работало так, как я изначально желал! Я отметил ответ Йенса как ответ, потому что он отличный и очень простой, и, вероятно, это то, что люди будут искать, но ваш - именно то, что я хотел. –