Моя проблема немного сложна.
У меня есть один класс (e: Component), который имеет объекты Ports.
Когда Компонент создает объект Port, он передает один из своих методов конструктору Port.
Методы подписи:
typedef std::vector<std::string> (Component::*ComponentMethod)(std::vector<std::string>);
Это прекрасно работает, если я делаю:
// in class Component
std::vector<std::string> Component::test_method( std::vector<std::string> ) {
std::cout << "Hello !\n";
// Do stuff
}
// Port ctor : Port( ComponentMethod callback );
Port* p = new Port(&Component::test_method);
Ну … теперь моя проблема в том, что я делаю подклассы класса Component, и я не знаю, как передавать методы подклассов в порт.
// in class SubtypeComponent
std::vector<std::string> SubtypeComponent::test_method2( std::vector<std::string> ) {
std::cout << "Hello !\n";
// Do stuff
}
// Port ctor : Port( ComponentMethod callback );
Port* p = new Port(&SubtypeComponent::test_method2);
// ERROR :'(
Это кажется нормальным: я предполагаю, что компилятор ожидает именно компонентный (только) метод.
—> Я ищу решение для «динамического» назначения методов в портах (но я не думаю, что это возможно)
—> Может быть, другим решением должно стать использование шаблонов? (определяя «указатели методов шаблона» вместо «указатели методов компонента»), но я не уверен.
Любая помощь будет оценена 🙂
Преобразования для указателя на член сначала немного противоречивы. Указатель на элемент, который ссылается на базовый элемент, может быть неявно преобразован в указатель на элемент в производный тип (противоположность), но обратное неверно.
Если вы действительно думаете об этом, это имеет смысл: если у вас есть указатель на член base, который может быть применен к производному типу, поскольку он гарантированно имеет базовый подобъект. Напротив, указатель на член, который ссылается на производный тип, может указывать на член, которого нет в базовом типе, и, следовательно, преобразование не имеет смысла.
Это проблема в вашем дизайне, которую вы должны решить, если вы хотите, чтобы указатели на член ссылались на базовый тип, указатели должен относятся к членам, присутствующим в базе (в этом случае выражение &derived::member
имеет тип T base::*
и нет необходимости преобразовывать), и они не должны были ранее сохраняться в указателе на член производного типа (после его сохранения компилятор не может знать, ссылается ли указатель на элемент base или член производного)
Вы явно не можете преобразовать указатель на член производного типа в указатель на член базы: это позволит вызывать член производного типа в базовом объекте без нарушения системы типов! Во всяком случае, преобразование должно работать наоборот, то есть вы можете преобразовать указатель на член на основе указателя на член производного типа (однако я считать это тоже невозможно, но я не уверен).
Способ полностью обойти эту проблему — использовать для удаления тип функции со стертым типом. Например, вы можете использовать std::function<std::vector<std::string>(std::vector<std::string>)
и установите его для ссылки на ваш объект. Если вам действительно нужно передать разные объекты в качестве аргумента, вы можете передать указатель или ссылку на базу и dynamic_cast<D&>(r)
это к ожидаемому типу (и иметь дело с исключением, если оно когда-либо вызывается на неправильном объекте).
Самый простой способ сделать это, вероятно, будет, если вы позволите компоненту иметь виртуальный метод, называемый, например, test_method
, Затем вы можете передать указатель на Компонент в конструктор Port, а затем Port может просто вызвать этот виртуальный метод:
Component* c = new Component();
// Port ctor : Port( Component* component );
Port* p = new Port(c); // Port would then call c->test_method();
или же
SubtypeComponent* sc = new SubtypeComponent();
// Port ctor : Port( Component* component );
Port* p = new Port(sc); // Port would then call sc->test_method();
Однако, если по какой-то причине это не дает вам достаточной гибкости, есть другой способ решения вашей проблемы: вам нужно передать тип вместо указателя метода. Затем этот тип содержит метод (и объект, потому что вы не можете вызвать метод без объекта). Вот пример этого типа (вы также можете использовать функцию std ::, если у вас есть C ++ 11):
class Handler {
public:
virtual std::vector<std::string> handle (std::vector<std::string> arguments) = 0;
};
template <class C> class Method: public Handler {
C* object;
std::vector<std::string> (C::*method) (std::vector<std::string>);
public:
Method (C *object, std::vector<std::string>(C::*method)(std::vector<std::string>)) {
this->object = object;
this->method = method;
}
virtual std::vector<std::string> handle (std::vector<std::string> arguments) {
// call the method
return (object->*method) (arguments);
}
};
Теперь вы модифицируете свой конструктор класса Port, чтобы в качестве аргумента он указывал на обработчик. Тогда вы можете написать:
Component* c = new Component();
// Port ctor : Port( Handler* handler );
Port* p = new Port(new Method<Component>(c, &Component::test_method));
а также
SubtypeComponent* sc = new SubtypeComponent();
// Port ctor : Port( Handler* handler );
Port* p = new Port(new Method<SubtypeComponent>(sc, &SubtypeComponent::test_method2));
Также взгляните на эти вопросы:
C ++ указатели на функции-члены в классе и подклассе
Приведение указателя метода
Вы можете использовать адаптер шаблона, поскольку указатели на члены могут быть преобразованы следующим образом:
struct Base {
bool foo(int);
};
struct Child : Base {
bool foo(int);
};
// real functional type
using Function = std::function<bool(Base*,int)>;
// adapter template for children classes member functions
template <class T>
using MemberFunction = (T::*)(int);
// real user function
void user(Function function);
// adapter template for children classes member functions
template <class T>
void user(MemberFunction<T> function) {
// pointers-to-members can be casted
user(static_cast<MemberFunction<Base>>(function));
}