2010-03-31 3 views
6

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

В настоящее время я реализую процесс умножения и деления двумя разными способами. Теперь есть некоторая дополнительная работа за кулисами, но помимо конкретных утверждений, где происходит умножение/деление, остальная часть кода идентична. Как вы можете себе представить, при таком подходе вы должны очень осторожно вносить какие-либо изменения. Окружающий код не является тривиальным, поэтому его либо случай вручную редактирует каждый метод, либо копирует изменения от одного метода к другому и запоминает изменение операторов * и /.

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

Мой первоначальный подход использовать указатели на функции:

void MultiplyData(double data) 
{ TransformData(data, &(operator *)); } 

void DivideData(double data) 
{ TransformData(data, &(operator /)); } 

void TransformData(double data, double (*func)(double op1, double op2)) 
{ /* Do stuff here... */ } 

Однако, я не могу передать операторы как указатели (это потому, что она является оператором на собственный тип?), Так что я пытался использовать объекты функции. Сначала я подумал, что multiplies и divides функторы в <functional> было бы идеально:

void MultiplyData(double data) 
{ 
    std::multiplies<double> multFunct; 
    TransformData(data, &multFunct); 
} 

void DivideData(double data) 
{ 
    std::divides<double> divFunct; 
    TransformData(data, &divFunct); 
} 

void TransformData(double data, std::binary_function<double, double, double> *funct) 
{ /* Do stuff here... */ } 

Как вы можете видеть, что я пытался использовать указатель на базовый класс передать функтор полиморфно. Проблема заключается в том, что std::binary_function не объявляет члену operator() для реализации дочерних классов.

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

+0

Что такое 'TransformData'? –

+0

'TransformData' - моя собственная функция, которая преобразует' data', используя функцию, обозначенную 'funct'. –

ответ

11

Сделать TransformData шаблонную функцию:

template <typename F> 
typename F::result_type TransformData(double data, F f) { ... } 

Назовите это таким образом:

double MultiplyData(double data) { 
    return TransformData(data, std::multiplies<double>()); 
} 

станд :: binary_function класс пометка. Первичная цель - не предоставлять интерфейс базового класса, а вводить некоторые typedefs в классы класса functor (через наследование), что делает их пригодными для использования другими частями стандартной библиотеки.

+0

Точно, о чем я думал :) – Akanksh

+2

Ваши шаблоны функций и функций нуждаются в возвращаемых типах. –

+0

@Charles: как (я думаю), вы уже знаете, это именно то, что предоставляет 'binary_function' - typedefs для типов параметров и возврата. –

1

Возможно, существует более общее решение, но в этом случае я бы использовал тот факт, что деление такое же, как умножение на обратное, то есть x/C == x * (1/C).

1

Прежде всего: TransformData должна быть функцией шаблона, которая использует статический полиморфизм, а не динамический. binary_function - это только «тег», эти кратность и деление не перегружают его operator() - потому что у него его нет.

template<typename Func> 
void TransformData(double *data,Func f) 
{ 
    for(int i=0;i<some_data_size;i++) 
     data[i]=f(data[i],otherdata[i]); 
} 

И теперь f будет использоваться правильно. кратных, делящихся или любых других.

И тогда вы звоните

TransformData(data,std::multiples<double>()); 
TransformData(data,std::divides<double>()); 
TransformData(data,some_other_functional); 
+0

'binary_function' не имеет' operator() ', по крайней мере, в реализации VC++. –

+0

Вы правильно добавили очистку – Artyom

0

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

template <typename TransformationFunctor> 
TransformData(double data) 
{ 
    TransformationFunctor f; 
    ... 
} 

, а затем использовать как:

MultiplyData(double data) 
{ 
    TransformData< std::multiplies<double> >(data); 
} 
+0

Каковы преимущества такого подхода к Марсело? –

+1

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

+0

О, но есть обратная сторона, вы не можете «пропускать» функциональные объекты с «состоянием», поэтому я думаю, что реализация Марсело лучше. – Akanksh

3

Возможно, вам стоит взглянуть на использование функции Boost.Bind или Boost.Function, чтобы вы могли вещи, как это:

TransformData(boost::function<double(double, double)> func) { 
    // use func like a function that 
    // returns a double and takes two 
    // double parameters 
} 
1

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

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

С transform также использует итераторы, вы можете применить его непосредственно к входу, как вы читаете из файла:

transform(istream_iterator<double>(infile), 
      istream_iterator<double>(), 
      coefficients.begin(), 
      data.begin(), 
      std::multiply); 

// do the other work on data 

transform(data.begin(), 
      data.end(), 
      coefficients.begin(), 
      ostream_iterator<double>(outfile), 
      std::divide); 

Другая возможность рассмотреть бы использовать std::valarray S вместо. Код valarray может содержать код: data *= coefficients; и data /= coefficients;

+0

Я действительно рассматривал использование 'transform', но multplication/division является частью более сложной функции, где приложение входной функции определяется выходом предыдущих преобразований, то есть отдельные преобразования не являются независимыми друг от друга. Извините, я должен был сделать это ясно, вместо того, чтобы просто намекать на «более сложный» характер функции. Тем не менее, это интересный подход, который я бы взял дальше, если бы не требовал реализации работоспособного функтора. –

Смежные вопросы