Можно ли объявить указатель на функцию с неизвестным (во время компиляции) типом возврата
У меня есть class A
, в котором я хочу иметь указатель на функцию в качестве элемента данных:
class A
{
protected:
double (*ptrToFunction) ( double );
public:
...
//Setting function according to its name
void SetPtrToFunction( std::string fName );
};
Но что, если я хочу ptrToFunction
быть иногда double
, а иногда - int
, иметь что-то вроде:
//T is a typename
T(*ptrToFunction) ( double );
Как я должен заявить об этом в этом случае?
4 ответа:
A дискриминируемый Союз может сделать это за вас:
Более общее и элегантное решение для дискриминируемого объединения может быть применено с помощью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 ); };
std::variant
В C++17 илиboost::variant
для более ранних стандартных редакций.
В качестве альтернативы, если вы хотите полностью игнорировать возвращаемый тип, вы можете преобразовать элемент в
std::function<void(double)>
и извлекайте пользу из стирания типа. КонцепцияCallable
увидит вызов через указатель, преобразованный вstatic_cast<void>(INVOKE(...))
, и отбросит возвращаемое значение, каким бы оно ни было. является.To 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; }
И, наконец, если вы заботитесь о возвращаемом значении, но не хотите хранить дискриминированное объединение. Тогда, зная о союзах и поведенииstd::function
, можно объединить два вышеперечисленных подхода.#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; }
Если у вашего класса нет шаблона, как в вашем примере, вы можете сделать это:
template <class T> struct myStruct { static T (*ptrToFunction)(double); };
Похоже, что вы используете
dlsym
/GetProcAddress
чтобы получить адрес функции.В этом случае вам нужно, по крайней мере, 2 сайта вызовов, чтобы устранить неоднозначность вызова, так как процессор на самом деле делает разные вещи для каждого из этих вызовов.
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;
Таким образом, функция должна быть создана с известным типом возвращаемого значения, а затем это используется с некоторым помеченным объединением, чтобы получить правильный вызов функции.
int A::doCall( double value ) { if( mFunction.rt == rtInt ) { int result = mFunction.mIntFunction( value ); } else if( mFunction.rt == rtDouble ) { double result = mFunction.mDoubleFunction( value ); } }
Можно объявить указатель на функцию с неизвестным (во время компиляции) возвращаемым значением
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); };