Динамическое создание функции указателя в C ++ - PullRequest
4 голосов
/ 05 апреля 2010

Сегодня я работал над своей домашней работой по расширенному исчислению, и мы делаем несколько итерационных методов в соответствии с методом Ньютона, чтобы найти решения таких вещей, как x ^ 2 = 2.Это заставило меня задуматься о том, что я мог бы написать функцию, которая бы брала два указателя функций: один на саму функцию, а другой на производную и автоматизировал процесс.Это не было бы слишком сложно, тогда я начал думать, мог бы я сделать так, чтобы пользователь ввел функцию и проанализировал этот ввод (да, я могу это сделать).Но могу ли я тогда динамически создать указатель на функцию с одной переменной в c ++.Например, если x ^ 2 + x, могу ли я сделать функцию double функцией (double x) {return x * x + x;} во время выполнения.Это возможно удаленно, или это похоже на самоизменяющийся код?

Редактировать:

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

Ответы [ 6 ]

4 голосов
/ 05 апреля 2010

Как уже говорили другие, вы не можете создавать новые функции C ++ во время выполнения любым переносимым способом. Однако вы можете создать оценщик выражений, который может оценивать такие вещи, как:

 (1 + 2) * 3

содержится в строке во время выполнения. Нетрудно расширить такой оценщик, чтобы иметь переменные и функции.

3 голосов
/ 05 апреля 2010

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

struct Expr
{
  virtual double eval(double x) = 0;
};

struct Sum : Expr
{
  Sum(Expr* a, Expr* b):a(a), b(b) {}
  virtual double eval(double x) {return a->eval(x) + b->eval(x);}
private:
  Expr *a, *b;
};

struct Product : Expr
{
  Product(Expr* a, Expr* b):a(a), b(b) {}
  virtual double eval(double x) {return a->eval(x) * b->eval(x);}
private:
  Expr *a, *b;
};

struct VarX : Expr
{
  virtual double eval(double x) {return x;}
};

struct Constant : Expr
{
  Constant(double c):c(c) {}
  virtual double eval(double x) {return c;}
private:
  double c;
};

Затем вы можете проанализировать ваше выражение в Expr объект во время выполнения. Например, x^2+x будет Expr* e = new Sum(new Product(new VarX(), new VarX()), new VarX()). Затем вы можете оценить это для данного значения x , используя e->eval(x).

Примечание: в приведенном выше коде я проигнорировал const-правильность для ясности - вы не должны:)

3 голосов
/ 05 апреля 2010

Это похоже на самоизменяющийся код, и это возможно - просто не в «чистом» C ++. Вам нужно знать некоторые сборки и несколько деталей реализации. Не идя по этому пути, вы можете абстрактно представлять операции (например, с помощью функторов) и строить дерево выражений для оценки.

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

// store coefficients as vector in "reverse" order, e.g. 1x^2 - 2x + 3
// is stored as [3, -2, 1]
typedef double Num;
typedef vector<double> Coeffs;
Num eval(Coeffs c, Num x) {
  assert(c.size()); // must not be empty
  Num result = 0;
  Num factor = 1;
  for (Coeffs::const_iterator i = c.begin(); i != c.end(); ++i) {
    result += *i * factor;
    factor *= x;
  }
  return result;
}

int main() {
  Coeffs c;       // x^2 + x + 0
  c.push_back(0);
  c.push_back(1);
  c.push_back(1); 
  cout << eval(c, 0) << '\n';
  cout << eval(c, 1) << '\n';
  cout << eval(c, 2) << '\n';
}
1 голос
/ 05 апреля 2010

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

1 голос
/ 05 апреля 2010

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

0 голосов
/ 05 апреля 2010

Как уже упоминали другие. Написание самоизменяющегося кода вовсе не является необходимым и является болезненным на скомпилированном языке, если вы хотите, чтобы он был переносимым. Самая сложная часть вашей работы - анализ входных данных. Я рекомендую muParser для оценки ваших выражений. Это должно снять большую боль, и вы сможете сосредоточиться на важной части вашего проекта.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...