Я переключаюсь с C на C ++, и я хотел бы оптимально использовать дополнительные функции, которые становятся доступными, и избегать вещей, которые считаются «в стиле C», таких как void *
указатели. В частности, я пытаюсь сделать gsl_function
-подобный интерфейс (НЕ обертку для использования gsl в C ++).
В C я написал несколько подпрограмм для поиска корня, интеграции ... которые используют gsl_function
-подобный интерфейс для передачи математических функций этим подпрограммам. Этот интерфейс выглядит следующим образом:
struct Function_struct
{
double (* p_func) (double x, void * p_params);
void * p_params;
};
typedef struct Function_struct Function;
#define FN_EVAL(p_F,x) (*((p_F)->p_func))(x,(p_F)->p_params)
и может использоваться следующим образом:
struct FuncParams_struct { double a; double b; double c; };
double my_sqrt(double x, void * p) {
struct FuncParams_struct * p_params = (struct FuncParams_struct *) p;
double a = p_params->a;
double b = p_params->b;
double c = p_params->c;
return a/sqrt(x+b)+c;
}
Function My_Sqrt;
My_Sqrt.p_func = &my_sqrt;
struct FuncParams_struct my_params = { 1.0, 1.0, 0.0 };
My_Sqrt.p_params = &my_params;
// Call the function at a certain x
double result_at_3 = FN_EVAL(&My_Sqrt, 3);
// Pass My_Sqrt to an integration routine
// (which does not care about the parameters a,b,c
// and which can take any other Function to integrate)
// It uses FN_EVAL to evaluate MySqrt at a certain x.
double integral = integrate(&My_Sqrt);
// Easily change some of the parameters
My_Sqrt.p_params->a=3.0;
Как видите, это позволяет мне создать экземпляр структуры Function
, который содержит указатель на некоторые параметры функции (которые обычно содержатся в другой структуре) и указатель на "нормальную" функцию C. Самое замечательное в этом то, что подпрограммам, использующим Function
, нужно только знать, что это функция, которая принимает значение double и возвращает значение double (они используют макрос FN_EVAL
). Им не нужно заботиться о типе и количестве параметров. С другой стороны, я могу легко изменить значения, хранимые в параметрах, из-за пределов подпрограмм.
Теперь я хотел бы выделить функции, выделенные выше, в функции C ++. Я много искал, что было бы лучшим способом получить это, но я не мог найти его (отчасти потому, что я немного ошеломлен возможностями C ++ по сравнению с C). До сих пор я думал сделать class Function
. Этот class
может хранить фактическое определение функции, и его можно сделать вызываемым, определив operator()
, так что подпрограммам не нужно заботиться о параметрах. Таким образом, вместо макроса FN_EVAL
можно использовать operator()
.
Вещи, которые я пока не мог решить / найти:
- как я буду хранить параметры. Я думал об использовании шаблона typename. Однако, насколько я понимаю, тогда и сам класс должен быть шаблоном, и в этом случае подпрограммы также должны принимать шаблонный класс. Я не хочу использовать
void *
.
- как бы я изменил параметры, которые хранятся в моем
class Function
. Должен ли я сделать их общедоступными, чтобы их можно было легко изменить, или я должен оставить их закрытыми и написать некоторый интерфейс для доступа к ним? В последнем случае, как я должен идти об этом? Поскольку я буду рассматривать большое разнообразие параметров (как по количеству, так и по типу).
Что нужно учитывать:
- Я нашел много вопросов и ответов об оболочках для использования gsl-подпрограмм в C ++. Это не то, что я ищу.
- Я тоже посмотрел на STL
<functional>
. Насколько я мог понять, он не соответствует моим требованиям к параметрам, которые я могу хранить в функции. Однако я могу ошибаться по этому поводу.
- Мои функции могут быть довольно сложными (т. Е. Не просто однострочными, как в примере выше), а параметры могут сами содержать смесь двойных чисел, целых, структур ...
Я хотел бы иметь возможность расширить Function class
на более высокие измерения, то есть f (x, y, z). В С у меня было например
struct FunctionThree_struct
{
double (* p_func) (double x, double y, double z, void * p_params);
void * p_params;
};
typedef struct FunctionThree_struct FunctionThree;
#define FN_THREE_EVAL(p_F,x,y,z) (*((p_F)->p_func))(x,y,z,(p_F)->p_params)
Я забочусь об эффективных вызовах функций. Особенно для интегралов более высокой размерности, функции будут вызываться миллионы раз.