Можно ли объявить указатель на функцию с неизвестным (во время компиляции) типом возврата


У меня есть 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 26

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);
    };