Можно ли создать указатель на функцию с неопределенными параметрами? c++
Есть абстрактный класс в котором реализован шаблон использования метода возвращающего определенное значение, но параметры этого метода разные при различных реализациях класса, можно ли сохранять очередь (хотябы одну переменную) из указателей на функции с неопредленными параметрами в них?
class A {
int (*func)(?);
void F() {
func(?)
}
};
class B : public A {
int v_func(int ptr) { std::cout << ptr << std::endl; }
B {
func = v_func;
}
}
Ответы (2 шт):
Нет, нельзя.
А если бы было можно, то как вы вызовете функцию в методе F. Ей нужны параметры. Какие ей нужны параметры знает только сама функция. Но и функция F тоже должна знать параметры. В конце концов параметры - это контракт между вызывающим кодом и вызываемой функцией.
Так вот, функцию F вы уже написали и откомпилировали. Значить контракт зафиксирован и можно вызывать только функции определённого типа.
Язык Си типизированный язык. Это значит функция должна быть вызвана на один тип.
Есть вариант создания классов, представляющих любые типы аргументов. Создаём абстрактный класс аргументов. Далее любые варианты как родственные классы от абстрактного. Типы аргументов сами функции должны преобразовывать под собственный класс.
Выкладываю два варианта.
// g++ -Wall -Wextra -Wpedantic -std=c++11 absargs.cpp -o absargs
# include <iostream>
class AbsArgs {
public :
virtual ~ AbsArgs(){}
} ;
class A {
public :
int (A::*func)(AbsArgs & );
int F(AbsArgs & arg) {
return (this ->* func ) ( arg ) ;
}
} ;
class BArgs : public AbsArgs {
public :
int ptr ;
BArgs(int x):ptr{x}{}
virtual ~ BArgs(){}
} ;
class CArgs : public AbsArgs {
public :
double number ;
std :: string name ;
CArgs(double d,std::string const & s):number{d},name{s}{}
virtual ~ CArgs(){}
} ;
class B : public A {
public :
int v_func(AbsArgs & arg) {
if(typeid(arg) == typeid(BArgs)) {
std::cout << "B(B) : " << static_cast<BArgs&>(arg).ptr << std::endl;
++ static_cast<BArgs&>(arg).ptr ;
return static_cast<BArgs&>(arg).ptr ; }
if(typeid(arg) == typeid(CArgs)) {
std::cout << "B(C) : "<< static_cast<CArgs&>(arg).number <<
" " << static_cast<CArgs&>(arg).name <<
std::endl;
++ static_cast<CArgs&>(arg).number ;
return 0 ; }
if(typeid(arg) == typeid(AbsArgs)) {
std::cout << "B(Abs) : no arguments" << std::endl;
return 0 ; }
throw ; }
B() {
func = static_cast<int (A::*)(AbsArgs & )>(& B::v_func);
}
} ;
class C : public A {
public :
int v_func(AbsArgs & arg) {
if(typeid(arg) == typeid(BArgs)) {
std::cout << "C(B) : " << static_cast<BArgs&>(arg).ptr << std::endl;
-- static_cast<BArgs&>(arg).ptr ;
return static_cast<BArgs&>(arg).ptr ; }
if(typeid(arg) == typeid(CArgs)) {
std::cout << "C(C) : "<< static_cast<CArgs&>(arg).number <<
" " << static_cast<CArgs&>(arg).name <<
std::endl;
-- static_cast<CArgs&>(arg).number ;
return 0 ; }
if(typeid(arg) == typeid(AbsArgs)) {
std::cout << "C(Abs) : no arguments" << std::endl;
return 0 ; }
throw ; }
C() {
func = static_cast<int (A::*)(AbsArgs & )>(& C::v_func);
}
} ;
int main ( ) {
A * a [ 2 ] ;
a [ 0 ] = new B ;
a [ 1 ] = new C ;
AbsArgs aa ;
BArgs ba { 7 } ;
CArgs ca { 3.1 , "name" } ;
a [ 0 ] -> F ( aa ) ;
a [ 0 ] -> F ( ba ) ;
a [ 0 ] -> F ( ca ) ;
a [ 1 ] -> F ( aa ) ;
a [ 1 ] -> F ( ba ) ;
a [ 1 ] -> F ( ca ) ;
delete a [ 1 ] ;
delete a [ 0 ] ;
}
проверка :
$ ./absargs
B(Abs) : no arguments
B(B) : 7
B(C) : 3.1 name
C(Abs) : no arguments
C(B) : 8
C(C) : 4.1 name
Достоинством служит богатые возможности передачи разных аргументов. Ускорение компиляции из-за скрытия типов аргументов. Сами аргументы имеют область видимости после выполнения метода.
Недостаток : в стеке добавляется запись тип аргументов.