Как создать контейнер, который содержит различные типы указателей на функции в C ++? - PullRequest
1 голос
/ 15 января 2011

Я занимаюсь проектом по линейному генетическому программированию, где программы разрабатываются и развиваются с помощью механизмов естественной эволюции. Их «ДНК» - это, по сути, контейнер (я успешно использовал массивы и векторы), который содержит указатели на набор доступных функций. Теперь для простых задач, таких как математические задачи, я мог бы использовать один указатель на функцию определенного типа, которая могла бы указывать на функции, которые все возвращают double и все принимают в качестве параметров два double.

К сожалению, это не очень практично. Мне нужно иметь возможность иметь контейнер, который может иметь различные виды указателей функций, скажем, указатель на функцию, которая не принимает аргументов, или функцию, которая принимает один аргумент, или функцию, которая возвращает что-то и т. Д. идея) ...

Есть ли способ сделать это, используя любой вид контейнера? Могу ли я сделать это, используя контейнер, который содержит полиморфные классы, которые, в свою очередь, имеют различные виды указателей на функции? Я надеюсь, что кто-то может направить меня к решению, потому что перепроектировать все, что я сделал до сих пор, будет болезненно.

Ответы [ 3 ]

2 голосов
/ 15 января 2011

Типичная идея для виртуальных машин - иметь отдельный стек, который используется для передачи аргументов и возвращаемых значений.

Ваши функции могут все еще иметь тип void fn (void), но вы передаете и возвращаете аргументы вручную.

Вы можете сделать что-то вроде этого:

class ArgumentStack {
    public:
        void push(double ret_val) { m_stack.push_back(ret_val); }

        double pop() {
             double arg = m_stack.back();
             m_stack.pop_back();
             return arg;
        }

    private:
        std::vector<double> m_stack;
};
ArgumentStack stack;

... чтобы функция могла выглядеть так:

// Multiplies two doubles on top of the stack.
void multiply() {
    // Read arguments.
    double a1 = stack.pop();
    double a2 = stack.pop();

    // Multiply!
    double result = a1 * a2;

    // Return the result by putting it on the stack.
    stack.push(result);
}

Это можно использовать следующим образом:

// Calculate 4 * 2.
stack.push(4);
stack.push(2);
multiply();
printf("2 * 4 = %f\n", stack.pop());

Следите ли вы?

1 голос
/ 15 января 2011

То, что вы упомянули сами, может быть реализовано с помощью контейнера std::function или дискриминационный союз, такой как Boost::variant.
Например:

#include <functional>
#include <cstdio>
#include <iostream>

struct F {
  virtual ~F() {}
};

template< class Return, class Param = void >
struct Func : F {
  std::function< Return( Param ) >  f;
  Func( std::function< Return( Param ) > const& f ) : f( f ) {}
  Return operator()( Param const& x ) const { return f( x ); }
};

template< class Return >
struct Func< Return, void > : F {
  std::function< Return() >  f;
  Func( std::function< Return() > const& f ) : f( f ) {}
  Return operator()() const { return f(); }
};

static void f_void_void( void ) { puts("void"); }
static int f_int_int( int x ) { return x; }

int main()
{
  F  *f[] = {
    new Func< void >( f_void_void ),
    new Func< int, int >( f_int_int ),
  };

  for ( F **a = f, **e = f + 2;  a != e;  ++ a ) {
    if      ( auto p = dynamic_cast< Func< void >*     >( *a ) ) {
      (*p)();
    }
    else if ( auto p = dynamic_cast< Func< int, int >* >( *a ) ) {
      std::cout<< (*p)( 1 ) <<'\n';
    }
  }
}

Но я не уверен, что это действительно то, что вы хотите ...
Что вы думаете о комментарии Альфа П. Штейнбаха?

1 голос
/ 15 января 2011

Вы не можете поместить полиморфную функцию в класс, поскольку функции, которые принимают (или возвращают) разные вещи, не могут использоваться одинаково ( с тем же интерфейсом ), что требуется для полиморфизма.

Идея иметь класс, предоставляющий виртуальную функцию для любого возможного типа функции, который вам нужен, будет работать, но (не зная ничего о вашей проблеме!) Его использование кажется мне странным : чтофункции будут переопределять производный класс?Не являются ли ваши функции некоррелированными?

Если ваши функции некоррелированы (если нет причин, по которым вам следует группировать их как члены одного класса, или если они будут статическими функциями, поскольку им не нужны переменные-члены) вы должны выбрать что-то другое ... Если вы выбираете свои функции случайным образом, вы можете просто иметь несколько различных контейнеров, один для типа функции, и просто выбирать контейнер случайным образом, а затем функцию внутри него.

Не могли бы вы привести примеры того, что делают ваши функции?

...