c++ - valores - ¿Es posible declarar un puntero a una función con un tipo de retorno desconocido(en tiempo de compilación)
puntero a objeto c++ (4)
Tengo una class A
en la que quiero tener un puntero a una función como miembro de datos:
class A
{
protected:
double (*ptrToFunction) ( double );
public:
...
//Setting function according to its name
void SetPtrToFunction( std::string fName );
};
Pero qué ptrToFunction
si quiero que ptrToFunction
sea a veces double
y, a veces, int
, tenga algo como:
//T is a typename
T(*ptrToFunction) ( double );
¿Cómo debo declararlo en este caso?
Parece que está usando dlsym
/ GetProcAddress
para obtener la dirección de las funciones.
En este caso, necesita al menos 2 sitios de llamadas para desambiguar la llamada, ya que la CPU realmente hace cosas diferentes para cada una de estas llamadas.
enum ReturnType { rtInt, rtDouble };
void SetPtrToFunction( std::string fName , enum ReturnType typeOfReturn );
struct Function {
enum ReturnType rt;
union {
std::function< int(double) > mIntFunction;
std::function< double(double) > mDoubleFunction;
} u;
} mFunction;
Por lo tanto, la función debe ser instanciada con un tipo de retorno conocido, y luego se usa con alguna unión etiquetada para obtener la llamada de función correcta.
int A::doCall( double value ) {
if( mFunction.rt == rtInt ) {
int result = mFunction.mIntFunction( value );
} else if( mFunction.rt == rtDouble ) {
double result = mFunction.mDoubleFunction( value );
}
}
Si su clase no tiene una plantilla, como en su ejemplo, podría hacer esto:
template <class T>
struct myStruct
{
static T (*ptrToFunction)(double);
};
Una unión discriminada puede hacer eso por ti:
class A
{
template<T>
using cb_type = T(double);
protected:
enum {IS_INT, IS_DOUBLE} cb_tag;
union {
cb_type<int> *ptrToIntFunction;
cb_type<double> *ptrToDoubleFunction;
};
public:
...
// Setting function according to its name
void SetPtrToFunction( std::string fName );
};
Una solución más general y elegante para una unión discriminada se puede aplicar con std::variant
en C ++ 17, o boost::variant
para revisiones estándar anteriores.
Alternativamente, si desea ignorar completamente el tipo de retorno, puede convertir el miembro en std::function<void(double)>
y beneficiarse de la eliminación de tipos. El concepto Callable
verá la llamada a través del puntero convertido en static_cast<void>(INVOKE(...))
y descartará el valor de retorno, sea el que sea.
Para illustrate :
#include <functional>
#include <iostream>
int foo(double d) { std::cout << d << ''/n''; return 0; }
char bar(double d) { std::cout << 2*d << ''/n''; return ''0''; }
int main() {
std::function<void(double)> cb;
cb = foo; cb(1.0);
cb = bar; cb(2.0);
return 0;
}
Y, por último, si le importa el valor de retorno pero no quiere almacenar una unión discriminada. Luego, al conocer las uniones y el comportamiento de la std::function
, puede combinar los dos enfoques anteriores.
#include <functional>
#include <iostream>
#include <cassert>
int foo(double d) { return d; }
double bar(double d) { return 2*d; }
struct Result {
union {
int i_res;
double d_res;
};
enum { IS_INT, IS_DOUBLE } u_tag;
Result(Result const&) = default;
Result(int i) : i_res{i}, u_tag{IS_INT} {}
Result(double d) : d_res{d}, u_tag{IS_DOUBLE} {}
Result& operator=(Result const&) = default;
auto& operator=(int i)
{ i_res = i; u_tag = IS_INT; return *this; }
auto& operator=(double d)
{ d_res = d; u_tag = IS_DOUBLE; return *this; }
};
int main() {
std::function<Result(double)> cb;
cb = foo;
auto r = cb(1.0);
assert(r.u_tag == Result::IS_INT);
std::cout << r.i_res << ''/n'';
cb = bar;
r = cb(2.0);
assert(r.u_tag == Result::IS_DOUBLE);
std::cout << r.d_res << ''/n'';
return 0;
}
posible declarar un puntero a una función con valor de retorno desconocido (en tiempo de compilación)
class Myclass
{
template<T1>
protected:
double (*pointertoFunction) ( double );
public:
...
void SetPtrToFunction( std::string firstname );
};
struct myStruct // structure or enum which ever ou want
{
static T1 (*pointertoFunction)(double);
};